From dcc8cf2e65d1aa555cce12431a16547e66b469ee Mon Sep 17 00:00:00 2001 From: Steve Block Date: Tue, 27 Apr 2010 16:31:00 +0100 Subject: Merge webkit.org at r58033 : Initial merge by git Change-Id: If006c38561af287c50cd578d251629b51e4d8cd1 --- .../DumpRenderTree/AccessibilityController.cpp | 14 + .../DumpRenderTree/AccessibilityController.h | 1 + .../DumpRenderTree/AccessibilityUIElement.cpp | 33 + .../DumpRenderTree/AccessibilityUIElement.h | 21 +- .../DumpRenderTree.gyp/DumpRenderTree.gyp | 186 +++ .../DumpRenderTree.xcodeproj/project.pbxproj | 8 + .../DumpRenderTree/LayoutTestController.cpp | 166 ++- WebKitTools/DumpRenderTree/LayoutTestController.h | 30 +- .../TestNetscapePlugIn.subproj/PluginObject.cpp | 144 ++- .../TestNetscapePlugIn.subproj/PluginObject.h | 4 + .../TestNetscapePlugIn.subproj/main.cpp | 38 +- .../chromium/AccessibilityController.cpp | 119 ++ .../chromium/AccessibilityController.h | 73 ++ .../chromium/AccessibilityUIElement.cpp | 585 +++++++++ .../chromium/AccessibilityUIElement.h | 142 +++ .../DumpRenderTree/chromium/CppBoundClass.cpp | 350 ++++++ .../DumpRenderTree/chromium/CppBoundClass.h | 241 ++++ WebKitTools/DumpRenderTree/chromium/CppVariant.cpp | 310 +++++ WebKitTools/DumpRenderTree/chromium/CppVariant.h | 133 ++ .../DumpRenderTree/chromium/DumpRenderTree.cpp | 119 ++ .../DumpRenderTree/chromium/EventSender.cpp | 807 ++++++++++++ WebKitTools/DumpRenderTree/chromium/EventSender.h | 145 +++ WebKitTools/DumpRenderTree/chromium/ImageDiff.cpp | 407 ++++++ .../chromium/LayoutTestController.cpp | 1179 ++++++++++++++++++ .../DumpRenderTree/chromium/LayoutTestController.h | 440 +++++++ .../DumpRenderTree/chromium/LayoutTestHelper.mm | 118 ++ .../DumpRenderTree/chromium/MockSpellCheck.cpp | 149 +++ .../DumpRenderTree/chromium/MockSpellCheck.h | 82 ++ .../chromium/PlainTextController.cpp | 79 ++ .../DumpRenderTree/chromium/PlainTextController.h | 52 + .../chromium/TestNavigationController.cpp | 269 ++++ .../chromium/TestNavigationController.h | 204 +++ WebKitTools/DumpRenderTree/chromium/TestShell.cpp | 614 +++++++++ WebKitTools/DumpRenderTree/chromium/TestShell.h | 149 +++ .../DumpRenderTree/chromium/TestShellGtk.cpp | 64 + .../DumpRenderTree/chromium/TestShellMac.mm | 125 ++ .../DumpRenderTree/chromium/TestShellWin.cpp | 100 ++ .../DumpRenderTree/chromium/TestWebWorker.h | 88 ++ .../chromium/TextInputController.cpp | 216 ++++ .../DumpRenderTree/chromium/TextInputController.h | 149 +++ .../DumpRenderTree/chromium/WebViewHost.cpp | 1316 ++++++++++++++++++++ WebKitTools/DumpRenderTree/chromium/WebViewHost.h | 271 ++++ WebKitTools/DumpRenderTree/chromium/config.h | 54 + WebKitTools/DumpRenderTree/fonts/ColorBits-A.png | Bin 0 -> 585 bytes WebKitTools/DumpRenderTree/fonts/ColorBits.ttf | Bin 0 -> 1028 bytes .../gtk/AccessibilityControllerGtk.cpp | 6 + .../gtk/AccessibilityUIElementGtk.cpp | 35 + WebKitTools/DumpRenderTree/gtk/DumpRenderTree.cpp | 128 +- WebKitTools/DumpRenderTree/gtk/EventSender.cpp | 44 +- .../DumpRenderTree/gtk/LayoutTestControllerGtk.cpp | 107 +- .../mac/AccessibilityControllerMac.mm | 6 + .../mac/AccessibilityUIElementMac.mm | 162 ++- .../mac/Configurations/Base.xcconfig | 32 + .../mac/Configurations/DebugRelease.xcconfig | 4 +- WebKitTools/DumpRenderTree/mac/DumpRenderTree.mm | 69 +- .../DumpRenderTree/mac/EventSendingController.mm | 105 +- .../DumpRenderTree/mac/LayoutTestControllerMac.mm | 160 ++- .../DumpRenderTree/mac/ResourceLoadDelegate.mm | 18 +- WebKitTools/DumpRenderTree/qt/DumpRenderTree.pro | 8 +- WebKitTools/DumpRenderTree/qt/DumpRenderTreeQt.cpp | 113 +- WebKitTools/DumpRenderTree/qt/DumpRenderTreeQt.h | 2 + WebKitTools/DumpRenderTree/qt/EventSenderQt.cpp | 36 +- WebKitTools/DumpRenderTree/qt/EventSenderQt.h | 10 +- WebKitTools/DumpRenderTree/qt/GCControllerQt.cpp | 12 +- WebKitTools/DumpRenderTree/qt/GCControllerQt.h | 2 + WebKitTools/DumpRenderTree/qt/ImageDiff.pro | 3 +- .../DumpRenderTree/qt/LayoutTestControllerQt.cpp | 203 ++- .../DumpRenderTree/qt/LayoutTestControllerQt.h | 39 +- .../qt/TestNetscapePlugin/TestNetscapePlugin.pro | 1 + WebKitTools/DumpRenderTree/qt/main.cpp | 7 +- .../unix/TestNetscapePlugin/TestNetscapePlugin.cpp | 25 +- .../win/AccessibilityControllerWin.cpp | 6 + .../win/AccessibilityUIElementWin.cpp | 27 + WebKitTools/DumpRenderTree/win/DumpRenderTree.cpp | 38 +- WebKitTools/DumpRenderTree/win/EventSender.cpp | 25 +- WebKitTools/DumpRenderTree/win/EventSender.h | 2 +- .../DumpRenderTree/win/FrameLoadDelegate.cpp | 5 +- .../DumpRenderTree/win/LayoutTestControllerWin.cpp | 130 +- .../DumpRenderTree/win/ResourceLoadDelegate.cpp | 33 +- .../DumpRenderTree/win/TestNetscapePlugin/main.cpp | 29 +- .../DumpRenderTree/wx/LayoutTestControllerWx.cpp | 49 +- 81 files changed, 11147 insertions(+), 328 deletions(-) create mode 100644 WebKitTools/DumpRenderTree/DumpRenderTree.gyp/DumpRenderTree.gyp create mode 100644 WebKitTools/DumpRenderTree/chromium/AccessibilityController.cpp create mode 100644 WebKitTools/DumpRenderTree/chromium/AccessibilityController.h create mode 100644 WebKitTools/DumpRenderTree/chromium/AccessibilityUIElement.cpp create mode 100644 WebKitTools/DumpRenderTree/chromium/AccessibilityUIElement.h create mode 100644 WebKitTools/DumpRenderTree/chromium/CppBoundClass.cpp create mode 100644 WebKitTools/DumpRenderTree/chromium/CppBoundClass.h create mode 100644 WebKitTools/DumpRenderTree/chromium/CppVariant.cpp create mode 100644 WebKitTools/DumpRenderTree/chromium/CppVariant.h create mode 100644 WebKitTools/DumpRenderTree/chromium/DumpRenderTree.cpp create mode 100644 WebKitTools/DumpRenderTree/chromium/EventSender.cpp create mode 100644 WebKitTools/DumpRenderTree/chromium/EventSender.h create mode 100644 WebKitTools/DumpRenderTree/chromium/ImageDiff.cpp create mode 100644 WebKitTools/DumpRenderTree/chromium/LayoutTestController.cpp create mode 100644 WebKitTools/DumpRenderTree/chromium/LayoutTestController.h create mode 100644 WebKitTools/DumpRenderTree/chromium/LayoutTestHelper.mm create mode 100644 WebKitTools/DumpRenderTree/chromium/MockSpellCheck.cpp create mode 100644 WebKitTools/DumpRenderTree/chromium/MockSpellCheck.h create mode 100644 WebKitTools/DumpRenderTree/chromium/PlainTextController.cpp create mode 100644 WebKitTools/DumpRenderTree/chromium/PlainTextController.h create mode 100644 WebKitTools/DumpRenderTree/chromium/TestNavigationController.cpp create mode 100644 WebKitTools/DumpRenderTree/chromium/TestNavigationController.h create mode 100644 WebKitTools/DumpRenderTree/chromium/TestShell.cpp create mode 100644 WebKitTools/DumpRenderTree/chromium/TestShell.h create mode 100644 WebKitTools/DumpRenderTree/chromium/TestShellGtk.cpp create mode 100644 WebKitTools/DumpRenderTree/chromium/TestShellMac.mm create mode 100644 WebKitTools/DumpRenderTree/chromium/TestShellWin.cpp create mode 100644 WebKitTools/DumpRenderTree/chromium/TestWebWorker.h create mode 100644 WebKitTools/DumpRenderTree/chromium/TextInputController.cpp create mode 100644 WebKitTools/DumpRenderTree/chromium/TextInputController.h create mode 100644 WebKitTools/DumpRenderTree/chromium/WebViewHost.cpp create mode 100644 WebKitTools/DumpRenderTree/chromium/WebViewHost.h create mode 100644 WebKitTools/DumpRenderTree/chromium/config.h create mode 100644 WebKitTools/DumpRenderTree/fonts/ColorBits-A.png create mode 100644 WebKitTools/DumpRenderTree/fonts/ColorBits.ttf (limited to 'WebKitTools/DumpRenderTree') diff --git a/WebKitTools/DumpRenderTree/AccessibilityController.cpp b/WebKitTools/DumpRenderTree/AccessibilityController.cpp index 045bc80..798389f 100644 --- a/WebKitTools/DumpRenderTree/AccessibilityController.cpp +++ b/WebKitTools/DumpRenderTree/AccessibilityController.cpp @@ -77,12 +77,26 @@ static JSValueRef logScrollingStartEventsCallback(JSContextRef ctx, JSObjectRef, return JSValueMakeUndefined(ctx); } +static JSValueRef getElementAtPointCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception) +{ + int x = 0; + int y = 0; + if (argumentCount == 2) { + x = JSValueToNumber(context, arguments[0], exception); + y = JSValueToNumber(context, arguments[1], exception); + } + + AccessibilityController* controller = static_cast(JSObjectGetPrivate(thisObject)); + return AccessibilityUIElement::makeJSAccessibilityUIElement(context, controller->elementAtPoint(x, y)); +} + JSClassRef AccessibilityController::getJSClass() { static JSStaticFunction staticFunctions[] = { { "logFocusEvents", logFocusEventsCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete }, { "logValueChangeEvents", logValueChangeEventsCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete }, { "logScrollingStartEvents", logScrollingStartEventsCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete }, + { "elementAtPoint", getElementAtPointCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete }, { 0, 0, 0 } }; diff --git a/WebKitTools/DumpRenderTree/AccessibilityController.h b/WebKitTools/DumpRenderTree/AccessibilityController.h index de58f84..5a6ca13 100644 --- a/WebKitTools/DumpRenderTree/AccessibilityController.h +++ b/WebKitTools/DumpRenderTree/AccessibilityController.h @@ -45,6 +45,7 @@ public: // Controller Methods - platform-independent implementations AccessibilityUIElement rootElement(); AccessibilityUIElement focusedElement(); + AccessibilityUIElement elementAtPoint(int x, int y); void setLogFocusEvents(bool); void setLogValueChangeEvents(bool); diff --git a/WebKitTools/DumpRenderTree/AccessibilityUIElement.cpp b/WebKitTools/DumpRenderTree/AccessibilityUIElement.cpp index 87fe05c..9cf34de 100644 --- a/WebKitTools/DumpRenderTree/AccessibilityUIElement.cpp +++ b/WebKitTools/DumpRenderTree/AccessibilityUIElement.cpp @@ -344,6 +344,12 @@ static JSValueRef showMenuCallback(JSContextRef context, JSObjectRef function, J return JSValueMakeUndefined(context); } +static JSValueRef pressCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception) +{ + toAXElement(thisObject)->press(); + return JSValueMakeUndefined(context); +} + static JSValueRef takeFocusCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception) { toAXElement(thisObject)->takeFocus(); @@ -435,6 +441,12 @@ static JSValueRef getLanguageCallback(JSContextRef context, JSObjectRef thisObje return JSValueMakeString(context, language.get()); } +static JSValueRef getHelpTextCallback(JSContextRef context, JSObjectRef thisObject, JSStringRef propertyName, JSValueRef* exception) +{ + JSRetainPtr language(Adopt, toAXElement(thisObject)->helpText()); + return JSValueMakeString(context, language.get()); +} + static JSValueRef getOrientationCallback(JSContextRef context, JSObjectRef thisObject, JSStringRef propertyName, JSValueRef* exception) { JSRetainPtr orientation(Adopt, toAXElement(thisObject)->orientation()); @@ -446,6 +458,16 @@ static JSValueRef getChildrenCountCallback(JSContextRef context, JSObjectRef thi return JSValueMakeNumber(context, toAXElement(thisObject)->childrenCount()); } +static JSValueRef rowCountCallback(JSContextRef context, JSObjectRef thisObject, JSStringRef propertyName, JSValueRef* exception) +{ + return JSValueMakeNumber(context, toAXElement(thisObject)->rowCount()); +} + +static JSValueRef columnCountCallback(JSContextRef context, JSObjectRef thisObject, JSStringRef propertyName, JSValueRef* exception) +{ + return JSValueMakeNumber(context, toAXElement(thisObject)->columnCount()); +} + static JSValueRef getXCallback(JSContextRef context, JSObjectRef thisObject, JSStringRef propertyName, JSValueRef* exception) { return JSValueMakeNumber(context, toAXElement(thisObject)->x()); @@ -602,6 +624,12 @@ static JSValueRef addNotificationListenerCallback(JSContextRef context, JSObject return JSValueMakeBoolean(context, succeeded); } +static JSValueRef removeNotificationListenerCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception) +{ + toAXElement(thisObject)->removeNotificationListener(); + return JSValueMakeUndefined(context); +} + // Destruction static void finalize(JSObjectRef thisObject) @@ -626,6 +654,7 @@ JSClassRef AccessibilityUIElement::getJSClass() { "title", getTitleCallback, 0, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete }, { "description", getDescriptionCallback, 0, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete }, { "language", getLanguageCallback, 0, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete }, + { "helpText", getHelpTextCallback, 0, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete }, { "stringValue", getStringValueCallback, 0, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete }, { "x", getXCallback, 0, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete }, { "y", getYCallback, 0, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete }, @@ -637,6 +666,8 @@ JSClassRef AccessibilityUIElement::getJSClass() { "minValue", getMinValueCallback, 0, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete }, { "maxValue", getMaxValueCallback, 0, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete }, { "childrenCount", getChildrenCountCallback, 0, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete }, + { "rowCount", rowCountCallback, 0, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete }, + { "columnCount", columnCountCallback, 0, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete }, { "insertionPointLineNumber", getInsertionPointLineNumberCallback, 0, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete }, { "selectedTextRange", getSelectedTextRangeCallback, 0, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete }, { "isEnabled", getIsEnabledCallback, 0, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete }, @@ -696,12 +727,14 @@ JSClassRef AccessibilityUIElement::getJSClass() { "increment", incrementCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete }, { "decrement", decrementCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete }, { "showMenu", showMenuCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete }, + { "press", pressCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete }, { "disclosedRowAtIndex", disclosedRowAtIndexCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete }, { "ariaOwnsElementAtIndex", ariaOwnsElementAtIndexCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete }, { "ariaFlowToElementAtIndex", ariaFlowToElementAtIndexCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete }, { "selectedRowAtIndex", selectedRowAtIndexCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete }, { "isEqual", isEqualCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete }, { "addNotificationListener", addNotificationListenerCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete }, + { "removeNotificationListener", removeNotificationListenerCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete }, { "takeFocus", takeFocusCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete }, { "takeSelection", takeSelectionCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete }, { "addSelection", addSelectionCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete }, diff --git a/WebKitTools/DumpRenderTree/AccessibilityUIElement.h b/WebKitTools/DumpRenderTree/AccessibilityUIElement.h index e7d3bc7..f62ec1a 100644 --- a/WebKitTools/DumpRenderTree/AccessibilityUIElement.h +++ b/WebKitTools/DumpRenderTree/AccessibilityUIElement.h @@ -51,6 +51,14 @@ typedef AtkObject* PlatformUIElement; typedef void* PlatformUIElement; #endif +#if PLATFORM(MAC) +#ifdef __OBJC__ +typedef id NotificationHandler; +#else +typedef struct objc_object* NotificationHandler; +#endif +#endif + class AccessibilityUIElement { public: AccessibilityUIElement(PlatformUIElement); @@ -89,6 +97,7 @@ public: void increment(); void decrement(); void showMenu(); + void press(); // Attributes - platform-independent implementations JSStringRef stringAttributeValue(JSStringRef attribute); @@ -104,6 +113,7 @@ public: JSStringRef language(); JSStringRef stringValue(); JSStringRef accessibilityValue() const; + JSStringRef helpText() const; JSStringRef orientation() const; double x(); double y(); @@ -143,6 +153,8 @@ public: int indexInTable(); JSStringRef rowIndexRange(); JSStringRef columnIndexRange(); + int rowCount(); + int columnCount(); // Tree/Outline specific attributes AccessibilityUIElement selectedRowAtIndex(unsigned); @@ -170,12 +182,17 @@ public: // Notifications // Function callback should take one argument, the name of the notification. bool addNotificationListener(JSObjectRef functionCallback); + // Make sure you call remove, because you can't rely on objects being deallocated in a timely fashion. + void removeNotificationListener(); private: static JSClassRef getJSClass(); - PlatformUIElement m_element; - JSObjectRef m_notificationFunctionCallback; + + // A retained, platform specific object used to help manage notifications for this object. +#if PLATFORM(MAC) + NotificationHandler m_notificationHandler; +#endif }; #endif // AccessibilityUIElement_h diff --git a/WebKitTools/DumpRenderTree/DumpRenderTree.gyp/DumpRenderTree.gyp b/WebKitTools/DumpRenderTree/DumpRenderTree.gyp/DumpRenderTree.gyp new file mode 100644 index 0000000..82b8671 --- /dev/null +++ b/WebKitTools/DumpRenderTree/DumpRenderTree.gyp/DumpRenderTree.gyp @@ -0,0 +1,186 @@ +# +# Copyright (C) 2010 Google Inc. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above +# copyright notice, this list of conditions and the following disclaimer +# in the documentation and/or other materials provided with the +# distribution. +# * Neither the name of Google Inc. nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# + +{ + 'includes': [ + '../../../WebKit/chromium/features.gypi', + ], + 'variables': { + 'webkit_top': '../../..', + 'webkit_api_dir': '<(webkit_top)/WebKit/chromium', + 'conditions': [ + # Location of the chromium src directory and target type is different + # if webkit is built inside chromium or as standalone project. + ['inside_chromium_build==0', { + # DumpRenderTree is being built outside of the full chromium project. + # e.g. via build-dumprendertree --chromium + 'chromium_src_dir': '<(webkit_api_dir)', + 'webkit_support_gyp': '<(webkit_api_dir)/webkit/support/webkit_support.gyp', + },{ + # WebKit is checked out in src/chromium/third_party/WebKit + 'chromium_src_dir': '<(webkit_top)/../..', + 'webkit_support_gyp': '<(webkit_top)/../../webkit/webkit.gyp', + }], + ], + }, + 'target_defaults': { + 'target_conditions': [ + ['OS!="linux" and OS!="freebsd" and OS!="openbsd"', { + 'sources/': [ + ['exclude', '(Gtk|Linux)\\.cpp$'] + ] + }], + ['OS!="win"', { + 'sources/': [ + ['exclude', 'Win\\.cpp$'], + ] + }], + ['OS!="mac"', { + 'sources/': [ + # .mm is already excluded by common.gypi + ['exclude', 'Mac\\.cpp$'], + ] + }], + ], + }, + 'targets': [ + { + 'target_name': 'DumpRenderTree', + 'type': 'executable', + 'mac_bundle': 1, + 'dependencies': [ + '<(webkit_api_dir)/WebKit.gyp:webkit', + '<(webkit_top)/JavaScriptCore/JavaScriptCore.gyp/JavaScriptCore.gyp:wtf_config', + '<(chromium_src_dir)/third_party/icu/icu.gyp:icuuc', + '<(chromium_src_dir)/third_party/npapi/npapi.gyp:npapi', + '<(chromium_src_dir)/skia/skia.gyp:skia', + '<(webkit_support_gyp):webkit_support', + ], + 'include_dirs': [ + '.', + '<(webkit_api_dir)', + '<(webkit_top)/JavaScriptCore', + '<(webkit_top)/WebKit/mac/WebCoreSupport', # For WebSystemInterface.h + '<(chromium_src_dir)', + ], + 'defines': [ + # Technically not a unit test but require functions available only to + # unit tests. + 'UNIT_TEST', + ], + 'sources': [ + '../chromium/AccessibilityController.cpp', + '../chromium/AccessibilityController.h', + '../chromium/AccessibilityUIElement.cpp', + '../chromium/AccessibilityUIElement.h', + '../chromium/CppBoundClass.cpp', + '../chromium/CppBoundClass.h', + '../chromium/CppVariant.cpp', + '../chromium/CppVariant.h', + '../chromium/DumpRenderTree.cpp', + '../chromium/EventSender.cpp', + '../chromium/EventSender.h', + '../chromium/LayoutTestController.cpp', + '../chromium/LayoutTestController.h', + '../chromium/MockSpellCheck.cpp', + '../chromium/MockSpellCheck.h', + '../chromium/PlainTextController.cpp', + '../chromium/PlainTextController.h', + '../chromium/TestNavigationController.cpp', + '../chromium/TestNavigationController.h', + '../chromium/TestShell.cpp', + '../chromium/TestShell.h', + '../chromium/TestShellGtk.cpp', + '../chromium/TestShellMac.mm', + '../chromium/TestShellWin.cpp', + '../chromium/TextInputController.cpp', + '../chromium/TextInputController.h', + '../chromium/WebViewHost.cpp', + '../chromium/WebViewHost.h', + ], + 'mac_bundle_resources': [ + '../qt/fonts/AHEM____.TTF', + '../fonts/WebKitWeightWatcher100.ttf', + '../fonts/WebKitWeightWatcher200.ttf', + '../fonts/WebKitWeightWatcher300.ttf', + '../fonts/WebKitWeightWatcher400.ttf', + '../fonts/WebKitWeightWatcher500.ttf', + '../fonts/WebKitWeightWatcher600.ttf', + '../fonts/WebKitWeightWatcher700.ttf', + '../fonts/WebKitWeightWatcher800.ttf', + '../fonts/WebKitWeightWatcher900.ttf', + ], + 'conditions': [ + ['OS=="mac"', { + 'dependencies': ['LayoutTestHelper'], + }], + ], + }, + + { + 'target_name': 'ImageDiff', + 'type': 'executable', + 'dependencies': [ + '<(webkit_top)/JavaScriptCore/JavaScriptCore.gyp/JavaScriptCore.gyp:wtf', + '<(chromium_src_dir)/gfx/gfx.gyp:gfx', + ], + 'include_dirs': [ + '<(webkit_top)/JavaScriptCore', + '<(chromium_src_dir)', + ], + 'sources': [ + '../chromium/ImageDiff.cpp', + ], + }, + ], # targets + + 'conditions': [ + ['OS=="mac"', { + 'targets': [ + { + 'target_name': 'LayoutTestHelper', + 'type': 'executable', + 'sources': ['../chromium/LayoutTestHelper.mm'], + 'link_settings': { + 'libraries': [ + '$(SDKROOT)/System/Library/Frameworks/AppKit.framework', + ], + }, + }, + ], + }], + ], # conditions +} + +# Local Variables: +# tab-width:2 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=2 shiftwidth=2: diff --git a/WebKitTools/DumpRenderTree/DumpRenderTree.xcodeproj/project.pbxproj b/WebKitTools/DumpRenderTree/DumpRenderTree.xcodeproj/project.pbxproj index 06f0599..3adfaf2 100644 --- a/WebKitTools/DumpRenderTree/DumpRenderTree.xcodeproj/project.pbxproj +++ b/WebKitTools/DumpRenderTree/DumpRenderTree.xcodeproj/project.pbxproj @@ -35,6 +35,8 @@ 1AC6C84A0D07638600CD3161 /* PluginObject.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1AC6C7800D07589B00CD3161 /* PluginObject.cpp */; }; 1AC6C84B0D07638600CD3161 /* TestObject.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1AC6C7810D07589B00CD3161 /* TestObject.cpp */; }; 23BCB8900EA57623003C6289 /* OpenGL.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 23BCB88F0EA57623003C6289 /* OpenGL.framework */; }; + 3713EDE2115BE19300705720 /* ColorBits-A.png in Copy Font Files */ = {isa = PBXBuildFile; fileRef = 3713EDDF115BE16F00705720 /* ColorBits-A.png */; }; + 3713EDE3115BE19300705720 /* ColorBits.ttf in Copy Font Files */ = {isa = PBXBuildFile; fileRef = 3713EDE0115BE16F00705720 /* ColorBits.ttf */; }; 5185F6B210714E07007AA393 /* HistoryDelegate.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5185F69F10714A57007AA393 /* HistoryDelegate.mm */; }; 5185F6B310714E12007AA393 /* HistoryDelegate.h in Headers */ = {isa = PBXBuildFile; fileRef = 5185F69E10714A57007AA393 /* HistoryDelegate.h */; }; 5DB9AC970F722C3600684641 /* AHEM____.TTF in Copy Font Files */ = {isa = PBXBuildFile; fileRef = AA7F10C20CB3C1030003BDC9 /* AHEM____.TTF */; }; @@ -156,6 +158,8 @@ dstSubfolderSpec = 7; files = ( 5DB9AC970F722C3600684641 /* AHEM____.TTF in Copy Font Files */, + 3713EDE2115BE19300705720 /* ColorBits-A.png in Copy Font Files */, + 3713EDE3115BE19300705720 /* ColorBits.ttf in Copy Font Files */, 5DB9AC980F722C3600684641 /* WebKitWeightWatcher100.ttf in Copy Font Files */, 5DB9AC990F722C3600684641 /* WebKitWeightWatcher200.ttf in Copy Font Files */, 5DB9AC9A0F722C3600684641 /* WebKitWeightWatcher300.ttf in Copy Font Files */, @@ -182,6 +186,8 @@ 1AC6C7810D07589B00CD3161 /* TestObject.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = TestObject.cpp; sourceTree = ""; }; 23BCB88F0EA57623003C6289 /* OpenGL.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = OpenGL.framework; path = /System/Library/Frameworks/OpenGL.framework; sourceTree = ""; }; 32A70AAB03705E1F00C91783 /* DumpRenderTreePrefix.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DumpRenderTreePrefix.h; sourceTree = ""; }; + 3713EDDF115BE16F00705720 /* ColorBits-A.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = "ColorBits-A.png"; path = "fonts/ColorBits-A.png"; sourceTree = ""; }; + 3713EDE0115BE16F00705720 /* ColorBits.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; name = ColorBits.ttf; path = fonts/ColorBits.ttf; sourceTree = ""; }; 375F09710DAC3CB600C8B4E5 /* WebKitWeightWatcher100.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; name = WebKitWeightWatcher100.ttf; path = fonts/WebKitWeightWatcher100.ttf; sourceTree = ""; }; 375F09720DAC3CB600C8B4E5 /* WebKitWeightWatcher200.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; name = WebKitWeightWatcher200.ttf; path = fonts/WebKitWeightWatcher200.ttf; sourceTree = ""; }; 375F09730DAC3CB600C8B4E5 /* WebKitWeightWatcher300.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; name = WebKitWeightWatcher300.ttf; path = fonts/WebKitWeightWatcher300.ttf; sourceTree = ""; }; @@ -417,6 +423,8 @@ 9345229B0BD12B2C0086EDA0 /* Resources */ = { isa = PBXGroup; children = ( + 3713EDDF115BE16F00705720 /* ColorBits-A.png */, + 3713EDE0115BE16F00705720 /* ColorBits.ttf */, AA7F10C20CB3C1030003BDC9 /* AHEM____.TTF */, 375F09710DAC3CB600C8B4E5 /* WebKitWeightWatcher100.ttf */, 375F09720DAC3CB600C8B4E5 /* WebKitWeightWatcher200.ttf */, diff --git a/WebKitTools/DumpRenderTree/LayoutTestController.cpp b/WebKitTools/DumpRenderTree/LayoutTestController.cpp index f528b31..a736160 100644 --- a/WebKitTools/DumpRenderTree/LayoutTestController.cpp +++ b/WebKitTools/DumpRenderTree/LayoutTestController.cpp @@ -74,6 +74,7 @@ LayoutTestController::LayoutTestController(const std::string& testPathOrURL, con , m_globalFlag(false) , m_isGeolocationPermissionSet(false) , m_geolocationPermission(false) + , m_handlesAuthenticationChallenges(false) , m_testPathOrURL(testPathOrURL) , m_expectedPixelHash(expectedPixelHash) { @@ -280,6 +281,14 @@ static JSValueRef addDisallowedURLCallback(JSContextRef context, JSObjectRef fun return JSValueMakeUndefined(context); } +static JSValueRef callShouldCloseOnWebViewCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception) +{ + // Has mac & windows implementation + LayoutTestController* controller = static_cast(JSObjectGetPrivate(thisObject)); + + return JSValueMakeBoolean(context, controller->callShouldCloseOnWebView()); +} + static JSValueRef clearAllDatabasesCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception) { // Has mac & windows implementation @@ -456,6 +465,23 @@ static JSValueRef keepWebHistoryCallback(JSContextRef context, JSObjectRef funct return JSValueMakeUndefined(context); } +static JSValueRef computedStyleIncludingVisitedInfoCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception) +{ + if (argumentCount != 1) + return JSValueMakeUndefined(context); + + // Has mac implementation + LayoutTestController* controller = static_cast(JSObjectGetPrivate(thisObject)); + return controller->computedStyleIncludingVisitedInfo(context, arguments[0]); +} + +static JSValueRef layerTreeAsTextCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception) +{ + // Has mac & windows implementation + LayoutTestController* controller = static_cast(JSObjectGetPrivate(thisObject)); + return JSValueMakeString(context, controller->layerTreeAsText().get()); +} + static JSValueRef notifyDoneCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception) { // Has mac & windows implementation @@ -914,14 +940,26 @@ static JSValueRef setXSSAuditorEnabledCallback(JSContextRef context, JSObjectRef return JSValueMakeUndefined(context); } -static JSValueRef setFrameSetFlatteningEnabledCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception) +static JSValueRef setSpatialNavigationEnabledCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception) +{ + if (argumentCount < 1) + return JSValueMakeUndefined(context); + + LayoutTestController* controller = static_cast(JSObjectGetPrivate(thisObject)); + controller->setSpatialNavigationEnabled(JSValueToBoolean(context, arguments[0])); + + return JSValueMakeUndefined(context); +} + + +static JSValueRef setFrameFlatteningEnabledCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception) { // Has mac & windows implementation if (argumentCount < 1) return JSValueMakeUndefined(context); LayoutTestController* controller = static_cast(JSObjectGetPrivate(thisObject)); - controller->setFrameSetFlatteningEnabled(JSValueToBoolean(context, arguments[0])); + controller->setFrameFlatteningEnabled(JSValueToBoolean(context, arguments[0])); return JSValueMakeUndefined(context); } @@ -1011,6 +1049,25 @@ static JSValueRef setUserStyleSheetLocationCallback(JSContextRef context, JSObje return JSValueMakeUndefined(context); } +static JSValueRef setWillSendRequestClearHeaderCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception) +{ + // Has mac & windows implementation + if (argumentCount < 1) + return JSValueMakeUndefined(context); + + JSRetainPtr header(Adopt, JSValueToStringCopy(context, arguments[0], exception)); + ASSERT(!*exception); + + size_t maxLength = JSStringGetMaximumUTF8CStringSize(header.get()); + char* headerBuffer = new char[maxLength + 1]; + JSStringGetUTF8CString(header.get(), headerBuffer, maxLength + 1); + + LayoutTestController* controller = static_cast(JSObjectGetPrivate(thisObject)); + controller->setWillSendRequestClearHeader(headerBuffer); + + return JSValueMakeUndefined(context); +} + static JSValueRef setWillSendRequestReturnsNullCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception) { // Has cross-platform implementation @@ -1217,7 +1274,25 @@ static JSValueRef waitForPolicyDelegateCallback(JSContextRef context, JSObjectRe return JSValueMakeUndefined(context); } -static JSValueRef whiteListAccessFromOriginCallback(JSContextRef context, JSObjectRef, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception) +static JSValueRef addOriginAccessWhitelistEntryCallback(JSContextRef context, JSObjectRef, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception) +{ + if (argumentCount != 4) + return JSValueMakeUndefined(context); + + JSRetainPtr sourceOrigin(Adopt, JSValueToStringCopy(context, arguments[0], exception)); + ASSERT(!*exception); + JSRetainPtr destinationProtocol(Adopt, JSValueToStringCopy(context, arguments[1], exception)); + ASSERT(!*exception); + JSRetainPtr destinationHost(Adopt, JSValueToStringCopy(context, arguments[2], exception)); + ASSERT(!*exception); + bool allowDestinationSubdomains = JSValueToBoolean(context, arguments[3]); + + LayoutTestController* controller = static_cast(JSObjectGetPrivate(thisObject)); + controller->addOriginAccessWhitelistEntry(sourceOrigin.get(), destinationProtocol.get(), destinationHost.get(), allowDestinationSubdomains); + return JSValueMakeUndefined(context); +} + +static JSValueRef removeOriginAccessWhitelistEntryCallback(JSContextRef context, JSObjectRef, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception) { if (argumentCount != 4) return JSValueMakeUndefined(context); @@ -1231,7 +1306,22 @@ static JSValueRef whiteListAccessFromOriginCallback(JSContextRef context, JSObje bool allowDestinationSubdomains = JSValueToBoolean(context, arguments[3]); LayoutTestController* controller = static_cast(JSObjectGetPrivate(thisObject)); - controller->whiteListAccessFromOrigin(sourceOrigin.get(), destinationProtocol.get(), destinationHost.get(), allowDestinationSubdomains); + controller->removeOriginAccessWhitelistEntry(sourceOrigin.get(), destinationProtocol.get(), destinationHost.get(), allowDestinationSubdomains); + return JSValueMakeUndefined(context); +} + +static JSValueRef setScrollbarPolicyCallback(JSContextRef context, JSObjectRef, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception) +{ + if (argumentCount != 2) + return JSValueMakeUndefined(context); + + JSRetainPtr orientation(Adopt, JSValueToStringCopy(context, arguments[0], exception)); + ASSERT(!*exception); + JSRetainPtr policy(Adopt, JSValueToStringCopy(context, arguments[1], exception)); + ASSERT(!*exception); + + LayoutTestController* controller = static_cast(JSObjectGetPrivate(thisObject)); + controller->setScrollbarPolicy(orientation.get(), policy.get()); return JSValueMakeUndefined(context); } @@ -1278,6 +1368,51 @@ static JSValueRef apiTestNewWindowDataLoadBaseURLCallback(JSContextRef context, return JSValueMakeUndefined(context); } +static JSValueRef apiTestGoToCurrentBackForwardItemCallback(JSContextRef context, JSObjectRef, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception) +{ + LayoutTestController* controller = static_cast(JSObjectGetPrivate(thisObject)); + controller->apiTestGoToCurrentBackForwardItem(); + return JSValueMakeUndefined(context); +} + +static JSValueRef setWebViewEditableCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception) +{ + // Has Mac implementation + if (argumentCount < 1) + return JSValueMakeUndefined(context); + + LayoutTestController* controller = static_cast(JSObjectGetPrivate(thisObject)); + controller->setWebViewEditable(JSValueToBoolean(context, arguments[0])); + + return JSValueMakeUndefined(context); +} + +static JSValueRef markerTextForListItemCallback(JSContextRef context, JSObjectRef, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception) +{ + LayoutTestController* controller = static_cast(JSObjectGetPrivate(thisObject)); + if (argumentCount < 1) + return JSValueMakeUndefined(context); + return JSValueMakeString(context, controller->markerTextForListItem(context, arguments[0]).get()); +} + +static JSValueRef authenticateSessionCallback(JSContextRef context, JSObjectRef, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception) +{ + // authenticateSession(url, username, password) + if (argumentCount != 3) + return JSValueMakeUndefined(context); + + JSRetainPtr url(Adopt, JSValueToStringCopy(context, arguments[0], exception)); + ASSERT(!*exception); + JSRetainPtr username(Adopt, JSValueToStringCopy(context, arguments[1], exception)); + ASSERT(!*exception); + JSRetainPtr password(Adopt, JSValueToStringCopy(context, arguments[2], exception)); + ASSERT(!*exception); + + LayoutTestController* controller = static_cast(JSObjectGetPrivate(thisObject)); + controller->authenticateSession(url.get(), username.get(), password.get()); + return JSValueMakeUndefined(context); +} + // Static Values static JSValueRef getGlobalFlagCallback(JSContextRef context, JSObjectRef thisObject, JSStringRef propertyName, JSValueRef* exception) @@ -1355,10 +1490,13 @@ JSStaticFunction* LayoutTestController::staticFunctions() { "addUserScript", addUserScriptCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete }, { "addUserStyleSheet", addUserStyleSheetCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete }, { "apiTestNewWindowDataLoadBaseURL", apiTestNewWindowDataLoadBaseURLCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete }, + { "apiTestGoToCurrentBackForwardItem", apiTestGoToCurrentBackForwardItemCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete }, + { "callShouldCloseOnWebView", callShouldCloseOnWebViewCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete }, { "clearAllDatabases", clearAllDatabasesCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete }, { "clearBackForwardList", clearBackForwardListCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete }, { "clearPersistentUserStyleSheet", clearPersistentUserStyleSheetCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete }, { "closeWebInspector", closeWebInspectorCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete }, + { "computedStyleIncludingVisitedInfo", computedStyleIncludingVisitedInfoCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete }, { "decodeHostName", decodeHostNameCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete }, { "disableImageLoading", disableImageLoadingCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete }, { "dispatchPendingLoadRequests", dispatchPendingLoadRequestsCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete }, @@ -1387,7 +1525,9 @@ JSStaticFunction* LayoutTestController::staticFunctions() { "grantDesktopNotificationPermission", grantDesktopNotificationPermissionCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete }, { "isCommandEnabled", isCommandEnabledCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete }, { "keepWebHistory", keepWebHistoryCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete }, + { "layerTreeAsText", layerTreeAsTextCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete }, { "numberOfPages", numberOfPagesCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete }, + { "markerTextForListItem", markerTextForListItemCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete }, { "notifyDone", notifyDoneCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete }, { "numberOfActiveAnimations", numberOfActiveAnimationsCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete }, { "overridePreference", overridePreferenceCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete }, @@ -1404,6 +1544,7 @@ JSStaticFunction* LayoutTestController::staticFunctions() { "queueNonLoadingScript", queueNonLoadingScriptCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete }, { "queueReload", queueReloadCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete }, { "removeAllVisitedLinks", removeAllVisitedLinksCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete }, + { "removeOriginAccessWhitelistEntry", removeOriginAccessWhitelistEntryCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete }, { "repaintSweepHorizontally", repaintSweepHorizontallyCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete }, { "setAcceptsEditing", setAcceptsEditingCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete }, { "setAllowUniversalAccessFromFileURLs", setAllowUniversalAccessFromFileURLsCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete }, @@ -1413,45 +1554,50 @@ JSStaticFunction* LayoutTestController::staticFunctions() { "setAuthenticationPassword", setAuthenticationPasswordCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete }, { "setAuthenticationUsername", setAuthenticationUsernameCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete }, { "setAuthorAndUserStylesEnabled", setAuthorAndUserStylesEnabledCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete }, + { "setCacheModel", setCacheModelCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete }, { "setCallCloseOnWebViews", setCallCloseOnWebViewsCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete }, { "setCanOpenWindows", setCanOpenWindowsCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete }, - { "setCacheModel", setCacheModelCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete }, { "setCloseRemainingWindowsWhenComplete", setCloseRemainingWindowsWhenCompleteCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete }, { "setCustomPolicyDelegate", setCustomPolicyDelegateCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete }, { "setDatabaseQuota", setDatabaseQuotaCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete }, { "setDomainRelaxationForbiddenForURLScheme", setDomainRelaxationForbiddenForURLSchemeCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete }, + { "setFrameFlatteningEnabled", setFrameFlatteningEnabledCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete }, { "setGeolocationPermission", setGeolocationPermissionCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete }, { "setHandlesAuthenticationChallenges", setHandlesAuthenticationChallengesCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete }, - { "setPOSIXLocale", setPOSIXLocaleCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete }, { "setIconDatabaseEnabled", setIconDatabaseEnabledCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete }, { "setJavaScriptProfilingEnabled", setJavaScriptProfilingEnabledCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete }, { "setMainFrameIsFirstResponder", setMainFrameIsFirstResponderCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete }, - { "setMockGeolocationPosition", setMockGeolocationPositionCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete }, { "setMockGeolocationError", setMockGeolocationErrorCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete }, + { "setMockGeolocationPosition", setMockGeolocationPositionCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete }, { "setNewWindowsCopyBackForwardList", setNewWindowsCopyBackForwardListCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete }, + { "setPOSIXLocale", setPOSIXLocaleCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete }, { "setPersistentUserStyleSheetLocation", setPersistentUserStyleSheetLocationCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete }, { "setPopupBlockingEnabled", setPopupBlockingEnabledCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete }, { "setPrivateBrowsingEnabled", setPrivateBrowsingEnabledCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete }, - { "setXSSAuditorEnabled", setXSSAuditorEnabledCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete }, - { "setFrameSetFlatteningEnabled", setFrameSetFlatteningEnabledCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete }, { "setSelectTrailingWhitespaceEnabled", setSelectTrailingWhitespaceEnabledCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete }, { "setSmartInsertDeleteEnabled", setSmartInsertDeleteEnabledCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete }, + { "setSpatialNavigationEnabled", setSpatialNavigationEnabledCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete }, { "setStopProvisionalFrameLoads", setStopProvisionalFrameLoadsCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete }, { "setTabKeyCyclesThroughElements", setTabKeyCyclesThroughElementsCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete }, { "setTimelineProfilingEnabled", setTimelineProfilingEnabledCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete }, { "setUseDashboardCompatibilityMode", setUseDashboardCompatibilityModeCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete }, { "setUserStyleSheetEnabled", setUserStyleSheetEnabledCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete }, { "setUserStyleSheetLocation", setUserStyleSheetLocationCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete }, + { "setWebViewEditable", setWebViewEditableCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete }, + { "setWillSendRequestClearHeader", setWillSendRequestClearHeaderCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete }, { "setWillSendRequestReturnsNull", setWillSendRequestReturnsNullCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete }, { "setWillSendRequestReturnsNullOnRedirect", setWillSendRequestReturnsNullOnRedirectCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete }, { "setWindowIsKey", setWindowIsKeyCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete }, + { "setXSSAuditorEnabled", setXSSAuditorEnabledCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete }, { "showWebInspector", showWebInspectorCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete }, { "testOnscreen", testOnscreenCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete }, { "testRepaint", testRepaintCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete }, { "waitForPolicyDelegate", waitForPolicyDelegateCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete }, { "waitUntilDone", waitUntilDoneCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete }, { "windowCount", windowCountCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete }, - { "whiteListAccessFromOrigin", whiteListAccessFromOriginCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete }, + { "addOriginAccessWhitelistEntry", addOriginAccessWhitelistEntryCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete }, + { "setScrollbarPolicy", setScrollbarPolicyCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete }, + { "authenticateSession", authenticateSessionCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete }, { 0, 0, 0 } }; diff --git a/WebKitTools/DumpRenderTree/LayoutTestController.h b/WebKitTools/DumpRenderTree/LayoutTestController.h index 3add32a..198a9f3 100644 --- a/WebKitTools/DumpRenderTree/LayoutTestController.h +++ b/WebKitTools/DumpRenderTree/LayoutTestController.h @@ -31,9 +31,10 @@ #include #include -#include +#include #include #include +#include class LayoutTestController : public RefCounted { public: @@ -46,6 +47,7 @@ public: void clearAllDatabases(); void clearBackForwardList(); void clearPersistentUserStyleSheet(); + bool callShouldCloseOnWebView(); JSStringRef copyDecodedHostName(JSStringRef name); JSStringRef copyEncodedHostName(JSStringRef name); void disableImageLoading(); @@ -55,6 +57,7 @@ public: JSRetainPtr counterValueForElementById(JSStringRef id); bool isCommandEnabled(JSStringRef name); void keepWebHistory(); + JSValueRef computedStyleIncludingVisitedInfo(JSContextRef, JSValueRef); void notifyDone(); int numberOfPages(float pageWidthInPixels, float pageHeightInPixels); void overridePreference(JSStringRef key, JSStringRef value); @@ -91,7 +94,9 @@ public: void setUserStyleSheetEnabled(bool flag); void setUserStyleSheetLocation(JSStringRef path); void setXSSAuditorEnabled(bool flag); - void setFrameSetFlatteningEnabled(bool enable); + void setFrameFlatteningEnabled(bool enable); + void setSpatialNavigationEnabled(bool enable); + void setScrollbarPolicy(JSStringRef orientation, JSStringRef policy); void waitForPolicyDelegate(); size_t webHistoryItemCount(); @@ -185,6 +190,9 @@ public: void setWaitToDump(bool waitToDump); void waitToDumpWatchdogTimerFired(); + const std::set& willSendRequestClearHeaders() const { return m_willSendRequestClearHeaders; } + void setWillSendRequestClearHeader(std::string header) { m_willSendRequestClearHeaders.insert(header); } + bool willSendRequestReturnsNull() const { return m_willSendRequestReturnsNull; } void setWillSendRequestReturnsNull(bool returnsNull) { m_willSendRequestReturnsNull = returnsNull; } @@ -217,7 +225,8 @@ public: bool sampleSVGAnimationForElementAtTime(JSStringRef animationId, double time, JSStringRef elementId); unsigned numberOfActiveAnimations() const; - void whiteListAccessFromOrigin(JSStringRef sourceOrigin, JSStringRef destinationProtocol, JSStringRef destinationHost, bool allowDestinationSubdomains); + void addOriginAccessWhitelistEntry(JSStringRef sourceOrigin, JSStringRef destinationProtocol, JSStringRef destinationHost, bool allowDestinationSubdomains); + void removeOriginAccessWhitelistEntry(JSStringRef sourceOrigin, JSStringRef destinationProtocol, JSStringRef destinationHost, bool allowDestinationSubdomains); void addUserScript(JSStringRef source, bool runAtStart); void addUserStyleSheet(JSStringRef source); @@ -226,6 +235,7 @@ public: bool isGeolocationPermissionSet() const { return m_isGeolocationPermissionSet; } bool geolocationPermission() const { return m_geolocationPermission; } + void setDeveloperExtrasEnabled(bool); void showWebInspector(); void closeWebInspector(); void setTimelineProfilingEnabled(bool enabled); @@ -233,10 +243,20 @@ public: void evaluateScriptInIsolatedWorld(unsigned worldId, JSObjectRef globalObject, JSStringRef script); void setPOSIXLocale(JSStringRef locale); - + + void setWebViewEditable(bool); + // The following API test functions should probably be moved to platform-specific // unit tests outside of DRT once they exist. void apiTestNewWindowDataLoadBaseURL(JSStringRef utf8Data, JSStringRef baseURL); + void apiTestGoToCurrentBackForwardItem(); + + // Simulate a request an embedding application could make, populating per-session credential storage. + void authenticateSession(JSStringRef url, JSStringRef username, JSStringRef password); + + JSRetainPtr layerTreeAsText() const; + + JSRetainPtr markerTextForListItem(JSContextRef context, JSValueRef nodeObject) const; static const unsigned maxViewWidth; static const unsigned maxViewHeight; @@ -282,6 +302,8 @@ private: std::string m_authenticationPassword; std::string m_testPathOrURL; std::string m_expectedPixelHash; // empty string if no hash + + std::set m_willSendRequestClearHeaders; // origins which have been granted desktop notification access std::vector m_desktopNotificationAllowedOrigins; diff --git a/WebKitTools/DumpRenderTree/TestNetscapePlugIn.subproj/PluginObject.cpp b/WebKitTools/DumpRenderTree/TestNetscapePlugIn.subproj/PluginObject.cpp index 8e278f5..7d388d0 100644 --- a/WebKitTools/DumpRenderTree/TestNetscapePlugIn.subproj/PluginObject.cpp +++ b/WebKitTools/DumpRenderTree/TestNetscapePlugIn.subproj/PluginObject.cpp @@ -1,6 +1,7 @@ /* * Copyright (C) 2006, 2007, 2008 Apple Inc. All rights reserved. * Copyright (C) 2009 Holger Hans Peter Freyther + * Copyright (C) 2010 Collabora Ltd. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -134,6 +135,7 @@ enum { ID_PROPERTY_PRIVATE_BROWSING_ENABLED, ID_PROPERTY_CACHED_PRIVATE_BROWSING_ENABLED, ID_PROPERTY_THROW_EXCEPTION_PROPERTY, + ID_LAST_SET_WINDOW_ARGUMENTS, NUM_PROPERTY_IDENTIFIERS }; @@ -147,7 +149,8 @@ static const NPUTF8 *pluginPropertyIdentifierNames[NUM_PROPERTY_IDENTIFIERS] = { "returnErrorFromNewStream", "privateBrowsingEnabled", "cachedPrivateBrowsingEnabled", - "testThrowExceptionProperty" + "testThrowExceptionProperty", + "lastSetWindowArguments" }; enum { @@ -181,6 +184,8 @@ enum { ID_GET_AND_FORGET_REMEMBERED_OBJECT, ID_REF_COUNT, ID_SET_STATUS, + ID_RESIZE_TO, + ID_NORMALIZE, NUM_METHOD_IDENTIFIERS }; @@ -215,7 +220,9 @@ static const NPUTF8 *pluginMethodIdentifierNames[NUM_METHOD_IDENTIFIERS] = { "getRememberedObject", "getAndForgetRememberedObject", "refCount", - "setStatus" + "setStatus", + "resizeTo", + "normalize" }; static NPUTF8* createCStringFromNPVariant(const NPVariant* variant) @@ -283,7 +290,15 @@ static bool pluginGetProperty(NPObject* obj, NPIdentifier name, NPVariant* resul } else if (name == pluginPropertyIdentifiers[ID_PROPERTY_THROW_EXCEPTION_PROPERTY]) { browser->setexception(obj, "plugin object testThrowExceptionProperty SUCCESS"); return true; + } else if (name == pluginPropertyIdentifiers[ID_LAST_SET_WINDOW_ARGUMENTS]) { + char* buf = static_cast(browser->memalloc(256)); + snprintf(buf, 256, "x: %d, y: %d, width: %u, height: %u, clipRect: (%u, %u, %u, %u)", (int)plugin->lastWindow.x, (int)plugin->lastWindow.y, (unsigned)plugin->lastWindow.width, (unsigned)plugin->lastWindow.height, + plugin->lastWindow.clipRect.left, plugin->lastWindow.clipRect.top, plugin->lastWindow.clipRect.right - plugin->lastWindow.clipRect.left, plugin->lastWindow.clipRect.bottom - plugin->lastWindow.clipRect.top); + + STRINGZ_TO_NPVARIANT(buf, *result); + return true; } + return false; } @@ -405,8 +420,8 @@ static bool testCallback(PluginObject* obj, const NPVariant* args, uint32_t argC free(callbackString); NPVariant browserResult; - browser->invoke(obj->npp, windowScriptObject, callbackIdentifier, 0, 0, &browserResult); - browser->releasevariantvalue(&browserResult); + if (browser->invoke(obj->npp, windowScriptObject, callbackIdentifier, 0, 0, &browserResult)) + browser->releasevariantvalue(&browserResult); browser->releaseobject(windowScriptObject); @@ -519,8 +534,8 @@ static bool testEnumerate(PluginObject* obj, const NPVariant* args, uint32_t arg NPVariant args[1]; STRINGZ_TO_NPVARIANT(string, args[0]); NPVariant browserResult; - browser->invoke(obj->npp, outArray, pushIdentifier, args, 1, &browserResult); - browser->releasevariantvalue(&browserResult); + if (browser->invoke(obj->npp, outArray, pushIdentifier, args, 1, &browserResult)) + browser->releasevariantvalue(&browserResult); browser->memfree(string); } @@ -710,8 +725,10 @@ bool testDocumentOpen(NPP npp) NPVariant docVariant; browser->getproperty(npp, windowObject, documentId, &docVariant); - if (docVariant.type != NPVariantType_Object) + if (docVariant.type != NPVariantType_Object) { + browser->releaseobject(windowObject); return false; + } NPObject *documentObject = NPVARIANT_TO_OBJECT(docVariant); @@ -720,17 +737,25 @@ bool testDocumentOpen(NPP npp) STRINGZ_TO_NPVARIANT("_blank", openArgs[1]); NPVariant result; - browser->invoke(npp, documentObject, openId, openArgs, 2, &result); + if (!browser->invoke(npp, documentObject, openId, openArgs, 2, &result)) { + browser->releaseobject(windowObject); + browser->releaseobject(documentObject); + return false; + } + browser->releaseobject(documentObject); - if (result.type == NPVariantType_Object) { - pluginLogWithWindowObjectVariableArgs(windowObject, npp, "DOCUMENT OPEN SUCCESS"); - notifyTestCompletion(npp, result.value.objectValue); - browser->releaseobject(result.value.objectValue); - return true; + if (result.type != NPVariantType_Object) { + browser->releaseobject(windowObject); + browser->releasevariantvalue(&result); + return false; } - return false; + pluginLogWithWindowObjectVariableArgs(windowObject, npp, "DOCUMENT OPEN SUCCESS"); + notifyTestCompletion(npp, result.value.objectValue); + browser->releaseobject(result.value.objectValue); + browser->releaseobject(windowObject); + return true; } bool testWindowOpen(NPP npp) @@ -747,14 +772,22 @@ bool testWindowOpen(NPP npp) STRINGZ_TO_NPVARIANT("_blank", openArgs[1]); NPVariant result; - browser->invoke(npp, windowObject, openId, openArgs, 2, &result); - if (result.type == NPVariantType_Object) { - pluginLogWithWindowObjectVariableArgs(windowObject, npp, "WINDOW OPEN SUCCESS"); - notifyTestCompletion(npp, result.value.objectValue); - browser->releaseobject(result.value.objectValue); - return true; + if (!browser->invoke(npp, windowObject, openId, openArgs, 2, &result)) { + browser->releaseobject(windowObject); + return false; } - return false; + + if (result.type != NPVariantType_Object) { + browser->releaseobject(windowObject); + browser->releasevariantvalue(&result); + return false; + } + + pluginLogWithWindowObjectVariableArgs(windowObject, npp, "WINDOW OPEN SUCCESS"); + notifyTestCompletion(npp, result.value.objectValue); + browser->releaseobject(result.value.objectValue); + browser->releaseobject(windowObject); + return true; } static bool testSetStatus(PluginObject* obj, const NPVariant* args, uint32_t argCount, NPVariant* result) @@ -771,7 +804,40 @@ static bool testSetStatus(PluginObject* obj, const NPVariant* args, uint32_t arg return true; } -static NPObject* rememberedObject; +static bool testResizeTo(PluginObject* obj, const NPVariant* args, uint32_t argCount, NPVariant* result) +{ + VOID_TO_NPVARIANT(*result); + + NPObject* windowObject; + if (NPERR_NO_ERROR != browser->getvalue(obj->npp, NPNVWindowNPObject, &windowObject)) + return false; + + NPVariant callResult; + if (browser->invoke(obj->npp, windowObject, browser->getstringidentifier("resizePlugin"), args, argCount, &callResult)) + browser->releasevariantvalue(&callResult); + + // Force layout. + if (browser->getproperty(obj->npp, windowObject, browser->getstringidentifier("pageYOffset"), &callResult)) + browser->releasevariantvalue(&callResult); + + return true; +} + +static bool normalizeOverride(PluginObject* obj, const NPVariant* args, uint32_t argCount, NPVariant* result) +{ + VOID_TO_NPVARIANT(*result); + + NPObject* windowObject; + if (NPERR_NO_ERROR != browser->getvalue(obj->npp, NPNVWindowNPObject, &windowObject)) + return false; + + NPVariant callResult; + if (browser->invoke(obj->npp, windowObject, browser->getstringidentifier("pluginCallback"), args, argCount, &callResult)) + browser->releasevariantvalue(&callResult); + + return true; +} + static bool pluginInvoke(NPObject* header, NPIdentifier name, const NPVariant* args, uint32_t argCount, NPVariant* result) { @@ -834,21 +900,21 @@ static bool pluginInvoke(NPObject* header, NPIdentifier name, const NPVariant* a browser->setproperty(plugin->npp, NPVARIANT_TO_OBJECT(args[0]), stringVariantToIdentifier(args[1]), &args[2]); return true; } else if (name == pluginMethodIdentifiers[ID_REMEMBER]) { - if (rememberedObject) - browser->releaseobject(rememberedObject); - rememberedObject = NPVARIANT_TO_OBJECT(args[0]); - browser->retainobject(rememberedObject); + if (plugin->rememberedObject) + browser->releaseobject(plugin->rememberedObject); + plugin->rememberedObject = NPVARIANT_TO_OBJECT(args[0]); + browser->retainobject(plugin->rememberedObject); VOID_TO_NPVARIANT(*result); return true; } else if (name == pluginMethodIdentifiers[ID_GET_REMEMBERED_OBJECT]) { - assert(rememberedObject); - browser->retainobject(rememberedObject); - OBJECT_TO_NPVARIANT(rememberedObject, *result); + assert(plugin->rememberedObject); + browser->retainobject(plugin->rememberedObject); + OBJECT_TO_NPVARIANT(plugin->rememberedObject, *result); return true; } else if (name == pluginMethodIdentifiers[ID_GET_AND_FORGET_REMEMBERED_OBJECT]) { - assert(rememberedObject); - OBJECT_TO_NPVARIANT(rememberedObject, *result); - rememberedObject = 0; + assert(plugin->rememberedObject); + OBJECT_TO_NPVARIANT(plugin->rememberedObject, *result); + plugin->rememberedObject = 0; return true; } else if (name == pluginMethodIdentifiers[ID_REF_COUNT]) { uint32_t refCount = NPVARIANT_TO_OBJECT(args[0])->referenceCount; @@ -856,6 +922,10 @@ static bool pluginInvoke(NPObject* header, NPIdentifier name, const NPVariant* a return true; } else if (name == pluginMethodIdentifiers[ID_SET_STATUS]) return testSetStatus(plugin, args, argCount, result); + else if (name == pluginMethodIdentifiers[ID_RESIZE_TO]) + return testResizeTo(plugin, args, argCount, result); + else if (name == pluginMethodIdentifiers[ID_NORMALIZE]) + return normalizeOverride(plugin, args, argCount, result); return false; } @@ -870,6 +940,7 @@ static void pluginInvalidate(NPObject* header) { PluginObject* plugin = reinterpret_cast(header); plugin->testObject = 0; + plugin->rememberedObject = 0; } static NPObject *pluginAllocate(NPP npp, NPClass *theClass) @@ -883,11 +954,13 @@ static NPObject *pluginAllocate(NPP npp, NPClass *theClass) newInstance->npp = npp; newInstance->testObject = browser->createobject(npp, getTestClass()); + newInstance->rememberedObject = 0; newInstance->eventLogging = FALSE; newInstance->onStreamLoad = 0; newInstance->onStreamDestroy = 0; newInstance->onDestroy = 0; newInstance->onURLNotify = 0; + newInstance->onSetWindow = 0; newInstance->logDestroy = FALSE; newInstance->logSetWindow = FALSE; newInstance->returnErrorFromNewStream = FALSE; @@ -900,6 +973,7 @@ static NPObject *pluginAllocate(NPP npp, NPClass *theClass) newInstance->testDocumentOpenInDestroyStream = FALSE; newInstance->testWindowOpen = FALSE; + newInstance->testKeyboardFocusForPlugins = FALSE; return (NPObject*)newInstance; } @@ -909,6 +983,8 @@ static void pluginDeallocate(NPObject* header) PluginObject* plugin = reinterpret_cast(header); if (plugin->testObject) browser->releaseobject(plugin->testObject); + if (plugin->rememberedObject) + browser->releaseobject(plugin->rememberedObject); free(plugin->firstUrl); free(plugin->firstHeaders); @@ -947,8 +1023,8 @@ void handleCallback(PluginObject* object, const char *url, NPReason reason, void NULL_TO_NPVARIANT(args[1]); NPVariant browserResult; - browser->invoke(object->npp, windowScriptObject, callbackIdentifier, args, 2, &browserResult); - browser->releasevariantvalue(&browserResult); + if (browser->invoke(object->npp, windowScriptObject, callbackIdentifier, args, 2, &browserResult)) + browser->releasevariantvalue(&browserResult); free(strHdr); } diff --git a/WebKitTools/DumpRenderTree/TestNetscapePlugIn.subproj/PluginObject.h b/WebKitTools/DumpRenderTree/TestNetscapePlugIn.subproj/PluginObject.h index 157a1d2..2ae4dbf 100644 --- a/WebKitTools/DumpRenderTree/TestNetscapePlugIn.subproj/PluginObject.h +++ b/WebKitTools/DumpRenderTree/TestNetscapePlugIn.subproj/PluginObject.h @@ -37,13 +37,16 @@ typedef struct { NPBool returnErrorFromNewStream; NPBool cachedPrivateBrowsingMode; NPObject* testObject; + NPObject* rememberedObject; NPStream* stream; NPBool testDocumentOpenInDestroyStream; NPBool testWindowOpen; + NPBool testKeyboardFocusForPlugins; char* onStreamLoad; char* onStreamDestroy; char* onDestroy; char* onURLNotify; + char* onSetWindow; char* firstUrl; char* firstHeaders; char* lastUrl; @@ -51,6 +54,7 @@ typedef struct { #ifdef XP_MACOSX NPEventModel eventModel; #endif + NPWindow lastWindow; } PluginObject; extern NPClass *getPluginClass(void); diff --git a/WebKitTools/DumpRenderTree/TestNetscapePlugIn.subproj/main.cpp b/WebKitTools/DumpRenderTree/TestNetscapePlugIn.subproj/main.cpp index 8ef228a..a552774 100644 --- a/WebKitTools/DumpRenderTree/TestNetscapePlugIn.subproj/main.cpp +++ b/WebKitTools/DumpRenderTree/TestNetscapePlugIn.subproj/main.cpp @@ -93,6 +93,8 @@ NPError NPP_New(NPMIMEType pluginType, NPP instance, uint16 mode, int16 argc, ch else if (strcasecmp(argn[i], "src") == 0 && strcasecmp(argv[i], "data:application/x-webkit-test-netscape,returnerrorfromnewstream") == 0) obj->returnErrorFromNewStream = TRUE; + else if (strcasecmp(argn[i], "onSetWindow") == 0 && !obj->onSetWindow) + obj->onSetWindow = strdup(argv[i]); else if (strcasecmp(argn[i], "logfirstsetwindow") == 0) obj->logSetWindow = TRUE; else if (strcasecmp(argn[i], "testnpruntime") == 0) @@ -111,6 +113,8 @@ NPError NPP_New(NPMIMEType pluginType, NPP instance, uint16 mode, int16 argc, ch obj->testDocumentOpenInDestroyStream = TRUE; else if (strcasecmp(argn[i], "testwindowopen") == 0) obj->testWindowOpen = TRUE; + else if (strcasecmp(argn[i], "src") == 0 && strstr(argv[i], "plugin-document-has-focus.pl")) + obj->testKeyboardFocusForPlugins = TRUE; } #ifndef NP_NO_CARBON @@ -160,6 +164,9 @@ NPError NPP_Destroy(NPP instance, NPSavedData **save) if (obj->onURLNotify) free(obj->onURLNotify); + + if (obj->onSetWindow) + free(obj->onSetWindow); if (obj->logDestroy) pluginLog(instance, "NPP_Destroy"); @@ -174,15 +181,25 @@ NPError NPP_SetWindow(NPP instance, NPWindow *window) PluginObject* obj = static_cast(instance->pdata); if (obj) { + obj->lastWindow = *window; + if (obj->logSetWindow) { pluginLog(instance, "NPP_SetWindow: %d %d", (int)window->width, (int)window->height); - obj->logSetWindow = false; + obj->logSetWindow = FALSE; } + if (obj->onSetWindow) + executeScript(obj, obj->onSetWindow); + if (obj->testWindowOpen) { testWindowOpen(instance); obj->testWindowOpen = FALSE; } + + if (obj->testKeyboardFocusForPlugins) { + obj->eventLogging = true; + executeScript(obj, "eventSender.keyDown('A');"); + } } return NPERR_NO_ERROR; @@ -275,6 +292,11 @@ static int16_t handleEventCarbon(NPP instance, PluginObject* obj, EventRecord* e break; case keyUp: pluginLog(instance, "keyUp '%c'", (char)(event->message & 0xFF)); + if (obj->testKeyboardFocusForPlugins) { + obj->eventLogging = false; + obj->testKeyboardFocusForPlugins = FALSE; + executeScript(obj, "layoutTestController.notifyDone();"); + } break; case autoKey: pluginLog(instance, "autoKey '%c'", (char)(event->message & 0xFF)); @@ -338,7 +360,21 @@ static int16_t handleEventCocoa(NPP instance, PluginObject* obj, NPCocoaEvent* e return 1; case NPCocoaEventKeyDown: + if (event->data.key.characters) + pluginLog(instance, "keyDown '%c'", CFStringGetCharacterAtIndex(reinterpret_cast(event->data.key.characters), 0)); + return 1; + case NPCocoaEventKeyUp: + if (event->data.key.characters) { + pluginLog(instance, "keyUp '%c'", CFStringGetCharacterAtIndex(reinterpret_cast(event->data.key.characters), 0)); + if (obj->testKeyboardFocusForPlugins) { + obj->eventLogging = false; + obj->testKeyboardFocusForPlugins = FALSE; + executeScript(obj, "layoutTestController.notifyDone();"); + } + } + return 1; + case NPCocoaEventFlagsChanged: return 1; diff --git a/WebKitTools/DumpRenderTree/chromium/AccessibilityController.cpp b/WebKitTools/DumpRenderTree/chromium/AccessibilityController.cpp new file mode 100644 index 0000000..afe850c --- /dev/null +++ b/WebKitTools/DumpRenderTree/chromium/AccessibilityController.cpp @@ -0,0 +1,119 @@ +/* + * Copyright (C) 2010 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "AccessibilityController.h" + +#include "TestShell.h" +#include "public/WebAccessibilityCache.h" +#include "public/WebAccessibilityObject.h" +#include "public/WebFrame.h" +#include "public/WebString.h" +#include "public/WebView.h" + +using namespace WebKit; + +AccessibilityController::AccessibilityController(TestShell* shell) + : m_shell(shell) +{ + + bindMethod("logFocusEvents", + &AccessibilityController::logFocusEventsCallback); + bindMethod("logScrollingStartEvents", + &AccessibilityController::logScrollingStartEventsCallback); + + bindProperty("focusedElement", + &AccessibilityController::focusedElementGetterCallback); + bindProperty("rootElement", + &AccessibilityController::rootElementGetterCallback); + + bindFallbackMethod(&AccessibilityController::fallbackCallback); +} + +void AccessibilityController::bindToJavascript(WebFrame* frame, const WebString& classname) +{ + WebAccessibilityCache::enableAccessibility(); + CppBoundClass::bindToJavascript(frame, classname); +} + +void AccessibilityController::reset() +{ + m_rootElement = WebAccessibilityObject(); + m_focusedElement = WebAccessibilityObject(); + m_elements.clear(); +} + +void AccessibilityController::setFocusedElement(const WebAccessibilityObject& focusedElement) +{ + m_focusedElement = focusedElement; +} + +AccessibilityUIElement* AccessibilityController::getFocusedElement() +{ + if (m_focusedElement.isNull()) + m_focusedElement = m_shell->webView()->accessibilityObject(); + return m_elements.create(m_focusedElement); +} + +AccessibilityUIElement* AccessibilityController::getRootElement() +{ + if (m_rootElement.isNull()) + m_rootElement = m_shell->webView()->accessibilityObject(); + return m_elements.createRoot(m_rootElement); +} + +void AccessibilityController::logFocusEventsCallback(const CppArgumentList&, CppVariant* result) +{ + // As of r49031, this is not being used upstream. + result->setNull(); +} + +void AccessibilityController::logScrollingStartEventsCallback(const CppArgumentList&, CppVariant* result) +{ + // As of r49031, this is not being used upstream. + result->setNull(); +} + +void AccessibilityController::focusedElementGetterCallback(CppVariant* result) +{ + result->set(*(getFocusedElement()->getAsCppVariant())); +} + +void AccessibilityController::rootElementGetterCallback(CppVariant* result) +{ + result->set(*(getRootElement()->getAsCppVariant())); +} + +void AccessibilityController::fallbackCallback(const CppArgumentList&, CppVariant* result) +{ + printf("CONSOLE MESSAGE: JavaScript ERROR: unknown method called on " + "AccessibilityController\n"); + result->setNull(); +} diff --git a/WebKitTools/DumpRenderTree/chromium/AccessibilityController.h b/WebKitTools/DumpRenderTree/chromium/AccessibilityController.h new file mode 100644 index 0000000..3cde7cc --- /dev/null +++ b/WebKitTools/DumpRenderTree/chromium/AccessibilityController.h @@ -0,0 +1,73 @@ +/* + * Copyright (C) 2010 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef AccessibilityController_h +#define AccessibilityController_h + +#include "AccessibilityUIElement.h" +#include "CppBoundClass.h" + +namespace WebKit { +class WebAccessibilityObject; +class WebFrame; +} + +class TestShell; + +class AccessibilityController : public CppBoundClass { +public: + explicit AccessibilityController(TestShell*); + + // Shadow to include accessibility initialization. + void bindToJavascript(WebKit::WebFrame*, const WebKit::WebString& classname); + void reset(); + + void setFocusedElement(const WebKit::WebAccessibilityObject&); + AccessibilityUIElement* getFocusedElement(); + AccessibilityUIElement* getRootElement(); + +private: + // Bound methods and properties + void logFocusEventsCallback(const CppArgumentList&, CppVariant*); + void logScrollingStartEventsCallback(const CppArgumentList&, CppVariant*); + void fallbackCallback(const CppArgumentList&, CppVariant*); + + void focusedElementGetterCallback(CppVariant*); + void rootElementGetterCallback(CppVariant*); + + WebKit::WebAccessibilityObject m_focusedElement; + WebKit::WebAccessibilityObject m_rootElement; + + AccessibilityUIElementList m_elements; + + TestShell* m_shell; +}; + +#endif // AccessibilityController_h diff --git a/WebKitTools/DumpRenderTree/chromium/AccessibilityUIElement.cpp b/WebKitTools/DumpRenderTree/chromium/AccessibilityUIElement.cpp new file mode 100644 index 0000000..8698e25 --- /dev/null +++ b/WebKitTools/DumpRenderTree/chromium/AccessibilityUIElement.cpp @@ -0,0 +1,585 @@ +/* + * Copyright (C) 2010 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "AccessibilityUIElement.h" + +#include "public/WebAccessibilityObject.h" +#include "public/WebCString.h" +#include "public/WebString.h" +#include + +using namespace WebKit; +using namespace std; + +// Map role value to string, matching Safari/Mac platform implementation to +// avoid rebaselining layout tests. +static string roleToString(WebAccessibilityRole role) +{ + string result = "AXRole: AX"; + switch (role) { + case WebAccessibilityRoleButton: + return result.append("Button"); + case WebAccessibilityRoleRadioButton: + return result.append("RadioButton"); + case WebAccessibilityRoleCheckBox: + return result.append("CheckBox"); + case WebAccessibilityRoleSlider: + return result.append("Slider"); + case WebAccessibilityRoleTabGroup: + return result.append("TabGroup"); + case WebAccessibilityRoleTextField: + return result.append("TextField"); + case WebAccessibilityRoleStaticText: + return result.append("StaticText"); + case WebAccessibilityRoleTextArea: + return result.append("TextArea"); + case WebAccessibilityRoleScrollArea: + return result.append("ScrollArea"); + case WebAccessibilityRolePopUpButton: + return result.append("PopUpButton"); + case WebAccessibilityRoleMenuButton: + return result.append("MenuButton"); + case WebAccessibilityRoleTable: + return result.append("Table"); + case WebAccessibilityRoleApplication: + return result.append("Application"); + case WebAccessibilityRoleGroup: + return result.append("Group"); + case WebAccessibilityRoleRadioGroup: + return result.append("RadioGroup"); + case WebAccessibilityRoleList: + return result.append("List"); + case WebAccessibilityRoleScrollBar: + return result.append("ScrollBar"); + case WebAccessibilityRoleValueIndicator: + return result.append("ValueIndicator"); + case WebAccessibilityRoleImage: + return result.append("Image"); + case WebAccessibilityRoleMenuBar: + return result.append("MenuBar"); + case WebAccessibilityRoleMenu: + return result.append("Menu"); + case WebAccessibilityRoleMenuItem: + return result.append("MenuItem"); + case WebAccessibilityRoleColumn: + return result.append("Column"); + case WebAccessibilityRoleRow: + return result.append("Row"); + case WebAccessibilityRoleToolbar: + return result.append("Toolbar"); + case WebAccessibilityRoleBusyIndicator: + return result.append("BusyIndicator"); + case WebAccessibilityRoleProgressIndicator: + return result.append("ProgressIndicator"); + case WebAccessibilityRoleWindow: + return result.append("Window"); + case WebAccessibilityRoleDrawer: + return result.append("Drawer"); + case WebAccessibilityRoleSystemWide: + return result.append("SystemWide"); + case WebAccessibilityRoleOutline: + return result.append("Outline"); + case WebAccessibilityRoleIncrementor: + return result.append("Incrementor"); + case WebAccessibilityRoleBrowser: + return result.append("Browser"); + case WebAccessibilityRoleComboBox: + return result.append("ComboBox"); + case WebAccessibilityRoleSplitGroup: + return result.append("SplitGroup"); + case WebAccessibilityRoleSplitter: + return result.append("Splitter"); + case WebAccessibilityRoleColorWell: + return result.append("ColorWell"); + case WebAccessibilityRoleGrowArea: + return result.append("GrowArea"); + case WebAccessibilityRoleSheet: + return result.append("Sheet"); + case WebAccessibilityRoleHelpTag: + return result.append("HelpTag"); + case WebAccessibilityRoleMatte: + return result.append("Matte"); + case WebAccessibilityRoleRuler: + return result.append("Ruler"); + case WebAccessibilityRoleRulerMarker: + return result.append("RulerMarker"); + case WebAccessibilityRoleLink: + return result.append("Link"); + case WebAccessibilityRoleDisclosureTriangle: + return result.append("DisclosureTriangle"); + case WebAccessibilityRoleGrid: + return result.append("Grid"); + case WebAccessibilityRoleCell: + return result.append("Cell"); + case WebAccessibilityRoleColumnHeader: + return result.append("ColumnHeader"); + case WebAccessibilityRoleRowHeader: + return result.append("RowHeader"); + case WebAccessibilityRoleWebCoreLink: + // Maps to Link role. + return result.append("Link"); + case WebAccessibilityRoleImageMapLink: + return result.append("ImageMapLink"); + case WebAccessibilityRoleImageMap: + return result.append("ImageMap"); + case WebAccessibilityRoleListMarker: + return result.append("ListMarker"); + case WebAccessibilityRoleWebArea: + return result.append("WebArea"); + case WebAccessibilityRoleHeading: + return result.append("Heading"); + case WebAccessibilityRoleListBox: + return result.append("ListBox"); + case WebAccessibilityRoleListBoxOption: + return result.append("ListBoxOption"); + case WebAccessibilityRoleTableHeaderContainer: + return result.append("TableHeaderContainer"); + case WebAccessibilityRoleDefinitionListTerm: + return result.append("DefinitionListTerm"); + case WebAccessibilityRoleDefinitionListDefinition: + return result.append("DefinitionListDefinition"); + case WebAccessibilityRoleAnnotation: + return result.append("Annotation"); + case WebAccessibilityRoleSliderThumb: + return result.append("SliderThumb"); + case WebAccessibilityRoleLandmarkApplication: + return result.append("LandmarkApplication"); + case WebAccessibilityRoleLandmarkBanner: + return result.append("LandmarkBanner"); + case WebAccessibilityRoleLandmarkComplementary: + return result.append("LandmarkComplementary"); + case WebAccessibilityRoleLandmarkContentInfo: + return result.append("LandmarkContentInfo"); + case WebAccessibilityRoleLandmarkMain: + return result.append("LandmarkMain"); + case WebAccessibilityRoleLandmarkNavigation: + return result.append("LandmarkNavigation"); + case WebAccessibilityRoleLandmarkSearch: + return result.append("LandmarkSearch"); + case WebAccessibilityRoleApplicationLog: + return result.append("ApplicationLog"); + case WebAccessibilityRoleApplicationMarquee: + return result.append("ApplicationMarquee"); + case WebAccessibilityRoleApplicationStatus: + return result.append("ApplicationStatus"); + case WebAccessibilityRoleApplicationTimer: + return result.append("ApplicationTimer"); + case WebAccessibilityRoleDocument: + return result.append("Document"); + case WebAccessibilityRoleDocumentArticle: + return result.append("DocumentArticle"); + case WebAccessibilityRoleDocumentNote: + return result.append("DocumentNote"); + case WebAccessibilityRoleDocumentRegion: + return result.append("DocumentRegion"); + case WebAccessibilityRoleUserInterfaceTooltip: + return result.append("UserInterfaceTooltip"); + default: + // Also matches WebAccessibilityRoleUnknown. + return result.append("Unknown"); + } +} + +string getDescription(const WebAccessibilityObject& object) +{ + string description = object.accessibilityDescription().utf8(); + return description.insert(0, "AXDescription: "); +} + +string getRole(const WebAccessibilityObject& object) +{ + return roleToString(object.roleValue()); +} + +string getTitle(const WebAccessibilityObject& object) +{ + string title = object.title().utf8(); + return title.insert(0, "AXTitle: "); +} + +string getAttributes(const WebAccessibilityObject& object) +{ + // FIXME: Concatenate all attributes of the AccessibilityObject. + string attributes(getTitle(object)); + attributes.append("\n"); + attributes.append(getRole(object)); + attributes.append("\n"); + attributes.append(getDescription(object)); + return attributes; +} + + +// Collects attributes into a string, delimited by dashes. Used by all methods +// that output lists of attributes: attributesOfLinkedUIElementsCallback, +// AttributesOfChildrenCallback, etc. +class AttributesCollector { +public: + void collectAttributes(const WebAccessibilityObject& object) + { + m_attributes.append("\n------------\n"); + m_attributes.append(getAttributes(object)); + } + + string attributes() const { return m_attributes; } + +private: + string m_attributes; +}; + +AccessibilityUIElement::AccessibilityUIElement(const WebAccessibilityObject& object, Factory* factory) + : m_accessibilityObject(object) + , m_factory(factory) +{ + + ASSERT(factory); + + bindMethod("allAttributes", &AccessibilityUIElement::allAttributesCallback); + bindMethod("attributesOfLinkedUIElements", + &AccessibilityUIElement::attributesOfLinkedUIElementsCallback); + bindMethod("attributesOfDocumentLinks", + &AccessibilityUIElement::attributesOfDocumentLinksCallback); + bindMethod("attributesOfChildren", + &AccessibilityUIElement::attributesOfChildrenCallback); + bindMethod("parameterizedAttributeNames", + &AccessibilityUIElement::parametrizedAttributeNamesCallback); + bindMethod("lineForIndex", &AccessibilityUIElement::lineForIndexCallback); + bindMethod("boundsForRange", &AccessibilityUIElement::boundsForRangeCallback); + bindMethod("stringForRange", &AccessibilityUIElement::stringForRangeCallback); + bindMethod("childAtIndex", &AccessibilityUIElement::childAtIndexCallback); + bindMethod("elementAtPoint", &AccessibilityUIElement::elementAtPointCallback); + bindMethod("attributesOfColumnHeaders", + &AccessibilityUIElement::attributesOfColumnHeadersCallback); + bindMethod("attributesOfRowHeaders", + &AccessibilityUIElement::attributesOfRowHeadersCallback); + bindMethod("attributesOfColumns", + &AccessibilityUIElement::attributesOfColumnsCallback); + bindMethod("attributesOfRows", + &AccessibilityUIElement::attributesOfRowsCallback); + bindMethod("attributesOfVisibleCells", + &AccessibilityUIElement::attributesOfVisibleCellsCallback); + bindMethod("attributesOfHeader", + &AccessibilityUIElement::attributesOfHeaderCallback); + bindMethod("indexInTable", &AccessibilityUIElement::indexInTableCallback); + bindMethod("rowIndexRange", &AccessibilityUIElement::rowIndexRangeCallback); + bindMethod("columnIndexRange", + &AccessibilityUIElement::columnIndexRangeCallback); + bindMethod("cellForColumnAndRow", + &AccessibilityUIElement::cellForColumnAndRowCallback); + bindMethod("titleUIElement", &AccessibilityUIElement::titleUIElementCallback); + bindMethod("setSelectedTextRange", + &AccessibilityUIElement::setSelectedTextRangeCallback); + bindMethod("attributeValue", &AccessibilityUIElement::attributeValueCallback); + bindMethod("isAttributeSettable", + &AccessibilityUIElement::isAttributeSettableCallback); + bindMethod("isActionSupported", + &AccessibilityUIElement::isActionSupportedCallback); + bindMethod("parentElement", &AccessibilityUIElement::parentElementCallback); + bindMethod("increment", &AccessibilityUIElement::incrementCallback); + bindMethod("decrement", &AccessibilityUIElement::decrementCallback); + + bindProperty("role", &AccessibilityUIElement::roleGetterCallback); + bindProperty("subrole", &m_subrole); + bindProperty("title", &AccessibilityUIElement::titleGetterCallback); + bindProperty("description", + &AccessibilityUIElement::descriptionGetterCallback); + bindProperty("language", &m_language); + bindProperty("x", &m_x); + bindProperty("y", &m_y); + bindProperty("width", &m_width); + bindProperty("height", &m_height); + bindProperty("clickPointX", &m_clickPointX); + bindProperty("clickPointY", &m_clickPointY); + bindProperty("intValue", &m_intValue); + bindProperty("minValue", &m_minValue); + bindProperty("maxValue", &m_maxValue); + bindProperty("childrenCount", + &AccessibilityUIElement::childrenCountGetterCallback); + bindProperty("insertionPointLineNumber", &m_insertionPointLineNumber); + bindProperty("selectedTextRange", &m_selectedTextRange); + bindProperty("isEnabled", &AccessibilityUIElement::isEnabledGetterCallback); + bindProperty("isRequired", &m_isRequired); + bindProperty("isSelected", &AccessibilityUIElement::isSelectedGetterCallback); + bindProperty("valueDescription", &m_valueDescription); + + bindFallbackMethod(&AccessibilityUIElement::fallbackCallback); +} + +AccessibilityUIElement* AccessibilityUIElement::getChildAtIndex(unsigned index) +{ + return m_factory->create(accessibilityObject().childAt(index)); +} + +void AccessibilityUIElement::allAttributesCallback(const CppArgumentList&, CppVariant* result) +{ + result->set(getAttributes(accessibilityObject())); +} + +void AccessibilityUIElement::attributesOfLinkedUIElementsCallback(const CppArgumentList&, CppVariant* result) +{ + result->setNull(); +} + +void AccessibilityUIElement::attributesOfDocumentLinksCallback(const CppArgumentList&, CppVariant* result) +{ + result->setNull(); +} + +void AccessibilityUIElement::attributesOfChildrenCallback(const CppArgumentList& arguments, CppVariant* result) +{ + AttributesCollector collector; + unsigned size = accessibilityObject().childCount(); + for (unsigned i = 0; i < size; ++i) + collector.collectAttributes(accessibilityObject().childAt(i)); + result->set(collector.attributes()); +} + +void AccessibilityUIElement::parametrizedAttributeNamesCallback(const CppArgumentList&, CppVariant* result) +{ + result->setNull(); +} + +void AccessibilityUIElement::lineForIndexCallback(const CppArgumentList&, CppVariant* result) +{ + result->setNull(); +} + +void AccessibilityUIElement::boundsForRangeCallback(const CppArgumentList&, CppVariant* result) +{ + result->setNull(); +} + +void AccessibilityUIElement::stringForRangeCallback(const CppArgumentList&, CppVariant* result) +{ + result->setNull(); +} + +void AccessibilityUIElement::childAtIndexCallback(const CppArgumentList& arguments, CppVariant* result) +{ + if (!arguments.size() || !arguments[0].isNumber()) { + result->setNull(); + return; + } + + AccessibilityUIElement* child = getChildAtIndex(arguments[0].toInt32()); + if (!child) { + result->setNull(); + return; + } + + result->set(*(child->getAsCppVariant())); +} + +void AccessibilityUIElement::elementAtPointCallback(const CppArgumentList&, CppVariant* result) +{ + result->setNull(); +} + +void AccessibilityUIElement::attributesOfColumnHeadersCallback(const CppArgumentList&, CppVariant* result) +{ + result->setNull(); +} + +void AccessibilityUIElement::attributesOfRowHeadersCallback(const CppArgumentList&, CppVariant* result) +{ + result->setNull(); +} + +void AccessibilityUIElement::attributesOfColumnsCallback(const CppArgumentList&, CppVariant* result) +{ + result->setNull(); +} + +void AccessibilityUIElement::attributesOfRowsCallback(const CppArgumentList&, CppVariant* result) +{ + result->setNull(); +} + +void AccessibilityUIElement::attributesOfVisibleCellsCallback(const CppArgumentList&, CppVariant* result) +{ + result->setNull(); +} + +void AccessibilityUIElement::attributesOfHeaderCallback(const CppArgumentList&, CppVariant* result) +{ + result->setNull(); +} + +void AccessibilityUIElement::indexInTableCallback(const CppArgumentList&, CppVariant* result) +{ + result->setNull(); +} + +void AccessibilityUIElement::rowIndexRangeCallback(const CppArgumentList&, CppVariant* result) +{ + result->setNull(); +} + +void AccessibilityUIElement::columnIndexRangeCallback(const CppArgumentList&, CppVariant* result) +{ + result->setNull(); +} + +void AccessibilityUIElement::cellForColumnAndRowCallback(const CppArgumentList&, CppVariant* result) +{ + result->setNull(); +} + +void AccessibilityUIElement::titleUIElementCallback(const CppArgumentList&, CppVariant* result) +{ + result->setNull(); +} + +void AccessibilityUIElement::setSelectedTextRangeCallback(const CppArgumentList&, CppVariant* result) +{ + result->setNull(); +} + +void AccessibilityUIElement::attributeValueCallback(const CppArgumentList&, CppVariant* result) +{ + result->setNull(); +} + +void AccessibilityUIElement::isAttributeSettableCallback(const CppArgumentList& arguments, CppVariant* result) +{ + if (arguments.size() < 1 && !arguments[0].isString()) { + result->setNull(); + return; + } + + string attribute = arguments[0].toString(); + bool settable = false; + if (attribute == "AXValue") + settable = accessibilityObject().canSetValueAttribute(); + result->set(settable); +} + +void AccessibilityUIElement::isActionSupportedCallback(const CppArgumentList&, CppVariant* result) +{ + // This one may be really hard to implement. + // Not exposed by AccessibilityObject. + result->setNull(); +} + +void AccessibilityUIElement::parentElementCallback(const CppArgumentList&, CppVariant* result) +{ + result->setNull(); +} + +void AccessibilityUIElement::incrementCallback(const CppArgumentList&, CppVariant* result) +{ + result->setNull(); +} + +void AccessibilityUIElement::decrementCallback(const CppArgumentList&, CppVariant* result) +{ + result->setNull(); +} + +void AccessibilityUIElement::fallbackCallback(const CppArgumentList &, CppVariant* result) +{ + // FIXME: Implement this. + result->setNull(); +} + +void AccessibilityUIElement::childrenCountGetterCallback(CppVariant* result) +{ + int count = 1; // Root object always has only one child, the WebView. + if (!isRoot()) + count = accessibilityObject().childCount(); + result->set(count); +} + +void AccessibilityUIElement::descriptionGetterCallback(CppVariant* result) +{ + result->set(getDescription(accessibilityObject())); +} + +void AccessibilityUIElement::isEnabledGetterCallback(CppVariant* result) +{ + result->set(accessibilityObject().isEnabled()); +} + +void AccessibilityUIElement::isSelectedGetterCallback(CppVariant* result) +{ + result->setNull(); +} + +void AccessibilityUIElement::roleGetterCallback(CppVariant* result) +{ + result->set(getRole(accessibilityObject())); +} + +void AccessibilityUIElement::titleGetterCallback(CppVariant* result) +{ + result->set(getTitle(accessibilityObject())); +} + + +RootAccessibilityUIElement::RootAccessibilityUIElement(const WebAccessibilityObject &object, Factory *factory) + : AccessibilityUIElement(object, factory) { } + +AccessibilityUIElement* RootAccessibilityUIElement::getChildAtIndex(unsigned index) +{ + if (index) + return 0; + + return factory()->create(accessibilityObject()); +} + + +AccessibilityUIElementList ::~AccessibilityUIElementList() +{ + clear(); +} + +void AccessibilityUIElementList::clear() +{ + for (ElementList::iterator i = m_elements.begin(); i != m_elements.end(); ++i) + delete (*i); + m_elements.clear(); +} + +AccessibilityUIElement* AccessibilityUIElementList::create(const WebAccessibilityObject& object) +{ + if (object.isNull()) + return 0; + + AccessibilityUIElement* element = new AccessibilityUIElement(object, this); + m_elements.append(element); + return element; +} + +AccessibilityUIElement* AccessibilityUIElementList::createRoot(const WebAccessibilityObject& object) +{ + AccessibilityUIElement* element = new RootAccessibilityUIElement(object, this); + m_elements.append(element); + return element; +} diff --git a/WebKitTools/DumpRenderTree/chromium/AccessibilityUIElement.h b/WebKitTools/DumpRenderTree/chromium/AccessibilityUIElement.h new file mode 100644 index 0000000..df3f5b9 --- /dev/null +++ b/WebKitTools/DumpRenderTree/chromium/AccessibilityUIElement.h @@ -0,0 +1,142 @@ +/* + * Copyright (C) 2010 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef AccessibilityUIElement_h +#define AccessibilityUIElement_h + +#include "CppBoundClass.h" +#include "public/WebAccessibilityObject.h" +#include + +class AccessibilityUIElement : public CppBoundClass { +public: + class Factory { + public: + virtual ~Factory() { } + virtual AccessibilityUIElement* create(const WebKit::WebAccessibilityObject&) = 0; + }; + + AccessibilityUIElement(const WebKit::WebAccessibilityObject&, Factory*); + + virtual AccessibilityUIElement* getChildAtIndex(unsigned); + virtual bool isRoot() const { return false; } + +protected: + const WebKit::WebAccessibilityObject& accessibilityObject() const { return m_accessibilityObject; } + Factory* factory() const { return m_factory; } + +private: + // Bound methods and properties. + void allAttributesCallback(const CppArgumentList&, CppVariant*); + void attributesOfLinkedUIElementsCallback(const CppArgumentList&, CppVariant*); + void attributesOfDocumentLinksCallback(const CppArgumentList&, CppVariant*); + void attributesOfChildrenCallback(const CppArgumentList&, CppVariant*); + void parametrizedAttributeNamesCallback(const CppArgumentList&, CppVariant*); + void lineForIndexCallback(const CppArgumentList&, CppVariant*); + void boundsForRangeCallback(const CppArgumentList&, CppVariant*); + void stringForRangeCallback(const CppArgumentList&, CppVariant*); + void childAtIndexCallback(const CppArgumentList&, CppVariant*); + void elementAtPointCallback(const CppArgumentList&, CppVariant*); + void attributesOfColumnHeadersCallback(const CppArgumentList&, CppVariant*); + void attributesOfRowHeadersCallback(const CppArgumentList&, CppVariant*); + void attributesOfColumnsCallback(const CppArgumentList&, CppVariant*); + void attributesOfRowsCallback(const CppArgumentList&, CppVariant*); + void attributesOfVisibleCellsCallback(const CppArgumentList&, CppVariant*); + void attributesOfHeaderCallback(const CppArgumentList&, CppVariant*); + void indexInTableCallback(const CppArgumentList&, CppVariant*); + void rowIndexRangeCallback(const CppArgumentList&, CppVariant*); + void columnIndexRangeCallback(const CppArgumentList&, CppVariant*); + void cellForColumnAndRowCallback(const CppArgumentList&, CppVariant*); + void titleUIElementCallback(const CppArgumentList&, CppVariant*); + void setSelectedTextRangeCallback(const CppArgumentList&, CppVariant*); + void attributeValueCallback(const CppArgumentList&, CppVariant*); + void isAttributeSettableCallback(const CppArgumentList&, CppVariant*); + void isActionSupportedCallback(const CppArgumentList&, CppVariant*); + void parentElementCallback(const CppArgumentList&, CppVariant*); + void incrementCallback(const CppArgumentList&, CppVariant*); + void decrementCallback(const CppArgumentList&, CppVariant*); + void fallbackCallback(const CppArgumentList&, CppVariant*); + + void childrenCountGetterCallback(CppVariant*); + void descriptionGetterCallback(CppVariant*); + void isEnabledGetterCallback(CppVariant*); + void isSelectedGetterCallback(CppVariant*); + void roleGetterCallback(CppVariant*); + void titleGetterCallback(CppVariant*); + + CppVariant m_subrole; + CppVariant m_language; + CppVariant m_x; + CppVariant m_y; + CppVariant m_width; + CppVariant m_height; + CppVariant m_clickPointX; + CppVariant m_clickPointY; + CppVariant m_intValue; + CppVariant m_minValue; + CppVariant m_maxValue; + CppVariant m_childrenCount; + CppVariant m_insertionPointLineNumber; + CppVariant m_selectedTextRange; + CppVariant m_isRequired; + CppVariant m_valueDescription; + + WebKit::WebAccessibilityObject m_accessibilityObject; + Factory* m_factory; +}; + + +class RootAccessibilityUIElement : public AccessibilityUIElement { +public: + RootAccessibilityUIElement(const WebKit::WebAccessibilityObject&, Factory*); + + virtual AccessibilityUIElement* getChildAtIndex(unsigned); + virtual bool isRoot() const { return true; } +}; + + +// Provides simple lifetime management of the AccessibilityUIElement instances: +// all AccessibilityUIElements ever created from the controller are stored in +// a list and cleared explicitly. +class AccessibilityUIElementList : public AccessibilityUIElement::Factory { +public: + AccessibilityUIElementList() { } + virtual ~AccessibilityUIElementList(); + + void clear(); + virtual AccessibilityUIElement* create(const WebKit::WebAccessibilityObject&); + AccessibilityUIElement* createRoot(const WebKit::WebAccessibilityObject&); + +private: + typedef Vector ElementList; + ElementList m_elements; +}; + +#endif // AccessibilityUIElement_h diff --git a/WebKitTools/DumpRenderTree/chromium/CppBoundClass.cpp b/WebKitTools/DumpRenderTree/chromium/CppBoundClass.cpp new file mode 100644 index 0000000..839787a --- /dev/null +++ b/WebKitTools/DumpRenderTree/chromium/CppBoundClass.cpp @@ -0,0 +1,350 @@ +/* + * Copyright (C) 2010 Google Inc. All rights reserved. + * Copyright (C) 2009 Pawel Hajdan (phajdan.jr@chromium.org) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +// This file contains definitions for CppBoundClass + +// Here's the control flow of a JS method getting forwarded to a class. +// - Something calls our NPObject with a function like "Invoke". +// - CppNPObject's static invoke() function forwards it to its attached +// CppBoundClass's invoke() method. +// - CppBoundClass has then overridden invoke() to look up the function +// name in its internal map of methods, and then calls the appropriate +// method. + +#include "config.h" +#include "CppBoundClass.h" + +#include "public/WebBindings.h" +#include "public/WebFrame.h" +#include "public/WebString.h" +#include +#include + +using namespace WebKit; +using namespace std; + +class CppVariantPropertyCallback : public CppBoundClass::PropertyCallback { +public: + CppVariantPropertyCallback(CppVariant* value) : m_value(value) { } + + virtual bool getValue(CppVariant* value) + { + value->set(*m_value); + return true; + } + + virtual bool setValue(const CppVariant& value) + { + m_value->set(value); + return true; + } + +private: + CppVariant* m_value; +}; + +class GetterPropertyCallback : public CppBoundClass::PropertyCallback { +public: + GetterPropertyCallback(CppBoundClass::GetterCallback* callback) + : m_callback(callback) { } + + virtual bool getValue(CppVariant* value) + { + m_callback->run(value); + return true; + } + + virtual bool setValue(const CppVariant& value) { return false; } + +private: + OwnPtr m_callback; +}; + +// Our special NPObject type. We extend an NPObject with a pointer to a +// CppBoundClass, which is just a C++ interface that we forward all NPObject +// callbacks to. +struct CppNPObject { + NPObject parent; // This must be the first field in the struct. + CppBoundClass* boundClass; + + // + // All following objects and functions are static, and just used to interface + // with NPObject/NPClass. + // + + // An NPClass associates static functions of CppNPObject with the + // function pointers used by the JS runtime. + static NPClass npClass; + + // Allocate a new NPObject with the specified class. + static NPObject* allocate(NPP, NPClass*); + + // Free an object. + static void deallocate(NPObject*); + + // Returns true if the C++ class associated with this NPObject exposes the + // given property. Called by the JS runtime. + static bool hasProperty(NPObject*, NPIdentifier); + + // Returns true if the C++ class associated with this NPObject exposes the + // given method. Called by the JS runtime. + static bool hasMethod(NPObject*, NPIdentifier); + + // If the given method is exposed by the C++ class associated with this + // NPObject, invokes it with the given arguments and returns a result. Otherwise, + // returns "undefined" (in the JavaScript sense). Called by the JS runtime. + static bool invoke(NPObject*, NPIdentifier, + const NPVariant* arguments, uint32_t argumentCount, + NPVariant* result); + + // If the given property is exposed by the C++ class associated with this + // NPObject, returns its value. Otherwise, returns "undefined" (in the + // JavaScript sense). Called by the JS runtime. + static bool getProperty(NPObject*, NPIdentifier, NPVariant* result); + + // If the given property is exposed by the C++ class associated with this + // NPObject, sets its value. Otherwise, does nothing. Called by the JS + // runtime. + static bool setProperty(NPObject*, NPIdentifier, const NPVariant* value); +}; + +// Build CppNPObject's static function pointers into an NPClass, for use +// in constructing NPObjects for the C++ classes. +NPClass CppNPObject::npClass = { + NP_CLASS_STRUCT_VERSION, + CppNPObject::allocate, + CppNPObject::deallocate, + /* NPInvalidateFunctionPtr */ 0, + CppNPObject::hasMethod, + CppNPObject::invoke, + /* NPInvokeDefaultFunctionPtr */ 0, + CppNPObject::hasProperty, + CppNPObject::getProperty, + CppNPObject::setProperty, + /* NPRemovePropertyFunctionPtr */ 0 +}; + +NPObject* CppNPObject::allocate(NPP npp, NPClass* aClass) +{ + CppNPObject* obj = new CppNPObject; + // obj->parent will be initialized by the NPObject code calling this. + obj->boundClass = 0; + return &obj->parent; +} + +void CppNPObject::deallocate(NPObject* npObj) +{ + CppNPObject* obj = reinterpret_cast(npObj); + delete obj; +} + +bool CppNPObject::hasMethod(NPObject* npObj, NPIdentifier ident) +{ + CppNPObject* obj = reinterpret_cast(npObj); + return obj->boundClass->hasMethod(ident); +} + +bool CppNPObject::hasProperty(NPObject* npObj, NPIdentifier ident) +{ + CppNPObject* obj = reinterpret_cast(npObj); + return obj->boundClass->hasProperty(ident); +} + +bool CppNPObject::invoke(NPObject* npObj, NPIdentifier ident, + const NPVariant* arguments, uint32_t argumentCount, + NPVariant* result) +{ + CppNPObject* obj = reinterpret_cast(npObj); + return obj->boundClass->invoke(ident, arguments, argumentCount, result); +} + +bool CppNPObject::getProperty(NPObject* npObj, NPIdentifier ident, NPVariant* result) +{ + CppNPObject* obj = reinterpret_cast(npObj); + return obj->boundClass->getProperty(ident, result); +} + +bool CppNPObject::setProperty(NPObject* npObj, NPIdentifier ident, const NPVariant* value) +{ + CppNPObject* obj = reinterpret_cast(npObj); + return obj->boundClass->setProperty(ident, value); +} + +CppBoundClass::~CppBoundClass() +{ + for (MethodList::iterator i = m_methods.begin(); i != m_methods.end(); ++i) + delete i->second; + + for (PropertyList::iterator i = m_properties.begin(); i != m_properties.end(); ++i) + delete i->second; + + // Unregister ourselves if we were bound to a frame. + if (m_boundToFrame) + WebBindings::unregisterObject(NPVARIANT_TO_OBJECT(m_selfVariant)); +} + +bool CppBoundClass::hasMethod(NPIdentifier ident) const +{ + return m_methods.find(ident) != m_methods.end(); +} + +bool CppBoundClass::hasProperty(NPIdentifier ident) const +{ + return m_properties.find(ident) != m_properties.end(); +} + +bool CppBoundClass::invoke(NPIdentifier ident, + const NPVariant* arguments, + size_t argumentCount, + NPVariant* result) { + MethodList::const_iterator end = m_methods.end(); + MethodList::const_iterator method = m_methods.find(ident); + Callback* callback; + if (method == end) { + if (!m_fallbackCallback.get()) { + VOID_TO_NPVARIANT(*result); + return false; + } + callback = m_fallbackCallback.get(); + } else + callback = (*method).second; + + // Build a CppArgumentList argument vector from the NPVariants coming in. + CppArgumentList cppArguments(argumentCount); + for (size_t i = 0; i < argumentCount; i++) + cppArguments[i].set(arguments[i]); + + CppVariant cppResult; + callback->run(cppArguments, &cppResult); + + cppResult.copyToNPVariant(result); + return true; +} + +bool CppBoundClass::getProperty(NPIdentifier ident, NPVariant* result) const +{ + PropertyList::const_iterator callback = m_properties.find(ident); + if (callback == m_properties.end()) { + VOID_TO_NPVARIANT(*result); + return false; + } + + CppVariant cppValue; + if (!callback->second->getValue(&cppValue)) + return false; + cppValue.copyToNPVariant(result); + return true; +} + +bool CppBoundClass::setProperty(NPIdentifier ident, const NPVariant* value) +{ + PropertyList::iterator callback = m_properties.find(ident); + if (callback == m_properties.end()) + return false; + + CppVariant cppValue; + cppValue.set(*value); + return (*callback).second->setValue(cppValue); +} + +void CppBoundClass::bindCallback(const string& name, Callback* callback) +{ + NPIdentifier ident = WebBindings::getStringIdentifier(name.c_str()); + MethodList::iterator oldCallback = m_methods.find(ident); + if (oldCallback != m_methods.end()) { + delete oldCallback->second; + if (!callback) { + m_methods.remove(oldCallback); + return; + } + } + + m_methods.set(ident, callback); +} + +void CppBoundClass::bindGetterCallback(const string& name, GetterCallback* callback) +{ + PropertyCallback* propertyCallback = callback ? new GetterPropertyCallback(callback) : 0; + bindProperty(name, propertyCallback); +} + +void CppBoundClass::bindProperty(const string& name, CppVariant* prop) +{ + PropertyCallback* propertyCallback = prop ? new CppVariantPropertyCallback(prop) : 0; + bindProperty(name, propertyCallback); +} + +void CppBoundClass::bindProperty(const string& name, PropertyCallback* callback) +{ + NPIdentifier ident = WebBindings::getStringIdentifier(name.c_str()); + PropertyList::iterator oldCallback = m_properties.find(ident); + if (oldCallback != m_properties.end()) { + delete oldCallback->second; + if (!callback) { + m_properties.remove(oldCallback); + return; + } + } + + m_properties.set(ident, callback); +} + +bool CppBoundClass::isMethodRegistered(const string& name) const +{ + NPIdentifier ident = WebBindings::getStringIdentifier(name.c_str()); + MethodList::const_iterator callback = m_methods.find(ident); + return callback != m_methods.end(); +} + +CppVariant* CppBoundClass::getAsCppVariant() +{ + if (!m_selfVariant.isObject()) { + // Create an NPObject using our static NPClass. The first argument (a + // plugin's instance handle) is passed through to the allocate function + // directly, and we don't use it, so it's ok to be 0. + NPObject* npObj = WebBindings::createObject(0, &CppNPObject::npClass); + CppNPObject* obj = reinterpret_cast(npObj); + obj->boundClass = this; + m_selfVariant.set(npObj); + WebBindings::releaseObject(npObj); // CppVariant takes the reference. + } + ASSERT(m_selfVariant.isObject()); + return &m_selfVariant; +} + +void CppBoundClass::bindToJavascript(WebFrame* frame, const WebString& classname) +{ + // BindToWindowObject will take its own reference to the NPObject, and clean + // up after itself. It will also (indirectly) register the object with V8, + // so we must remember this so we can unregister it when we're destroyed. + frame->bindToWindowObject(classname, NPVARIANT_TO_OBJECT(*getAsCppVariant())); + m_boundToFrame = true; +} diff --git a/WebKitTools/DumpRenderTree/chromium/CppBoundClass.h b/WebKitTools/DumpRenderTree/chromium/CppBoundClass.h new file mode 100644 index 0000000..6cb638e --- /dev/null +++ b/WebKitTools/DumpRenderTree/chromium/CppBoundClass.h @@ -0,0 +1,241 @@ +/* + * Copyright (C) 2010 Google Inc. All rights reserved. + * Copyright (C) 2009 Pawel Hajdan (phajdan.jr@chromium.org) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + CppBoundClass class: + This base class serves as a parent for C++ classes designed to be bound to + JavaScript objects. + + Subclasses should define the constructor to build the property and method + lists needed to bind this class to a JS object. They should also declare + and define member variables and methods to be exposed to JS through + that object. +*/ + +#ifndef CppBoundClass_h +#define CppBoundClass_h + +#include "CppVariant.h" +#include +#include +#include +#include + +namespace WebKit { +class WebFrame; +class WebString; +} + +typedef Vector CppArgumentList; + +// CppBoundClass lets you map Javascript method calls and property accesses +// directly to C++ method calls and CppVariant* variable access. +class CppBoundClass : public Noncopyable { +public: + class PropertyCallback { + public: + virtual ~PropertyCallback() { } + + // Sets |value| to the value of the property. Returns false in case of + // failure. |value| is always non-0. + virtual bool getValue(CppVariant* result) = 0; + + // sets the property value to |value|. Returns false in case of failure. + virtual bool setValue(const CppVariant&) = 0; + }; + + // Callback class for "void function(CppVariant*)" + class GetterCallback { + public: + virtual ~GetterCallback() {} + virtual void run(CppVariant*) = 0; + }; + + // The constructor should call BindMethod, BindProperty, and + // SetFallbackMethod as needed to set up the methods, properties, and + // fallback method. + CppBoundClass() : m_boundToFrame(false) {} + virtual ~CppBoundClass(); + + // Return a CppVariant representing this class, for use with BindProperty(). + // The variant type is guaranteed to be NPVariantType_Object. + CppVariant* getAsCppVariant(); + + // Given a WebFrame, BindToJavascript builds the NPObject that will represent + // the class and binds it to the frame's window under the given name. This + // should generally be called from the WebView delegate's + // WindowObjectCleared(). A class so bound will be accessible to JavaScript + // as window.. The owner of the CppBoundObject is responsible for + // keeping the object around while the frame is alive, and for destroying it + // afterwards. + void bindToJavascript(WebKit::WebFrame*, const WebKit::WebString& classname); + + // Used by a test. Returns true if a method with name |name| exists, + // regardless of whether a fallback is registered. + bool isMethodRegistered(const std::string&) const; + +protected: + // Callback for "void function(const CppArguemntList&, CppVariant*)" + class Callback { + public: + virtual ~Callback() {} + virtual void run(const CppArgumentList&, CppVariant*) = 0; + }; + + // Callback for "void T::method(const CppArguemntList&, CppVariant*)" + template class MemberCallback : public Callback { + public: + typedef void (T::*MethodType)(const CppArgumentList&, CppVariant*); + MemberCallback(T* object, MethodType method) + : m_object(object) + , m_method(method) {} + virtual ~MemberCallback() {} + + virtual void run(const CppArgumentList& arguments, CppVariant* result) + { + (m_object->*m_method)(arguments, result); + } + private: + T* m_object; + MethodType m_method; + }; + + // Callback class for "void T::method(CppVariant*)" + template class MemberGetterCallback : public GetterCallback { + public: + typedef void (T::*MethodType)(CppVariant*); + MemberGetterCallback(T* object, MethodType method) + : m_object(object) + , m_method(method) {} + virtual ~MemberGetterCallback() {} + + virtual void run(CppVariant* result) { (m_object->*m_method)(result); } + private: + T* m_object; + MethodType m_method; + }; + + // Bind the Javascript method called the string parameter to the C++ method. + void bindCallback(const std::string&, Callback*); + + // A wrapper for bindCallback, to simplify the common case of binding a + // method on the current object. Though not verified here, |method| + // must be a method of this CppBoundClass subclass. + template + void bindMethod(const std::string& name, void (T::*method)(const CppArgumentList&, CppVariant*)) + { + Callback* callback = new MemberCallback(static_cast(this), method); + bindCallback(name, callback); + } + + // Bind Javascript property |name| to the C++ getter callback |callback|. + // This can be used to create read-only properties. + void bindGetterCallback(const std::string&, GetterCallback*); + + // A wrapper for BindGetterCallback, to simplify the common case of binding a + // property on the current object. Though not verified here, |method| + // must be a method of this CppBoundClass subclass. + template + void bindProperty(const std::string& name, void (T::*method)(CppVariant*)) + { + GetterCallback* callback = new MemberGetterCallback(static_cast(this), method); + bindGetterCallback(name, callback); + } + + // Bind the Javascript property called |name| to a CppVariant. + void bindProperty(const std::string&, CppVariant*); + + // Bind Javascript property called |name| to a PropertyCallback. + // CppBoundClass assumes control over the life time of the callback. + void bindProperty(const std::string&, PropertyCallback*); + + // Set the fallback callback, which is called when when a callback is + // invoked that isn't bound. + // If it is 0 (its default value), a JavaScript exception is thrown in + // that case (as normally expected). If non 0, the fallback method is + // invoked and the script continues its execution. + // Passing 0 clears out any existing binding. + // It is used for tests and should probably only be used in such cases + // as it may cause unexpected behaviors (a JavaScript object with a + // fallback always returns true when checked for a method's + // existence). + void bindFallbackCallback(Callback* fallbackCallback) + { + m_fallbackCallback.set(fallbackCallback); + } + + // A wrapper for BindFallbackCallback, to simplify the common case of + // binding a method on the current object. Though not verified here, + // |method| must be a method of this CppBoundClass subclass. + // Passing 0 for |method| clears out any existing binding. + template + void bindFallbackMethod(void (T::*method)(const CppArgumentList&, CppVariant*)) + { + if (method) { + Callback* callback = new MemberCallback(static_cast(this), method); + bindFallbackCallback(callback); + } else + bindFallbackCallback(0); + } + + // Some fields are protected because some tests depend on accessing them, + // but otherwise they should be considered private. + + typedef HashMap PropertyList; + typedef HashMap MethodList; + // These maps associate names with property and method pointers to be + // exposed to JavaScript. + PropertyList m_properties; + MethodList m_methods; + + // The callback gets invoked when a call is made to an nonexistent method. + OwnPtr m_fallbackCallback; + +private: + // NPObject callbacks. + friend struct CppNPObject; + bool hasMethod(NPIdentifier) const; + bool invoke(NPIdentifier, const NPVariant* args, size_t argCount, + NPVariant* result); + bool hasProperty(NPIdentifier) const; + bool getProperty(NPIdentifier, NPVariant* result) const; + bool setProperty(NPIdentifier, const NPVariant*); + + // A lazily-initialized CppVariant representing this class. We retain 1 + // reference to this object, and it is released on deletion. + CppVariant m_selfVariant; + + // True if our np_object has been bound to a WebFrame, in which case it must + // be unregistered with V8 when we delete it. + bool m_boundToFrame; +}; + +#endif // CppBoundClass_h diff --git a/WebKitTools/DumpRenderTree/chromium/CppVariant.cpp b/WebKitTools/DumpRenderTree/chromium/CppVariant.cpp new file mode 100644 index 0000000..9539907 --- /dev/null +++ b/WebKitTools/DumpRenderTree/chromium/CppVariant.cpp @@ -0,0 +1,310 @@ +/* + * Copyright (C) 2010 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "CppVariant.h" + +#include "public/WebBindings.h" +#include +#include +#include + +using namespace WebKit; +using namespace std; + +CppVariant::CppVariant() +{ + type = NPVariantType_Null; +} + +// Note that Set() performs a deep copy, which is necessary to safely +// call FreeData() on the value in the destructor. +CppVariant::CppVariant(const CppVariant& original) +{ + type = NPVariantType_Null; + set(original); +} + +// See comment for copy constructor, above. +CppVariant& CppVariant::operator=(const CppVariant& original) +{ + if (&original != this) + set(original); + return *this; +} + +CppVariant::~CppVariant() +{ + freeData(); +} + +void CppVariant::freeData() +{ + WebBindings::releaseVariantValue(this); +} + +bool CppVariant::isEqual(const CppVariant& other) const +{ + if (type != other.type) + return false; + + switch (type) { + case NPVariantType_Bool: + return (value.boolValue == other.value.boolValue); + case NPVariantType_Int32: + return (value.intValue == other.value.intValue); + case NPVariantType_Double: + return (value.doubleValue == other.value.doubleValue); + case NPVariantType_String: { + const NPString *this_value = &value.stringValue; + const NPString *other_value = &other.value.stringValue; + uint32_t len = this_value->UTF8Length; + return len == other_value->UTF8Length + && !strncmp(this_value->UTF8Characters, + other_value->UTF8Characters, len); + } + case NPVariantType_Null: + case NPVariantType_Void: + return true; + case NPVariantType_Object: { + NPObject* thisValue = value.objectValue; + NPObject* otherValue = other.value.objectValue; + return thisValue->_class == otherValue->_class + && thisValue->referenceCount == otherValue->referenceCount; + } + } + return false; +} + +void CppVariant::copyToNPVariant(NPVariant* result) const +{ + result->type = type; + switch (type) { + case NPVariantType_Bool: + result->value.boolValue = value.boolValue; + break; + case NPVariantType_Int32: + result->value.intValue = value.intValue; + break; + case NPVariantType_Double: + result->value.doubleValue = value.doubleValue; + break; + case NPVariantType_String: + WebBindings::initializeVariantWithStringCopy(result, &value.stringValue); + break; + case NPVariantType_Null: + case NPVariantType_Void: + // Nothing to set. + break; + case NPVariantType_Object: + result->type = NPVariantType_Object; + result->value.objectValue = WebBindings::retainObject(value.objectValue); + break; + } +} + +void CppVariant::set(const NPVariant& newValue) +{ + freeData(); + switch (newValue.type) { + case NPVariantType_Bool: + set(newValue.value.boolValue); + break; + case NPVariantType_Int32: + set(newValue.value.intValue); + break; + case NPVariantType_Double: + set(newValue.value.doubleValue); + break; + case NPVariantType_String: + set(newValue.value.stringValue); + break; + case NPVariantType_Null: + case NPVariantType_Void: + type = newValue.type; + break; + case NPVariantType_Object: + set(newValue.value.objectValue); + break; + } +} + +void CppVariant::setNull() +{ + freeData(); + type = NPVariantType_Null; +} + +void CppVariant::set(bool newValue) +{ + freeData(); + type = NPVariantType_Bool; + value.boolValue = newValue; +} + +void CppVariant::set(int32_t newValue) +{ + freeData(); + type = NPVariantType_Int32; + value.intValue = newValue; +} + +void CppVariant::set(double newValue) +{ + freeData(); + type = NPVariantType_Double; + value.doubleValue = newValue; +} + +// The newValue must be a null-terminated string. +void CppVariant::set(const char* newValue) +{ + freeData(); + type = NPVariantType_String; + NPString newString = {newValue, + static_cast(strlen(newValue))}; + WebBindings::initializeVariantWithStringCopy(this, &newString); +} + +void CppVariant::set(const string& newValue) +{ + freeData(); + type = NPVariantType_String; + NPString newString = {newValue.data(), + static_cast(newValue.size())}; + WebBindings::initializeVariantWithStringCopy(this, &newString); +} + +void CppVariant::set(const NPString& newValue) +{ + freeData(); + type = NPVariantType_String; + WebBindings::initializeVariantWithStringCopy(this, &newValue); +} + +void CppVariant::set(NPObject* newValue) +{ + freeData(); + type = NPVariantType_Object; + value.objectValue = WebBindings::retainObject(newValue); +} + +string CppVariant::toString() const +{ + ASSERT(isString()); + return string(value.stringValue.UTF8Characters, + value.stringValue.UTF8Length); +} + +int32_t CppVariant::toInt32() const +{ + if (isInt32()) + return value.intValue; + if (isDouble()) + return static_cast(value.doubleValue); + ASSERT_NOT_REACHED(); + return 0; +} + +double CppVariant::toDouble() const +{ + if (isInt32()) + return static_cast(value.intValue); + if (isDouble()) + return value.doubleValue; + ASSERT_NOT_REACHED(); + return 0; +} + +bool CppVariant::toBoolean() const +{ + ASSERT(isBool()); + return value.boolValue; +} + +Vector CppVariant::toStringVector() const +{ + + ASSERT(isObject()); + Vector stringVector; + NPObject* npValue = value.objectValue; + NPIdentifier lengthId = WebBindings::getStringIdentifier("length"); + + if (!WebBindings::hasProperty(0, npValue, lengthId)) + return stringVector; + + NPVariant lengthValue; + if (!WebBindings::getProperty(0, npValue, lengthId, &lengthValue)) + return stringVector; + + int length = 0; + // The length is a double in some cases. + if (NPVARIANT_IS_DOUBLE(lengthValue)) + length = static_cast(NPVARIANT_TO_DOUBLE(lengthValue)); + else if (NPVARIANT_IS_INT32(lengthValue)) + length = NPVARIANT_TO_INT32(lengthValue); + WebBindings::releaseVariantValue(&lengthValue); + + // For sanity, only allow 100 items. + length = min(100, length); + for (int i = 0; i < length; ++i) { + // Get each of the items. + char indexInChar[20]; // Enough size to store 32-bit integer + snprintf(indexInChar, 20, "%d", i); + string index(indexInChar); + NPIdentifier indexId = WebBindings::getStringIdentifier(index.c_str()); + if (!WebBindings::hasProperty(0, npValue, indexId)) + continue; + NPVariant indexValue; + if (!WebBindings::getProperty(0, npValue, indexId, &indexValue)) + continue; + if (NPVARIANT_IS_STRING(indexValue)) { + string item(NPVARIANT_TO_STRING(indexValue).UTF8Characters, + NPVARIANT_TO_STRING(indexValue).UTF8Length); + stringVector.append(item); + } + WebBindings::releaseVariantValue(&indexValue); + } + return stringVector; +} + +bool CppVariant::invoke(const string& method, const CppVariant* arguments, + uint32_t argumentCount, CppVariant& result) const +{ + ASSERT(isObject()); + NPIdentifier methodName = WebBindings::getStringIdentifier(method.c_str()); + NPObject* npObject = value.objectValue; + if (!WebBindings::hasMethod(0, npObject, methodName)) + return false; + NPVariant r; + bool status = WebBindings::invoke(0, npObject, methodName, arguments, argumentCount, &r); + result.set(r); + return status; +} diff --git a/WebKitTools/DumpRenderTree/chromium/CppVariant.h b/WebKitTools/DumpRenderTree/chromium/CppVariant.h new file mode 100644 index 0000000..d34a163 --- /dev/null +++ b/WebKitTools/DumpRenderTree/chromium/CppVariant.h @@ -0,0 +1,133 @@ +/* + * Copyright (C) 2010 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + This file contains the declaration for CppVariant, a type used by C++ classes + that are to be bound to JavaScript objects. + + CppVariant exists primarily as an interface between C++ callers and the + corresponding NPVariant type. CppVariant also provides a number of + convenience constructors and accessors, so that the NPVariantType values + don't need to be exposed, and a destructor to free any memory allocated for + string values. +*/ + +#ifndef CppVariant_h +#define CppVariant_h + +#include "base/basictypes.h" +#include "public/WebBindings.h" +#include +#include + +class CppVariant : public NPVariant { +public: + CppVariant(); + ~CppVariant(); + void setNull(); + void set(bool); + void set(int32_t); + void set(double); + + // Note that setting a CppVariant to a string value involves copying the + // string data, which must be freed with a call to freeData() when the + // CppVariant is set to a different value or is no longer needed. Normally + // this is handled by the other set() methods and by the destructor. + void set(const char*); // Must be a null-terminated string. + void set(const std::string&); + void set(const NPString&); + void set(const NPVariant&); + + // Note that setting a CppVariant to an NPObject involves ref-counting + // the actual object. freeData() should only be called if the CppVariant + // is no longer needed. The other set() methods handle this internally. + // Also, the object's NPClass is expected to be a static object: neither + // the NP runtime nor CppVariant will ever free it. + void set(NPObject*_value); + + // These three methods all perform deep copies of any string data. This + // allows local CppVariants to be released by the destructor without + // corrupting their sources. In performance-critical code, or when strings + // are very long, avoid creating new CppVariants. + // In case of NPObject as the data, the copying involves ref-counting + // as opposed to deep-copying. The ref-counting ensures that sources don't + // get corrupted when the copies get destroyed. + void copyToNPVariant(NPVariant* result) const; + CppVariant& operator=(const CppVariant& original); + CppVariant(const CppVariant& original); + + // Calls NPN_ReleaseVariantValue, which frees any string data + // held by the object and sets its type to null. + // In case of NPObject, the NPN_ReleaseVariantValue decrements + // the ref-count (releases when ref-count becomes 0) + void freeData(); + + // Compares this CppVariant's type and value to another's. They must be + // identical in both type and value to be considered equal. For string and + // object types, a deep comparison is performed; that is, the contents of the + // strings, or the classes and refcounts of the objects, must be the same, + // but they need not be the same pointers. + bool isEqual(const CppVariant&) const; + + // The value of a CppVariant may be read directly from its NPVariant (but + // should only be set using one of the set() methods above). Although the + // type of a CppVariant is likewise public, it can be accessed through these + // functions rather than directly if a caller wishes to avoid dependence on + // the NPVariantType values. + bool isBool() const { return (type == NPVariantType_Bool); } + bool isInt32() const { return (type == NPVariantType_Int32); } + bool isDouble() const { return (type == NPVariantType_Double); } + bool isNumber() const { return (isInt32() || isDouble()); } + bool isString() const { return (type == NPVariantType_String); } + bool isVoid() const { return (type == NPVariantType_Void); } + bool isNull() const { return (type == NPVariantType_Null); } + bool isEmpty() const { return (isVoid() || isNull()); } + bool isObject() const { return (type == NPVariantType_Object); } + + // Converters. The CppVariant must be of a type convertible to these values. + // For example, toInt32() works only if isNumber() is true. + std::string toString() const; + int32_t toInt32() const; + double toDouble() const; + bool toBoolean() const; + // Returns a vector of strings for the specified argument. This is useful + // for converting a JavaScript array of strings into a vector of strings. + Vector toStringVector() const; + + // Invoke method of the given name on an object with the supplied arguments. + // The first argument should be the object on which the method is to be + // invoked. Returns whether the method was successfully invoked. If the + // method was invoked successfully, any return value is stored in the + // CppVariant specified by result. + bool invoke(const std::string&, const CppVariant* arguments, + uint32_t argumentCount, CppVariant& result) const; +}; + +#endif // CppVariant_h diff --git a/WebKitTools/DumpRenderTree/chromium/DumpRenderTree.cpp b/WebKitTools/DumpRenderTree/chromium/DumpRenderTree.cpp new file mode 100644 index 0000000..c81480f --- /dev/null +++ b/WebKitTools/DumpRenderTree/chromium/DumpRenderTree.cpp @@ -0,0 +1,119 @@ +/* + * Copyright (C) 2010 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" + +#include "TestShell.h" +#include "webkit/support/webkit_support.h" +#include +#if OS(MAC_OS_X) +#include "WebSystemInterface.h" +#endif + +using namespace std; + +static const char optionComplexText[] = "--complex-text"; +static const char optionDumpAllPixels[] = "--dump-all-pixels"; +static const char optionNotree[] = "--notree"; +static const char optionPixelTests[] = "--pixel-tests"; +static const char optionThreaded[] = "--threaded"; +static const char optionTree[] = "--tree"; + +static void runTest(TestShell& shell, TestParams& params, const string& testName) +{ + string pathOrURL = testName; + string::size_type separatorPosition = pathOrURL.find("'"); + if (separatorPosition != string::npos) { + params.pixelHash = pathOrURL.substr(separatorPosition + 1); + pathOrURL.erase(separatorPosition); + } + params.testUrl = webkit_support::CreateURLForPathOrURL(pathOrURL); + shell.resetTestController(); + shell.runFileTest(params); +} + +int main(int argc, char* argv[]) +{ +#if OS(MAC_OS_X) + // Need to call before instantiate WebKitClient. + InitWebCoreSystemInterface(); +#endif + webkit_support::SetUpTestEnvironment(); + + TestParams params; + Vector tests; + bool serverMode = false; + for (int i = 1; i < argc; ++i) { + string argument(argv[i]); + if (argument == "-") + serverMode = true; + else if (argument == optionNotree) + params.dumpTree = false; + else if (argument == optionPixelTests) + params.dumpPixels = true; + else if (argument.size() && argument[0] == '-') + fprintf(stderr, "Unknown option: %s\n", argv[i]); + else + tests.append(argument); + } + + { // Explicit scope for the TestShell instance. + TestShell shell; + if (serverMode && !tests.size()) { + params.printSeparators = true; + char testString[2048]; // 2048 is the same as the sizes of other platforms. + while (fgets(testString, sizeof(testString), stdin)) { + char* newLinePosition = strchr(testString, '\n'); + if (newLinePosition) + *newLinePosition = '\0'; + if (testString[0] == '\0') + continue; + runTest(shell, params, testString); + } + } else if (!tests.size()) + printf("#EOF\n"); + else { + params.printSeparators = tests.size() > 1; + for (unsigned i = 0; i < tests.size(); i++) + runTest(shell, params, tests[i]); + } + + shell.callJSGC(); + shell.callJSGC(); + + // When we finish the last test, cleanup the LayoutTestController. + // It may have references to not-yet-cleaned up windows. By + // cleaning up here we help purify reports. + shell.resetTestController(); + } + + webkit_support::TearDownTestEnvironment(); + return EXIT_SUCCESS; +} diff --git a/WebKitTools/DumpRenderTree/chromium/EventSender.cpp b/WebKitTools/DumpRenderTree/chromium/EventSender.cpp new file mode 100644 index 0000000..c48aaf4 --- /dev/null +++ b/WebKitTools/DumpRenderTree/chromium/EventSender.cpp @@ -0,0 +1,807 @@ +/* + * Copyright (C) 2010 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +// This file contains the definition for EventSender. +// +// Some notes about drag and drop handling: +// Windows drag and drop goes through a system call to doDragDrop. At that +// point, program control is given to Windows which then periodically makes +// callbacks into the webview. This won't work for layout tests, so instead, +// we queue up all the mouse move and mouse up events. When the test tries to +// start a drag (by calling EvenSendingController::doDragDrop), we take the +// events in the queue and replay them. +// The behavior of queuing events and replaying them can be disabled by a +// layout test by setting eventSender.dragMode to false. + +#include "config.h" +#include "EventSender.h" + +#include "TestShell.h" +#include "base/keyboard_codes.h" +#include "base/time.h" +#include "public/WebDragData.h" +#include "public/WebDragOperation.h" +#include "public/WebPoint.h" +#include "public/WebString.h" +#include "public/WebView.h" +#include "webkit/support/webkit_support.h" +#include +#include + +#if OS(WINDOWS) +#include "public/win/WebInputEventFactory.h" +#endif + +// FIXME: layout before each event? + +using namespace base; +using namespace std; +using namespace WebKit; + +TestShell* EventSender::testShell = 0; +WebPoint EventSender::lastMousePos; +WebMouseEvent::Button EventSender::pressedButton = WebMouseEvent::ButtonNone; +WebMouseEvent::Button EventSender::lastButtonType = WebMouseEvent::ButtonNone; + +struct SavedEvent { + enum SavedEventType { + Unspecified, + MouseUp, + MouseMove, + LeapForward + }; + + SavedEventType type; + WebMouseEvent::Button buttonType; // For MouseUp. + WebPoint pos; // For MouseMove. + int milliseconds; // For LeapForward. + + SavedEvent() + : type(Unspecified) + , buttonType(WebMouseEvent::ButtonNone) + , milliseconds(0) {} +}; + +static WebDragData currentDragData; +static WebDragOperation currentDragEffect; +static WebDragOperationsMask currentDragEffectsAllowed; +static bool replayingSavedEvents = false; +static Deque mouseEventQueue; + +// Time and place of the last mouse up event. +static double lastClickTimeSec = 0; +static WebPoint lastClickPos; +static int clickCount = 0; + +// maximum distance (in space and time) for a mouse click +// to register as a double or triple click +static const double multipleClickTimeSec = 1; +static const int multipleClickRadiusPixels = 5; + +// How much we should scroll per event - the value here is chosen to +// match the WebKit impl and layout test results. +static const float scrollbarPixelsPerTick = 40.0f; + +inline bool outsideMultiClickRadius(const WebPoint& a, const WebPoint& b) +{ + return ((a.x - b.x) * (a.x - b.x) + (a.y - b.y) * (a.y - b.y)) > + multipleClickRadiusPixels * multipleClickRadiusPixels; +} + +// Used to offset the time the event hander things an event happened. This is +// done so tests can run without a delay, but bypass checks that are time +// dependent (e.g., dragging has a timeout vs selection). +static uint32 timeOffsetMs = 0; + +static double getCurrentEventTimeSec() +{ + return (TimeTicks::Now().ToInternalValue() / Time::kMicrosecondsPerMillisecond + timeOffsetMs) / 1000.0; +} + +static void advanceEventTime(int32_t deltaMs) +{ + timeOffsetMs += deltaMs; +} + +static void initMouseEvent(WebInputEvent::Type t, WebMouseEvent::Button b, + const gfx::Point& pos, WebMouseEvent* e) +{ + e->type = t; + e->button = b; + e->modifiers = 0; + e->x = pos.x(); + e->y = pos.y(); + e->globalX = pos.x(); + e->globalY = pos.y(); + e->timeStampSeconds = getCurrentEventTimeSec(); + e->clickCount = clickCount; +} + +// Returns true if the specified key is the system key. +static bool applyKeyModifier(const string& modifierName, WebInputEvent* event) +{ + bool isSystemKey = false; + const char* characters = modifierName.c_str(); + if (!strcmp(characters, "ctrlKey") +#if !OS(MAC_OS_X) + || !strcmp(characters, "addSelectionKey") +#endif + ) { + event->modifiers |= WebInputEvent::ControlKey; + } else if (!strcmp(characters, "shiftKey") || !strcmp(characters, "rangeSelectionKey")) + event->modifiers |= WebInputEvent::ShiftKey; + else if (!strcmp(characters, "altKey")) { + event->modifiers |= WebInputEvent::AltKey; +#if !OS(MAC_OS_X) + // On Windows all keys with Alt modifier will be marked as system key. + // We keep the same behavior on Linux, see: + // WebKit/chromium/src/gtk/WebInputEventFactory.cpp + // If we want to change this behavior on Linux, this piece of code must be + // kept in sync with the related code in above file. + isSystemKey = true; +#endif +#if OS(MAC_OS_X) + } else if (!strcmp(characters, "metaKey") || !strcmp(characters, "addSelectionKey")) { + event->modifiers |= WebInputEvent::MetaKey; + // On Mac only command key presses are marked as system key. + // See the related code in: WebKit/chromium/src/mac/WebInputEventFactory.cpp + // It must be kept in sync with the related code in above file. + isSystemKey = true; +#else + } else if (!strcmp(characters, "metaKey")) { + event->modifiers |= WebInputEvent::MetaKey; +#endif + } + return isSystemKey; +} + +static bool applyKeyModifiers(const CppVariant* argument, WebInputEvent* event) +{ + bool isSystemKey = false; + if (argument->isObject()) { + Vector modifiers = argument->toStringVector(); + for (Vector::const_iterator i = modifiers.begin(); i != modifiers.end(); ++i) + isSystemKey |= applyKeyModifier(*i, event); + } else if (argument->isString()) + isSystemKey = applyKeyModifier(argument->toString(), event); + return isSystemKey; +} + +// Get the edit command corresponding to a keyboard event. +// Returns true if the specified event corresponds to an edit command, the name +// of the edit command will be stored in |*name|. +bool getEditCommand(const WebKeyboardEvent& event, string* name) +{ +#if OS(MAC_OS_X) + // We only cares about Left,Right,Up,Down keys with Command or Command+Shift + // modifiers. These key events correspond to some special movement and + // selection editor commands, and was supposed to be handled in + // WebKit/chromium/src/EditorClientImpl.cpp. But these keys will be marked + // as system key, which prevents them from being handled. Thus they must be + // handled specially. + if ((event.modifiers & ~WebKeyboardEvent::ShiftKey) != WebKeyboardEvent::MetaKey) + return false; + + switch (event.windowsKeyCode) { + case base::VKEY_LEFT: + *name = "MoveToBeginningOfLine"; + break; + case base::VKEY_RIGHT: + *name = "MoveToEndOfLine"; + break; + case base::VKEY_UP: + *name = "MoveToBeginningOfDocument"; + break; + case base::VKEY_DOWN: + *name = "MoveToEndOfDocument"; + break; + default: + return false; + } + + if (event.modifiers & WebKeyboardEvent::ShiftKey) + name->append("AndModifySelection"); + + return true; +#else + return false; +#endif +} + +// Key event location code introduced in DOM Level 3. +// See also: http://www.w3.org/TR/DOM-Level-3-Events/#events-keyboardevents +enum KeyLocationCode { + DOMKeyLocationStandard = 0x00, + DOMKeyLocationLeft = 0x01, + DOMKeyLocationRight = 0x02, + DOMKeyLocationNumpad = 0x03 +}; + +EventSender::EventSender(TestShell* shell) + : m_methodFactory(this) +{ + // Set static testShell variable since we can't do it in an initializer list. + // We also need to be careful not to assign testShell to new windows which are + // temporary. + if (!testShell) + testShell = shell; + + // Initialize the map that associates methods of this class with the names + // they will use when called by JavaScript. The actual binding of those + // names to their methods will be done by calling bindToJavaScript() (defined + // by CppBoundClass, the parent to EventSender). + bindMethod("mouseDown", &EventSender::mouseDown); + bindMethod("mouseUp", &EventSender::mouseUp); + bindMethod("contextClick", &EventSender::contextClick); + bindMethod("mouseMoveTo", &EventSender::mouseMoveTo); + bindMethod("mouseWheelTo", &EventSender::mouseWheelTo); + bindMethod("leapForward", &EventSender::leapForward); + bindMethod("keyDown", &EventSender::keyDown); + bindMethod("dispatchMessage", &EventSender::dispatchMessage); + bindMethod("enableDOMUIEventLogging", &EventSender::enableDOMUIEventLogging); + bindMethod("fireKeyboardEventsToElement", &EventSender::fireKeyboardEventsToElement); + bindMethod("clearKillRing", &EventSender::clearKillRing); + bindMethod("textZoomIn", &EventSender::textZoomIn); + bindMethod("textZoomOut", &EventSender::textZoomOut); + bindMethod("zoomPageIn", &EventSender::zoomPageIn); + bindMethod("zoomPageOut", &EventSender::zoomPageOut); + bindMethod("scheduleAsynchronousClick", &EventSender::scheduleAsynchronousClick); + bindMethod("beginDragWithFiles", &EventSender::beginDragWithFiles); + + // When set to true (the default value), we batch mouse move and mouse up + // events so we can simulate drag & drop. + bindProperty("dragMode", &dragMode); +#if OS(WINDOWS) + bindProperty("WM_KEYDOWN", &wmKeyDown); + bindProperty("WM_KEYUP", &wmKeyUp); + bindProperty("WM_CHAR", &wmChar); + bindProperty("WM_DEADCHAR", &wmDeadChar); + bindProperty("WM_SYSKEYDOWN", &wmSysKeyDown); + bindProperty("WM_SYSKEYUP", &wmSysKeyUp); + bindProperty("WM_SYSCHAR", &wmSysChar); + bindProperty("WM_SYSDEADCHAR", &wmSysDeadChar); +#endif +} + +void EventSender::reset() +{ + // The test should have finished a drag and the mouse button state. + ASSERT(currentDragData.isNull()); + currentDragData.reset(); + currentDragEffect = WebKit::WebDragOperationNone; + currentDragEffectsAllowed = WebKit::WebDragOperationNone; + pressedButton = WebMouseEvent::ButtonNone; + dragMode.set(true); +#if OS(WINDOWS) + wmKeyDown.set(WM_KEYDOWN); + wmKeyUp.set(WM_KEYUP); + wmChar.set(WM_CHAR); + wmDeadChar.set(WM_DEADCHAR); + wmSysKeyDown.set(WM_SYSKEYDOWN); + wmSysKeyUp.set(WM_SYSKEYUP); + wmSysChar.set(WM_SYSCHAR); + wmSysDeadChar.set(WM_SYSDEADCHAR); +#endif + lastMousePos = WebPoint(0, 0); + lastClickTimeSec = 0; + lastClickPos = WebPoint(0, 0); + clickCount = 0; + lastButtonType = WebMouseEvent::ButtonNone; + timeOffsetMs = 0; +} + +WebView* EventSender::webview() +{ + return testShell->webView(); +} + +void EventSender::doDragDrop(const WebKit::WebPoint& eventPos, + const WebDragData& dragData, + WebDragOperationsMask mask) +{ + WebMouseEvent event; + initMouseEvent(WebInputEvent::MouseDown, pressedButton, lastMousePos, &event); + WebPoint clientPoint(event.x, event.y); + WebPoint screenPoint(event.globalX, event.globalY); + currentDragData = dragData; + currentDragEffectsAllowed = mask; + currentDragEffect = webview()->dragTargetDragEnter(dragData, 0, clientPoint, screenPoint, currentDragEffectsAllowed); + + // Finish processing events. + replaySavedEvents(); +} + +WebMouseEvent::Button EventSender::getButtonTypeFromButtonNumber(int buttonCode) +{ + if (!buttonCode) + return WebMouseEvent::ButtonLeft; + if (buttonCode == 2) + return WebMouseEvent::ButtonRight; + return WebMouseEvent::ButtonMiddle; +} + +int EventSender::getButtonNumberFromSingleArg(const CppArgumentList& arguments) +{ + int buttonCode = 0; + if (arguments.size() > 0 && arguments[0].isNumber()) + buttonCode = arguments[0].toInt32(); + return buttonCode; +} + +void EventSender::updateClickCountForButton(WebMouseEvent::Button buttonType) +{ + if ((getCurrentEventTimeSec() - lastClickTimeSec < multipleClickTimeSec) + && (!outsideMultiClickRadius(lastMousePos, lastClickPos)) + && (buttonType == lastButtonType)) + ++clickCount; + else { + clickCount = 1; + lastButtonType = buttonType; + } +} + +// +// Implemented javascript methods. +// + +void EventSender::mouseDown(const CppArgumentList& arguments, CppVariant* result) +{ + if (result) // Could be 0 if invoked asynchronously. + result->setNull(); + + webview()->layout(); + + int buttonNumber = getButtonNumberFromSingleArg(arguments); + ASSERT(buttonNumber != -1); + + WebMouseEvent::Button buttonType = getButtonTypeFromButtonNumber(buttonNumber); + + updateClickCountForButton(buttonType); + + WebMouseEvent event; + pressedButton = buttonType; + initMouseEvent(WebInputEvent::MouseDown, buttonType, lastMousePos, &event); + if (arguments.size() >= 2 && (arguments[1].isObject() || arguments[1].isString())) + applyKeyModifiers(&(arguments[1]), &event); + webview()->handleInputEvent(event); +} + +void EventSender::mouseUp(const CppArgumentList& arguments, CppVariant* result) +{ + if (result) // Could be 0 if invoked asynchronously. + result->setNull(); + + webview()->layout(); + + int buttonNumber = getButtonNumberFromSingleArg(arguments); + ASSERT(buttonNumber != -1); + + WebMouseEvent::Button buttonType = getButtonTypeFromButtonNumber(buttonNumber); + + if (isDragMode() && !replayingSavedEvents) { + SavedEvent savedEvent; + savedEvent.type = SavedEvent::MouseUp; + savedEvent.buttonType = buttonType; + mouseEventQueue.append(savedEvent); + replaySavedEvents(); + } else { + WebMouseEvent event; + initMouseEvent(WebInputEvent::MouseUp, buttonType, lastMousePos, &event); + if (arguments.size() >= 2 && (arguments[1].isObject() || arguments[1].isString())) + applyKeyModifiers(&(arguments[1]), &event); + doMouseUp(event); + } +} + +void EventSender::doMouseUp(const WebMouseEvent& e) +{ + webview()->handleInputEvent(e); + + pressedButton = WebMouseEvent::ButtonNone; + lastClickTimeSec = e.timeStampSeconds; + lastClickPos = lastMousePos; + + // If we're in a drag operation, complete it. + if (currentDragData.isNull()) + return; + WebPoint clientPoint(e.x, e.y); + WebPoint screenPoint(e.globalX, e.globalY); + + currentDragEffect = webview()->dragTargetDragOver(clientPoint, screenPoint, currentDragEffectsAllowed); + if (currentDragEffect) + webview()->dragTargetDrop(clientPoint, screenPoint); + else + webview()->dragTargetDragLeave(); + webview()->dragSourceEndedAt(clientPoint, screenPoint, currentDragEffect); + webview()->dragSourceSystemDragEnded(); + + currentDragData.reset(); +} + +void EventSender::mouseMoveTo(const CppArgumentList& arguments, CppVariant* result) +{ + result->setNull(); + + if (arguments.size() < 2 || !arguments[0].isNumber() || !arguments[1].isNumber()) + return; + webview()->layout(); + + WebPoint mousePos(arguments[0].toInt32(), arguments[1].toInt32()); + + if (isDragMode() && pressedButton == WebMouseEvent::ButtonLeft && !replayingSavedEvents) { + SavedEvent savedEvent; + savedEvent.type = SavedEvent::MouseMove; + savedEvent.pos = mousePos; + mouseEventQueue.append(savedEvent); + } else { + WebMouseEvent event; + initMouseEvent(WebInputEvent::MouseMove, pressedButton, mousePos, &event); + doMouseMove(event); + } +} + +void EventSender::mouseWheelTo(const CppArgumentList& arguments, CppVariant* result) +{ + result->setNull(); + + if (arguments.size() < 2 || !arguments[0].isNumber() || !arguments[1].isNumber()) + return; + + // Force a layout here just to make sure every position has been + // determined before we send events (as well as all the other methods + // that send an event do). The layout test calling this + // (scrollbars/overflow-scrollbar-horizontal-wheel-scroll.html, only one + // for now) does not rely on this though. + webview()->layout(); + + int horizontal = arguments[0].toInt32(); + int vertical = arguments[1].toInt32(); + + WebMouseWheelEvent event; + initMouseEvent(WebInputEvent::MouseWheel, pressedButton, lastMousePos, &event); + event.wheelTicksX = static_cast(horizontal); + event.wheelTicksY = static_cast(vertical); + event.deltaX = -horizontal * scrollbarPixelsPerTick; + event.deltaY = -vertical * scrollbarPixelsPerTick; + webview()->handleInputEvent(event); +} + +void EventSender::doMouseMove(const WebMouseEvent& e) +{ + lastMousePos = WebPoint(e.x, e.y); + + webview()->handleInputEvent(e); + + if (pressedButton == WebMouseEvent::ButtonNone || currentDragData.isNull()) + return; + WebPoint clientPoint(e.x, e.y); + WebPoint screenPoint(e.globalX, e.globalY); + currentDragEffect = webview()->dragTargetDragOver(clientPoint, screenPoint, currentDragEffectsAllowed); +} + +void EventSender::keyDown(const CppArgumentList& arguments, CppVariant* result) +{ + result->setNull(); + if (arguments.size() < 1 || !arguments[0].isString()) + return; + bool generateChar = false; + + // FIXME: I'm not exactly sure how we should convert the string to a key + // event. This seems to work in the cases I tested. + // FIXME: Should we also generate a KEY_UP? + string codeStr = arguments[0].toString(); + + // Convert \n -> VK_RETURN. Some layout tests use \n to mean "Enter", when + // Windows uses \r for "Enter". + int code = 0; + int text = 0; + bool needsShiftKeyModifier = false; + if ("\n" == codeStr) { + generateChar = true; + text = code = base::VKEY_RETURN; + } else if ("rightArrow" == codeStr) + code = base::VKEY_RIGHT; + else if ("downArrow" == codeStr) + code = base::VKEY_DOWN; + else if ("leftArrow" == codeStr) + code = base::VKEY_LEFT; + else if ("upArrow" == codeStr) + code = base::VKEY_UP; + else if ("delete" == codeStr) + code = base::VKEY_BACK; + else if ("pageUp" == codeStr) + code = base::VKEY_PRIOR; + else if ("pageDown" == codeStr) + code = base::VKEY_NEXT; + else if ("home" == codeStr) + code = base::VKEY_HOME; + else if ("end" == codeStr) + code = base::VKEY_END; + else { + // Compare the input string with the function-key names defined by the + // DOM spec (i.e. "F1",...,"F24"). If the input string is a function-key + // name, set its key code. + for (int i = 1; i <= 24; ++i) { + char functionChars[10]; + snprintf(functionChars, 10, "F%d", i); + string functionKeyName(functionChars); + if (functionKeyName == codeStr) { + code = base::VKEY_F1 + (i - 1); + break; + } + } + if (!code) { + WebString webCodeStr = WebString::fromUTF8(codeStr.data(), codeStr.size()); + ASSERT(webCodeStr.length() == 1); + text = code = webCodeStr.data()[0]; + needsShiftKeyModifier = needsShiftModifier(code); + if ((code & 0xFF) >= 'a' && (code & 0xFF) <= 'z') + code -= 'a' - 'A'; + generateChar = true; + } + } + + // For one generated keyboard event, we need to generate a keyDown/keyUp + // pair; refer to EventSender.cpp in WebKit/WebKitTools/DumpRenderTree/win. + // On Windows, we might also need to generate a char event to mimic the + // Windows event flow; on other platforms we create a merged event and test + // the event flow that that platform provides. + WebKeyboardEvent eventDown, eventChar, eventUp; + eventDown.type = WebInputEvent::RawKeyDown; + eventDown.modifiers = 0; + eventDown.windowsKeyCode = code; + if (generateChar) { + eventDown.text[0] = text; + eventDown.unmodifiedText[0] = text; + } + eventDown.setKeyIdentifierFromWindowsKeyCode(); + + if (arguments.size() >= 2 && (arguments[1].isObject() || arguments[1].isString())) + eventDown.isSystemKey = applyKeyModifiers(&(arguments[1]), &eventDown); + + if (needsShiftKeyModifier) + eventDown.modifiers |= WebInputEvent::ShiftKey; + + // See if KeyLocation argument is given. + if (arguments.size() >= 3 && arguments[2].isNumber()) { + int location = arguments[2].toInt32(); + if (location == DOMKeyLocationNumpad) + eventDown.modifiers |= WebInputEvent::IsKeyPad; + } + + eventChar = eventUp = eventDown; + eventUp.type = WebInputEvent::KeyUp; + // EventSender.m forces a layout here, with at least one + // test (fast/forms/focus-control-to-page.html) relying on this. + webview()->layout(); + + // In the browser, if a keyboard event corresponds to an editor command, + // the command will be dispatched to the renderer just before dispatching + // the keyboard event, and then it will be executed in the + // RenderView::handleCurrentKeyboardEvent() method, which is called from + // third_party/WebKit/WebKit/chromium/src/EditorClientImpl.cpp. + // We just simulate the same behavior here. + string editCommand; + if (getEditCommand(eventDown, &editCommand)) + testShell->webViewHost()->setEditCommand(editCommand, ""); + + webview()->handleInputEvent(eventDown); + + testShell->webViewHost()->clearEditCommand(); + + if (generateChar) { + eventChar.type = WebInputEvent::Char; + eventChar.keyIdentifier[0] = '\0'; + webview()->handleInputEvent(eventChar); + } + + webview()->handleInputEvent(eventUp); +} + +void EventSender::dispatchMessage(const CppArgumentList& arguments, CppVariant* result) +{ + result->setNull(); + +#if OS(WINDOWS) + if (arguments.size() == 3) { + // Grab the message id to see if we need to dispatch it. + int msg = arguments[0].toInt32(); + + // WebKit's version of this function stuffs a MSG struct and uses + // TranslateMessage and DispatchMessage. We use a WebKeyboardEvent, which + // doesn't need to receive the DeadChar and SysDeadChar messages. + if (msg == WM_DEADCHAR || msg == WM_SYSDEADCHAR) + return; + + webview()->layout(); + + unsigned long lparam = static_cast(arguments[2].toDouble()); + webview()->handleInputEvent(WebInputEventFactory::keyboardEvent(0, msg, arguments[1].toInt32(), lparam)); + } else + ASSERT_NOT_REACHED(); +#endif +} + +bool EventSender::needsShiftModifier(int keyCode) +{ + // If code is an uppercase letter, assign a SHIFT key to + // eventDown.modifier, this logic comes from + // WebKit/WebKitTools/DumpRenderTree/Win/EventSender.cpp + return (keyCode & 0xFF) >= 'A' && (keyCode & 0xFF) <= 'Z'; +} + +void EventSender::leapForward(const CppArgumentList& arguments, CppVariant* result) +{ + result->setNull(); + + if (arguments.size() < 1 || !arguments[0].isNumber()) + return; + + int milliseconds = arguments[0].toInt32(); + if (isDragMode() && pressedButton == WebMouseEvent::ButtonLeft && !replayingSavedEvents) { + SavedEvent savedEvent; + savedEvent.type = SavedEvent::LeapForward; + savedEvent.milliseconds = milliseconds; + mouseEventQueue.append(savedEvent); + } else + doLeapForward(milliseconds); +} + +void EventSender::doLeapForward(int milliseconds) +{ + advanceEventTime(milliseconds); +} + +// Apple's port of WebKit zooms by a factor of 1.2 (see +// WebKit/WebView/WebView.mm) +void EventSender::textZoomIn(const CppArgumentList&, CppVariant* result) +{ + webview()->setZoomLevel(true, webview()->zoomLevel() + 1); + result->setNull(); +} + +void EventSender::textZoomOut(const CppArgumentList&, CppVariant* result) +{ + webview()->setZoomLevel(true, webview()->zoomLevel() - 1); + result->setNull(); +} + +void EventSender::zoomPageIn(const CppArgumentList&, CppVariant* result) +{ + webview()->setZoomLevel(false, webview()->zoomLevel() + 1); + result->setNull(); +} + +void EventSender::zoomPageOut(const CppArgumentList&, CppVariant* result) +{ + webview()->setZoomLevel(false, webview()->zoomLevel() - 1); + result->setNull(); +} + +void EventSender::replaySavedEvents() +{ + replayingSavedEvents = true; + while (!mouseEventQueue.isEmpty()) { + SavedEvent e = mouseEventQueue.first(); + mouseEventQueue.removeFirst(); + + switch (e.type) { + case SavedEvent::MouseMove: { + WebMouseEvent event; + initMouseEvent(WebInputEvent::MouseMove, pressedButton, e.pos, &event); + doMouseMove(event); + break; + } + case SavedEvent::LeapForward: + doLeapForward(e.milliseconds); + break; + case SavedEvent::MouseUp: { + WebMouseEvent event; + initMouseEvent(WebInputEvent::MouseUp, e.buttonType, lastMousePos, &event); + doMouseUp(event); + break; + } + default: + ASSERT_NOT_REACHED(); + } + } + + replayingSavedEvents = false; +} + +void EventSender::contextClick(const CppArgumentList& arguments, CppVariant* result) +{ + result->setNull(); + + webview()->layout(); + + updateClickCountForButton(WebMouseEvent::ButtonRight); + + // Generate right mouse down and up. + + WebMouseEvent event; + pressedButton = WebMouseEvent::ButtonRight; + initMouseEvent(WebInputEvent::MouseDown, WebMouseEvent::ButtonRight, lastMousePos, &event); + webview()->handleInputEvent(event); + + initMouseEvent(WebInputEvent::MouseUp, WebMouseEvent::ButtonRight, lastMousePos, &event); + webview()->handleInputEvent(event); + + pressedButton = WebMouseEvent::ButtonNone; +} + +void EventSender::scheduleAsynchronousClick(const CppArgumentList& arguments, CppVariant* result) +{ + result->setNull(); + + webkit_support::PostTaskFromHere(m_methodFactory.NewRunnableMethod( + &EventSender::mouseDown, arguments, static_cast(0))); + webkit_support::PostTaskFromHere(m_methodFactory.NewRunnableMethod( + &EventSender::mouseUp, arguments, static_cast(0))); +} + +void EventSender::beginDragWithFiles(const CppArgumentList& arguments, CppVariant* result) +{ + currentDragData.initialize(); + Vector files = arguments[0].toStringVector(); + for (size_t i = 0; i < files.size(); ++i) + currentDragData.appendToFileNames(webkit_support::GetAbsoluteWebStringFromUTF8Path(files[i])); + currentDragEffectsAllowed = WebKit::WebDragOperationCopy; + + // Provide a drag source. + webview()->dragTargetDragEnter(currentDragData, 0, lastMousePos, lastMousePos, currentDragEffectsAllowed); + + // dragMode saves events and then replays them later. We don't need/want that. + dragMode.set(false); + + // Make the rest of eventSender think a drag is in progress. + pressedButton = WebMouseEvent::ButtonLeft; + + result->setNull(); +} + +// +// Unimplemented stubs +// + +void EventSender::enableDOMUIEventLogging(const CppArgumentList&, CppVariant* result) +{ + result->setNull(); +} + +void EventSender::fireKeyboardEventsToElement(const CppArgumentList&, CppVariant* result) +{ + result->setNull(); +} + +void EventSender::clearKillRing(const CppArgumentList&, CppVariant* result) +{ + result->setNull(); +} diff --git a/WebKitTools/DumpRenderTree/chromium/EventSender.h b/WebKitTools/DumpRenderTree/chromium/EventSender.h new file mode 100644 index 0000000..756b008 --- /dev/null +++ b/WebKitTools/DumpRenderTree/chromium/EventSender.h @@ -0,0 +1,145 @@ +/* + * Copyright (C) 2010 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + EventSender class: + Bound to a JavaScript window.eventSender object using + CppBoundClass::bindToJavascript(), this allows layout tests to fire DOM events. +*/ + +#ifndef EventSender_h +#define EventSender_h + +#include "CppBoundClass.h" +#include "base/task.h" +#include "public/WebDragOperation.h" +#include "public/WebInputEvent.h" +#include "public/WebPoint.h" + +class TestShell; + +namespace WebKit { +class WebDragData; +class WebView; +} + +class EventSender : public CppBoundClass { +public: + // Builds the property and method lists needed to bind this class to a JS + // object. + EventSender(TestShell*); + + // Resets some static variable state. + void reset(); + + // Simulate drag&drop system call. + static void doDragDrop(const WebKit::WebPoint&, + const WebKit::WebDragData&, + WebKit::WebDragOperationsMask); + + // JS callback methods. + void mouseDown(const CppArgumentList&, CppVariant*); + void mouseUp(const CppArgumentList&, CppVariant*); + void mouseMoveTo(const CppArgumentList&, CppVariant*); + void mouseWheelTo(const CppArgumentList&, CppVariant*); + void leapForward(const CppArgumentList&, CppVariant*); + void keyDown(const CppArgumentList&, CppVariant*); + void dispatchMessage(const CppArgumentList&, CppVariant*); + void textZoomIn(const CppArgumentList&, CppVariant*); + void textZoomOut(const CppArgumentList&, CppVariant*); + void zoomPageIn(const CppArgumentList&, CppVariant*); + void zoomPageOut(const CppArgumentList&, CppVariant*); + void scheduleAsynchronousClick(const CppArgumentList&, CppVariant*); + void beginDragWithFiles(const CppArgumentList&, CppVariant*); + CppVariant dragMode; + + // Unimplemented stubs + void contextClick(const CppArgumentList&, CppVariant*); + void enableDOMUIEventLogging(const CppArgumentList&, CppVariant*); + void fireKeyboardEventsToElement(const CppArgumentList&, CppVariant*); + void clearKillRing(const CppArgumentList&, CppVariant*); + + // Properties used in layout tests. +#if defined(OS_WIN) + CppVariant wmKeyDown; + CppVariant wmKeyUp; + CppVariant wmChar; + CppVariant wmDeadChar; + CppVariant wmSysKeyDown; + CppVariant wmSysKeyUp; + CppVariant wmSysChar; + CppVariant wmSysDeadChar; +#endif + +private: + // Returns the test shell's webview. + static WebKit::WebView* webview(); + + // Returns true if dragMode is true. + bool isDragMode() { return dragMode.isBool() && dragMode.toBoolean(); } + + // Sometimes we queue up mouse move and mouse up events for drag drop + // handling purposes. These methods dispatch the event. + static void doMouseMove(const WebKit::WebMouseEvent&); + static void doMouseUp(const WebKit::WebMouseEvent&); + static void doLeapForward(int milliseconds); + static void replaySavedEvents(); + + // Helper to return the button type given a button code + static WebKit::WebMouseEvent::Button getButtonTypeFromButtonNumber(int); + + // Helper to extract the button number from the optional argument in + // mouseDown and mouseUp + static int getButtonNumberFromSingleArg(const CppArgumentList&); + + // Returns true if the specified key code passed in needs a shift key + // modifier to be passed into the generated event. + bool needsShiftModifier(int); + + void updateClickCountForButton(WebKit::WebMouseEvent::Button); + + ScopedRunnableMethodFactory m_methodFactory; + + // Non-owning pointer. The EventSender is owned by the TestShell. + static TestShell* testShell; + + // Location of last mouseMoveTo event. + static WebKit::WebPoint lastMousePos; + + // Currently pressed mouse button (Left/Right/Middle or None) + static WebKit::WebMouseEvent::Button pressedButton; + + // The last button number passed to mouseDown and mouseUp. + // Used to determine whether the click count continues to + // increment or not. + static WebKit::WebMouseEvent::Button lastButtonType; +}; + +#endif // EventSender_h diff --git a/WebKitTools/DumpRenderTree/chromium/ImageDiff.cpp b/WebKitTools/DumpRenderTree/chromium/ImageDiff.cpp new file mode 100644 index 0000000..81d5f62 --- /dev/null +++ b/WebKitTools/DumpRenderTree/chromium/ImageDiff.cpp @@ -0,0 +1,407 @@ +/* + * Copyright (C) 2010 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +// This file input format is based loosely on +// WebKitTools/DumpRenderTree/ImageDiff.m + +// The exact format of this tool's output to stdout is important, to match +// what the run-webkit-tests script expects. + +#include "config.h" + +#include "gfx/codec/png_codec.h" +#include +#include +#include +#include +#include +#include +#include + +using namespace gfx; +using namespace std; + +// Causes the app to remain open, waiting for pairs of filenames on stdin. +// The caller is then responsible for terminating this app. +static const char optionPollStdin[] = "--use-stdin"; +static const char optionGenerateDiff[] = "--diff"; + +// Return codes used by this utility. +static const int statusSame = 0; +static const int statusDifferent = 1; +static const int statusError = 2; + +// Color codes. +static const uint32_t rgbaRed = 0x000000ff; +static const uint32_t rgbaAlpha = 0xff000000; + +class Image { +public: + Image() + : m_width(0) + , m_height(0) {} + + Image(const Image& image) + : m_width(image.m_width) + , m_height(image.m_height) + , m_data(image.m_data) {} + + bool hasImage() const { return m_width > 0 && m_height > 0; } + int width() const { return m_width; } + int height() const { return m_height; } + const unsigned char* data() const { return &m_data.front(); } + + // Creates the image from stdin with the given data length. On success, it + // will return true. On failure, no other methods should be accessed. + bool craeteFromStdin(size_t byteLength) + { + if (!byteLength) + return false; + + OwnArrayPtr source(new unsigned char[byteLength]); + if (fread(source.get(), 1, byteLength, stdin) != byteLength) + return false; + + if (!PNGCodec::Decode(source.get(), byteLength, PNGCodec::FORMAT_RGBA, + &m_data, &m_width, &m_height)) { + clear(); + return false; + } + return true; + } + + // Creates the image from the given filename on disk, and returns true on + // success. + bool createFromFilename(const char* filename) + { + FILE* f = fopen(filename, "rb"); + if (!f) + return false; + + vector compressed; + const int bufSize = 1024; + unsigned char buf[bufSize]; + size_t numRead = 0; + while ((numRead = fread(buf, 1, bufSize, f)) > 0) + std::copy(buf, &buf[numRead], std::back_inserter(compressed)); + + fclose(f); + + if (!PNGCodec::Decode(&compressed[0], compressed.size(), + PNGCodec::FORMAT_RGBA, &m_data, &m_width, &m_height)) { + clear(); + return false; + } + return true; + } + + void clear() + { + m_width = m_height = 0; + m_data.clear(); + } + + // Returns the RGBA value of the pixel at the given location + const uint32_t pixelAt(int x, int y) const + { + ASSERT(x >= 0 && x < m_width); + ASSERT(y >= 0 && y < m_height); + return *reinterpret_cast(&(m_data[(y * m_width + x) * 4])); + } + + void setPixelAt(int x, int y, uint32_t color) const + { + ASSERT(x >= 0 && x < m_width); + ASSERT(y >= 0 && y < m_height); + void* addr = &const_cast(&m_data.front())[(y * m_width + x) * 4]; + *reinterpret_cast(addr) = color; + } + +private: + // pixel dimensions of the image + int m_width, m_height; + + vector m_data; +}; + +float percentageDifferent(const Image& baseline, const Image& actual) +{ + int w = min(baseline.width(), actual.width()); + int h = min(baseline.height(), actual.height()); + + // Compute pixels different in the overlap + int pixelsDifferent = 0; + for (int y = 0; y < h; y++) { + for (int x = 0; x < w; x++) { + if (baseline.pixelAt(x, y) != actual.pixelAt(x, y)) + pixelsDifferent++; + } + } + + // Count pixels that are a difference in size as also being different + int maxWidth = max(baseline.width(), actual.width()); + int maxHeight = max(baseline.height(), actual.height()); + + // ...pixels off the right side, but not including the lower right corner + pixelsDifferent += (maxWidth - w) * h; + + // ...pixels along the bottom, including the lower right corner + pixelsDifferent += (maxHeight - h) * maxWidth; + + // Like the WebKit ImageDiff tool, we define percentage different in terms + // of the size of the 'actual' bitmap. + float totalPixels = static_cast(actual.width()) * static_cast(actual.height()); + if (!totalPixels) + return 100.0f; // When the bitmap is empty, they are 100% different. + return static_cast(pixelsDifferent) / totalPixels * 100; +} + +void printHelp() +{ + fprintf(stderr, + "Usage:\n" + " ImageDiff \n" + " Compares two files on disk, returning 0 when they are the same\n" + " ImageDiff --use-stdin\n" + " Stays open reading pairs of filenames from stdin, comparing them,\n" + " and sending 0 to stdout when they are the same\n" + " ImageDiff --diff \n" + " Compares two files on disk, outputs an image that visualizes the" + " difference to \n"); + /* For unfinished webkit-like-mode (see below) + "\n" + " ImageDiff -s\n" + " Reads stream input from stdin, should be EXACTLY of the format\n" + " \"Content-length: Content-length: ...\n" + " it will take as many file pairs as given, and will compare them as\n" + " (cmp_file, reference_file) pairs\n"); + */ +} + +int compareImages(const char* file1, const char* file2) +{ + Image actualImage; + Image baselineImage; + + if (!actualImage.createFromFilename(file1)) { + fprintf(stderr, "ImageDiff: Unable to open file \"%s\"\n", file1); + return statusError; + } + if (!baselineImage.createFromFilename(file2)) { + fprintf(stderr, "ImageDiff: Unable to open file \"%s\"\n", file2); + return statusError; + } + + float percent = percentageDifferent(actualImage, baselineImage); + if (percent > 0.0) { + // failure: The WebKit version also writes the difference image to + // stdout, which seems excessive for our needs. + printf("diff: %01.2f%% failed\n", percent); + return statusDifferent; + } + + // success + printf("diff: %01.2f%% passed\n", percent); + return statusSame; + +} + +// Untested mode that acts like WebKit's image comparator. I wrote this but +// decided it's too complicated. We may use it in the future if it looks useful. +int untestedCompareImages() +{ + Image actualImage; + Image baselineImage; + char buffer[2048]; + while (fgets(buffer, sizeof(buffer), stdin)) { + if (!strncmp("Content-length: ", buffer, 16)) { + char* context; +#if OS(WINDOWS) + strtok_s(buffer, " ", &context); + int imageSize = strtol(strtok_s(0, " ", &context), 0, 10); +#else + strtok_r(buffer, " ", &context); + int imageSize = strtol(strtok_r(0, " ", &context), 0, 10); +#endif + + bool success = false; + if (imageSize > 0 && !actualImage.hasImage()) { + if (!actualImage.craeteFromStdin(imageSize)) { + fputs("Error, input image can't be decoded.\n", stderr); + return 1; + } + } else if (imageSize > 0 && !baselineImage.hasImage()) { + if (!baselineImage.craeteFromStdin(imageSize)) { + fputs("Error, baseline image can't be decoded.\n", stderr); + return 1; + } + } else { + fputs("Error, image size must be specified.\n", stderr); + return 1; + } + } + + if (actualImage.hasImage() && baselineImage.hasImage()) { + float percent = percentageDifferent(actualImage, baselineImage); + if (percent > 0.0) { + // failure: The WebKit version also writes the difference image to + // stdout, which seems excessive for our needs. + printf("diff: %01.2f%% failed\n", percent); + } else { + // success + printf("diff: %01.2f%% passed\n", percent); + } + actualImage.clear(); + baselineImage.clear(); + } + fflush(stdout); + } + return 0; +} + +bool createImageDiff(const Image& image1, const Image& image2, Image* out) +{ + int w = min(image1.width(), image2.width()); + int h = min(image1.height(), image2.height()); + *out = Image(image1); + bool same = (image1.width() == image2.width()) && (image1.height() == image2.height()); + + // FIXME: do something with the extra pixels if the image sizes are different. + for (int y = 0; y < h; y++) { + for (int x = 0; x < w; x++) { + uint32_t basePixel = image1.pixelAt(x, y); + if (basePixel != image2.pixelAt(x, y)) { + // Set differing pixels red. + out->setPixelAt(x, y, rgbaRed | rgbaAlpha); + same = false; + } else { + // Set same pixels as faded. + uint32_t alpha = basePixel & rgbaAlpha; + uint32_t newPixel = basePixel - ((alpha / 2) & rgbaAlpha); + out->setPixelAt(x, y, newPixel); + } + } + } + + return same; +} + +static bool writeFile(const char* outFile, const unsigned char* data, size_t dataSize) +{ + FILE* file = fopen(outFile, "wb"); + if (!file) { + fprintf(stderr, "ImageDiff: Unable to create file \"%s\"\n", file); + return false; + } + if (dataSize != fwrite(data, 1, dataSize, file)) { + fclose(file); + fprintf(stderr, "ImageDiff: Unable to write data to file \"%s\"\n", file); + return false; + } + fclose(file); + return true; +} + +int diffImages(const char* file1, const char* file2, const char* outFile) +{ + Image actualImage; + Image baselineImage; + + if (!actualImage.createFromFilename(file1)) { + fprintf(stderr, "ImageDiff: Unable to open file \"%s\"\n", file1); + return statusError; + } + if (!baselineImage.createFromFilename(file2)) { + fprintf(stderr, "ImageDiff: Unable to open file \"%s\"\n", file2); + return statusError; + } + + Image diffImage; + bool same = createImageDiff(baselineImage, actualImage, &diffImage); + if (same) + return statusSame; + + vector pngData; + PNGCodec::Encode(diffImage.data(), PNGCodec::FORMAT_RGBA, diffImage.width(), + diffImage.height(), diffImage.width() * 4, false, &pngData); + if (!writeFile(outFile, &pngData.front(), pngData.size())) + return statusError; + return statusDifferent; +} + +int main(int argc, const char* argv[]) +{ + Vector values; + bool pollStdin = false; + bool generateDiff = false; + for (int i = 1; i < argc; ++i) { + if (!strcmp(argv[i], optionPollStdin)) + pollStdin = true; + else if (!strcmp(argv[i], optionGenerateDiff)) + generateDiff = true; + else + values.append(argv[i]); + } + + if (pollStdin) { + // Watch stdin for filenames. + const size_t bufferSize = PATH_MAX; + char stdinBuffer[bufferSize]; + char firstName[bufferSize]; + bool haveFirstName = false; + while (fgets(stdinBuffer, bufferSize, stdin)) { + if (!stdinBuffer[0]) + continue; + + if (haveFirstName) { + // compareImages writes results to stdout unless an error occurred. + if (compareImages(firstName, stdinBuffer) == statusError) + printf("error\n"); + fflush(stdout); + haveFirstName = false; + } else { + // Save the first filename in another buffer and wait for the second + // filename to arrive via stdin. + strcpy(firstName, stdinBuffer); + haveFirstName = true; + } + } + return 0; + } + + if (generateDiff) { + if (values.size() == 3) + return diffImages(values[0], values[1], values[2]); + } else if (values.size() == 2) + return compareImages(argv[1], argv[2]); + + printHelp(); + return statusError; +} diff --git a/WebKitTools/DumpRenderTree/chromium/LayoutTestController.cpp b/WebKitTools/DumpRenderTree/chromium/LayoutTestController.cpp new file mode 100644 index 0000000..d0dca69 --- /dev/null +++ b/WebKitTools/DumpRenderTree/chromium/LayoutTestController.cpp @@ -0,0 +1,1179 @@ +/* + * Copyright (C) 2010 Google Inc. All rights reserved. + * Copyright (C) 2010 Pawel Hajdan (phajdan.jr@chromium.org) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "LayoutTestController.h" + +#include "TestShell.h" +#include "WebViewHost.h" +#include "base/string_util.h" +#include "public/WebAnimationController.h" +#include "public/WebConsoleMessage.h" +#include "public/WebDocument.h" +#include "public/WebFrame.h" +#include "public/WebInputElement.h" +#include "public/WebKit.h" +#include "public/WebScriptSource.h" +#include "public/WebSecurityPolicy.h" +#include "public/WebSettings.h" +#include "public/WebSize.h" +#include "public/WebURL.h" +#include "public/WebView.h" +#include "webkit/support/webkit_support.h" + +#if OS(WINDOWS) +#include +#endif + +using namespace WebKit; +using namespace std; + +LayoutTestController::LayoutTestController(TestShell* shell) + : m_timeoutFactory(this) + , m_shell(shell) + , m_workQueue(this) +{ + + // Initialize the map that associates methods of this class with the names + // they will use when called by JavaScript. The actual binding of those + // names to their methods will be done by calling bindToJavaScript() (defined + // by CppBoundClass, the parent to LayoutTestController). + bindMethod("dumpAsText", &LayoutTestController::dumpAsText); + bindMethod("dumpChildFrameScrollPositions", &LayoutTestController::dumpChildFrameScrollPositions); + bindMethod("dumpChildFramesAsText", &LayoutTestController::dumpChildFramesAsText); + bindMethod("dumpDatabaseCallbacks", &LayoutTestController::dumpDatabaseCallbacks); + bindMethod("dumpEditingCallbacks", &LayoutTestController::dumpEditingCallbacks); + bindMethod("dumpBackForwardList", &LayoutTestController::dumpBackForwardList); + bindMethod("dumpFrameLoadCallbacks", &LayoutTestController::dumpFrameLoadCallbacks); + bindMethod("dumpResourceLoadCallbacks", &LayoutTestController::dumpResourceLoadCallbacks); + bindMethod("dumpStatusCallbacks", &LayoutTestController::dumpWindowStatusChanges); + bindMethod("dumpTitleChanges", &LayoutTestController::dumpTitleChanges); + bindMethod("setAcceptsEditing", &LayoutTestController::setAcceptsEditing); + bindMethod("waitUntilDone", &LayoutTestController::waitUntilDone); + bindMethod("notifyDone", &LayoutTestController::notifyDone); + bindMethod("queueReload", &LayoutTestController::queueReload); + bindMethod("queueLoadingScript", &LayoutTestController::queueLoadingScript); + bindMethod("queueNonLoadingScript", &LayoutTestController::queueNonLoadingScript); + bindMethod("queueLoad", &LayoutTestController::queueLoad); + bindMethod("queueBackNavigation", &LayoutTestController::queueBackNavigation); + bindMethod("queueForwardNavigation", &LayoutTestController::queueForwardNavigation); + bindMethod("windowCount", &LayoutTestController::windowCount); + bindMethod("setCanOpenWindows", &LayoutTestController::setCanOpenWindows); + bindMethod("setCloseRemainingWindowsWhenComplete", &LayoutTestController::setCloseRemainingWindowsWhenComplete); + bindMethod("objCIdentityIsEqual", &LayoutTestController::objCIdentityIsEqual); + bindMethod("setAlwaysAcceptCookies", &LayoutTestController::setAlwaysAcceptCookies); + bindMethod("setWindowIsKey", &LayoutTestController::setWindowIsKey); + bindMethod("setTabKeyCyclesThroughElements", &LayoutTestController::setTabKeyCyclesThroughElements); + bindMethod("setUserStyleSheetLocation", &LayoutTestController::setUserStyleSheetLocation); + bindMethod("setUserStyleSheetEnabled", &LayoutTestController::setUserStyleSheetEnabled); + bindMethod("pathToLocalResource", &LayoutTestController::pathToLocalResource); + bindMethod("addFileToPasteboardOnDrag", &LayoutTestController::addFileToPasteboardOnDrag); + bindMethod("execCommand", &LayoutTestController::execCommand); + bindMethod("isCommandEnabled", &LayoutTestController::isCommandEnabled); + bindMethod("setPopupBlockingEnabled", &LayoutTestController::setPopupBlockingEnabled); + bindMethod("setStopProvisionalFrameLoads", &LayoutTestController::setStopProvisionalFrameLoads); + bindMethod("setSmartInsertDeleteEnabled", &LayoutTestController::setSmartInsertDeleteEnabled); + bindMethod("setSelectTrailingWhitespaceEnabled", &LayoutTestController::setSelectTrailingWhitespaceEnabled); + bindMethod("pauseAnimationAtTimeOnElementWithId", &LayoutTestController::pauseAnimationAtTimeOnElementWithId); + bindMethod("pauseTransitionAtTimeOnElementWithId", &LayoutTestController::pauseTransitionAtTimeOnElementWithId); + bindMethod("elementDoesAutoCompleteForElementWithId", &LayoutTestController::elementDoesAutoCompleteForElementWithId); + bindMethod("numberOfActiveAnimations", &LayoutTestController::numberOfActiveAnimations); + bindMethod("disableImageLoading", &LayoutTestController::disableImageLoading); + bindMethod("setIconDatabaseEnabled", &LayoutTestController::setIconDatabaseEnabled); + bindMethod("setCustomPolicyDelegate", &LayoutTestController::setCustomPolicyDelegate); + bindMethod("waitForPolicyDelegate", &LayoutTestController::waitForPolicyDelegate); + bindMethod("setWillSendRequestReturnsNullOnRedirect", &LayoutTestController::setWillSendRequestReturnsNullOnRedirect); + bindMethod("setWillSendRequestReturnsNull", &LayoutTestController::setWillSendRequestReturnsNull); + bindMethod("addOriginAccessWhitelistEntry", &LayoutTestController::addOriginAccessWhitelistEntry); + bindMethod("clearAllDatabases", &LayoutTestController::clearAllDatabases); + bindMethod("setDatabaseQuota", &LayoutTestController::setDatabaseQuota); + bindMethod("setPOSIXLocale", &LayoutTestController::setPOSIXLocale); + bindMethod("counterValueForElementById", &LayoutTestController::counterValueForElementById); + bindMethod("addUserScript", &LayoutTestController::addUserScript); + bindMethod("pageNumberForElementById", &LayoutTestController::pageNumberForElementById); + bindMethod("numberOfPages", &LayoutTestController::numberOfPages); + bindMethod("dumpSelectionRect", &LayoutTestController::dumpSelectionRect); + + // The following are stubs. + bindMethod("dumpAsWebArchive", &LayoutTestController::dumpAsWebArchive); + bindMethod("setMainFrameIsFirstResponder", &LayoutTestController::setMainFrameIsFirstResponder); + bindMethod("dumpSelectionRect", &LayoutTestController::dumpSelectionRect); + bindMethod("display", &LayoutTestController::display); + bindMethod("testRepaint", &LayoutTestController::testRepaint); + bindMethod("repaintSweepHorizontally", &LayoutTestController::repaintSweepHorizontally); + bindMethod("clearBackForwardList", &LayoutTestController::clearBackForwardList); + bindMethod("keepWebHistory", &LayoutTestController::keepWebHistory); + bindMethod("storeWebScriptObject", &LayoutTestController::storeWebScriptObject); + bindMethod("accessStoredWebScriptObject", &LayoutTestController::accessStoredWebScriptObject); + bindMethod("objCClassNameOf", &LayoutTestController::objCClassNameOf); + bindMethod("addDisallowedURL", &LayoutTestController::addDisallowedURL); + bindMethod("setCallCloseOnWebViews", &LayoutTestController::setCallCloseOnWebViews); + bindMethod("setPrivateBrowsingEnabled", &LayoutTestController::setPrivateBrowsingEnabled); + bindMethod("setUseDashboardCompatibilityMode", &LayoutTestController::setUseDashboardCompatibilityMode); + + bindMethod("setXSSAuditorEnabled", &LayoutTestController::setXSSAuditorEnabled); + bindMethod("evaluateScriptInIsolatedWorld", &LayoutTestController::evaluateScriptInIsolatedWorld); + bindMethod("overridePreference", &LayoutTestController::overridePreference); + bindMethod("setAllowUniversalAccessFromFileURLs", &LayoutTestController::setAllowUniversalAccessFromFileURLs); + bindMethod("setAllowFileAccessFromFileURLs", &LayoutTestController::setAllowFileAccessFromFileURLs); + bindMethod("setTimelineProfilingEnabled", &LayoutTestController::setTimelineProfilingEnabled); + bindMethod("evaluateInWebInspector", &LayoutTestController::evaluateInWebInspector); + bindMethod("forceRedSelectionColors", &LayoutTestController::forceRedSelectionColors); + + // The fallback method is called when an unknown method is invoked. + bindFallbackMethod(&LayoutTestController::fallbackMethod); + + // Shared properties. + // globalFlag is used by a number of layout tests in + // LayoutTests\http\tests\security\dataURL. + bindProperty("globalFlag", &m_globalFlag); + // webHistoryItemCount is used by tests in LayoutTests\http\tests\history + bindProperty("webHistoryItemCount", &m_webHistoryItemCount); +} + +LayoutTestController::WorkQueue::~WorkQueue() +{ + reset(); +} + +void LayoutTestController::WorkQueue::processWorkSoon() +{ + if (m_controller->m_shell->webViewHost()->topLoadingFrame()) + return; + + if (!m_queue.isEmpty()) { + // We delay processing queued work to avoid recursion problems. + m_timer.Start(base::TimeDelta(), this, &WorkQueue::processWork); + } else if (!m_controller->m_waitUntilDone) { + m_controller->m_shell->testFinished(); + } +} + +void LayoutTestController::WorkQueue::processWork() +{ + TestShell* shell = m_controller->m_shell; + // Quit doing work once a load is in progress. + while (!m_queue.isEmpty()) { + bool startedLoad = m_queue.first()->run(shell); + delete m_queue.first(); + m_queue.removeFirst(); + if (startedLoad) + return; + } + + if (!m_controller->m_waitUntilDone && !shell->webViewHost()->topLoadingFrame()) + shell->testFinished(); +} + +void LayoutTestController::WorkQueue::reset() +{ + m_frozen = false; + while (!m_queue.isEmpty()) { + delete m_queue.first(); + m_queue.removeFirst(); + } +} + +void LayoutTestController::WorkQueue::addWork(WorkItem* work) +{ + if (m_frozen) { + delete work; + return; + } + m_queue.append(work); +} + +void LayoutTestController::dumpAsText(const CppArgumentList&, CppVariant* result) +{ + m_dumpAsText = true; + result->setNull(); +} + +void LayoutTestController::dumpDatabaseCallbacks(const CppArgumentList&, CppVariant* result) +{ + // Do nothing; we don't use this flag anywhere for now + result->setNull(); +} + +void LayoutTestController::dumpEditingCallbacks(const CppArgumentList&, CppVariant* result) +{ + m_dumpEditingCallbacks = true; + result->setNull(); +} + +void LayoutTestController::dumpBackForwardList(const CppArgumentList&, CppVariant* result) +{ + m_dumpBackForwardList = true; + result->setNull(); +} + +void LayoutTestController::dumpFrameLoadCallbacks(const CppArgumentList&, CppVariant* result) +{ + m_dumpFrameLoadCallbacks = true; + result->setNull(); +} + +void LayoutTestController::dumpResourceLoadCallbacks(const CppArgumentList&, CppVariant* result) +{ + m_dumpResourceLoadCallbacks = true; + result->setNull(); +} + +void LayoutTestController::dumpChildFrameScrollPositions(const CppArgumentList&, CppVariant* result) +{ + m_dumpChildFrameScrollPositions = true; + result->setNull(); +} + +void LayoutTestController::dumpChildFramesAsText(const CppArgumentList&, CppVariant* result) +{ + m_dumpChildFramesAsText = true; + result->setNull(); +} + +void LayoutTestController::dumpWindowStatusChanges(const CppArgumentList&, CppVariant* result) +{ + m_dumpWindowStatusChanges = true; + result->setNull(); +} + +void LayoutTestController::dumpTitleChanges(const CppArgumentList&, CppVariant* result) +{ + m_dumpTitleChanges = true; + result->setNull(); +} + +void LayoutTestController::setAcceptsEditing(const CppArgumentList& arguments, CppVariant* result) +{ + if (arguments.size() > 0 && arguments[0].isBool()) + m_acceptsEditing = arguments[0].value.boolValue; + result->setNull(); +} + +void LayoutTestController::waitUntilDone(const CppArgumentList&, CppVariant* result) +{ + if (!webkit_support::BeingDebugged()) { + webkit_support::PostDelayedTaskFromHere( + m_timeoutFactory.NewRunnableMethod(&LayoutTestController::notifyDoneTimedOut), + m_shell->layoutTestTimeout()); + } + m_waitUntilDone = true; + result->setNull(); +} + +void LayoutTestController::notifyDone(const CppArgumentList&, CppVariant* result) +{ + // Test didn't timeout. Kill the timeout timer. + m_timeoutFactory.RevokeAll(); + + completeNotifyDone(false); + result->setNull(); +} + +void LayoutTestController::notifyDoneTimedOut() +{ + completeNotifyDone(true); +} + +void LayoutTestController::completeNotifyDone(bool isTimeout) +{ + if (m_waitUntilDone && !m_shell->webViewHost()->topLoadingFrame() && m_workQueue.isEmpty()) { + if (isTimeout) + m_shell->testTimedOut(); + else + m_shell->testFinished(); + } + m_waitUntilDone = false; +} + +class WorkItemBackForward : public LayoutTestController::WorkItem { +public: + WorkItemBackForward(int distance) : m_distance(distance) {} + bool run(TestShell* shell) + { + shell->goToOffset(m_distance); + return true; // FIXME: Did it really start a navigation? + } +private: + int m_distance; +}; + +void LayoutTestController::queueBackNavigation(const CppArgumentList& arguments, CppVariant* result) +{ + if (arguments.size() > 0 && arguments[0].isNumber()) + m_workQueue.addWork(new WorkItemBackForward(-arguments[0].toInt32())); + result->setNull(); +} + +void LayoutTestController::queueForwardNavigation(const CppArgumentList& arguments, CppVariant* result) +{ + if (arguments.size() > 0 && arguments[0].isNumber()) + m_workQueue.addWork(new WorkItemBackForward(arguments[0].toInt32())); + result->setNull(); +} + +class WorkItemReload : public LayoutTestController::WorkItem { +public: + bool run(TestShell* shell) + { + shell->reload(); + return true; + } +}; + +void LayoutTestController::queueReload(const CppArgumentList&, CppVariant* result) +{ + m_workQueue.addWork(new WorkItemReload); + result->setNull(); +} + +class WorkItemLoadingScript : public LayoutTestController::WorkItem { +public: + WorkItemLoadingScript(const string& script) : m_script(script) {} + bool run(TestShell* shell) + { + shell->webView()->mainFrame()->executeScript(WebScriptSource(WebString::fromUTF8(m_script))); + return true; // FIXME: Did it really start a navigation? + } +private: + string m_script; +}; + +class WorkItemNonLoadingScript : public LayoutTestController::WorkItem { +public: + WorkItemNonLoadingScript(const string& script) : m_script(script) {} + bool run(TestShell* shell) + { + shell->webView()->mainFrame()->executeScript(WebScriptSource(WebString::fromUTF8(m_script))); + return false; + } +private: + string m_script; +}; + +void LayoutTestController::queueLoadingScript(const CppArgumentList& arguments, CppVariant* result) +{ + if (arguments.size() > 0 && arguments[0].isString()) + m_workQueue.addWork(new WorkItemLoadingScript(arguments[0].toString())); + result->setNull(); +} + +void LayoutTestController::queueNonLoadingScript(const CppArgumentList& arguments, CppVariant* result) +{ + if (arguments.size() > 0 && arguments[0].isString()) + m_workQueue.addWork(new WorkItemNonLoadingScript(arguments[0].toString())); + result->setNull(); +} + +class WorkItemLoad : public LayoutTestController::WorkItem { +public: + WorkItemLoad(const WebURL& url, const WebString& target) + : m_url(url) + , m_target(target) {} + bool run(TestShell* shell) + { + shell->webViewHost()->loadURLForFrame(m_url, m_target); + return true; // FIXME: Did it really start a navigation? + } +private: + WebURL m_url; + WebString m_target; +}; + +void LayoutTestController::queueLoad(const CppArgumentList& arguments, CppVariant* result) +{ + if (arguments.size() > 0 && arguments[0].isString()) { + // FIXME: Implement WebURL::resolve() and avoid GURL. + GURL currentURL = m_shell->webView()->mainFrame()->url(); + GURL fullURL = currentURL.Resolve(arguments[0].toString()); + + string target = ""; + if (arguments.size() > 1 && arguments[1].isString()) + target = arguments[1].toString(); + + m_workQueue.addWork(new WorkItemLoad(fullURL, WebString::fromUTF8(target))); + } + result->setNull(); +} + +void LayoutTestController::objCIdentityIsEqual(const CppArgumentList& arguments, CppVariant* result) +{ + if (arguments.size() < 2) { + // This is the best we can do to return an error. + result->setNull(); + return; + } + result->set(arguments[0].isEqual(arguments[1])); +} + +void LayoutTestController::reset() +{ + if (m_shell) { + m_shell->webView()->setZoomLevel(false, 0); + m_shell->webView()->setTabKeyCyclesThroughElements(true); +#if defined(OS_LINUX) + // (Constants copied because we can't depend on the header that defined + // them from this file.) + m_shell->webView()->setSelectionColors(0xff1e90ff, 0xff000000, 0xffc8c8c8, 0xff323232); +#endif // defined(OS_LINUX) + m_shell->webView()->removeAllUserContent(); + } + m_dumpAsText = false; + m_dumpEditingCallbacks = false; + m_dumpFrameLoadCallbacks = false; + m_dumpResourceLoadCallbacks = false; + m_dumpBackForwardList = false; + m_dumpChildFrameScrollPositions = false; + m_dumpChildFramesAsText = false; + m_dumpWindowStatusChanges = false; + m_dumpSelectionRect = false; + m_dumpTitleChanges = false; + m_acceptsEditing = true; + m_waitUntilDone = false; + m_canOpenWindows = false; + m_testRepaint = false; + m_sweepHorizontally = false; + m_shouldAddFileToPasteboard = false; + m_stopProvisionalFrameLoads = false; + m_globalFlag.set(false); + m_webHistoryItemCount.set(0); + + webkit_support::SetAcceptAllCookies(false); + WebSecurityPolicy::resetOriginAccessWhiteLists(); + + // Reset the default quota for each origin to 5MB + webkit_support::SetDatabaseQuota(5 * 1024 * 1024); + + setlocale(LC_ALL, ""); + + if (m_closeRemainingWindows) + m_shell->closeRemainingWindows(); + else + m_closeRemainingWindows = true; + m_workQueue.reset(); +} + +void LayoutTestController::locationChangeDone() +{ + m_webHistoryItemCount.set(m_shell->navigationEntryCount()); + + // No more new work after the first complete load. + m_workQueue.setFrozen(true); + + if (!m_waitUntilDone) + m_workQueue.processWorkSoon(); +} + +void LayoutTestController::policyDelegateDone() +{ + ASSERT(m_waitUntilDone); + m_shell->testFinished(); + m_waitUntilDone = false; +} + +void LayoutTestController::setCanOpenWindows(const CppArgumentList&, CppVariant* result) +{ + m_canOpenWindows = true; + result->setNull(); +} + +void LayoutTestController::setTabKeyCyclesThroughElements(const CppArgumentList& arguments, CppVariant* result) +{ + if (arguments.size() > 0 && arguments[0].isBool()) + m_shell->webView()->setTabKeyCyclesThroughElements(arguments[0].toBoolean()); + result->setNull(); +} + +void LayoutTestController::windowCount(const CppArgumentList&, CppVariant* result) +{ + result->set(static_cast(m_shell->windowCount())); +} + +void LayoutTestController::setCloseRemainingWindowsWhenComplete(const CppArgumentList& arguments, CppVariant* result) +{ + if (arguments.size() > 0 && arguments[0].isBool()) + m_closeRemainingWindows = arguments[0].value.boolValue; + result->setNull(); +} + +void LayoutTestController::setAlwaysAcceptCookies(const CppArgumentList& arguments, CppVariant* result) +{ + if (arguments.size() > 0) + webkit_support::SetAcceptAllCookies(cppVariantToBool(arguments[0])); + result->setNull(); +} + +void LayoutTestController::setWindowIsKey(const CppArgumentList& arguments, CppVariant* result) +{ + if (arguments.size() > 0 && arguments[0].isBool()) + m_shell->setFocus(m_shell->webView(), arguments[0].value.boolValue); + result->setNull(); +} + +void LayoutTestController::setUserStyleSheetEnabled(const CppArgumentList& arguments, CppVariant* result) +{ + if (arguments.size() > 0 && arguments[0].isBool()) + m_shell->webView()->settings()->setUserStyleSheetLocation(arguments[0].value.boolValue ? m_userStyleSheetLocation : WebURL()); + result->setNull(); +} + +void LayoutTestController::setUserStyleSheetLocation(const CppArgumentList& arguments, CppVariant* result) +{ + if (arguments.size() > 0 && arguments[0].isString()) { + m_userStyleSheetLocation = webkit_support::RewriteLayoutTestsURL(arguments[0].toString()); + m_shell->webView()->settings()->setUserStyleSheetLocation(m_userStyleSheetLocation); + } + result->setNull(); +} + +void LayoutTestController::execCommand(const CppArgumentList& arguments, CppVariant* result) +{ + result->setNull(); + if (arguments.size() <= 0 || !arguments[0].isString()) + return; + + std::string command = arguments[0].toString(); + std::string value(""); + // Ignore the second parameter (which is userInterface) + // since this command emulates a manual action. + if (arguments.size() >= 3 && arguments[2].isString()) + value = arguments[2].toString(); + + // Note: webkit's version does not return the boolean, so neither do we. + m_shell->webView()->focusedFrame()->executeCommand(WebString::fromUTF8(command), WebString::fromUTF8(value)); +} + +void LayoutTestController::isCommandEnabled(const CppArgumentList& arguments, CppVariant* result) +{ + if (arguments.size() <= 0 || !arguments[0].isString()) { + result->setNull(); + return; + } + + std::string command = arguments[0].toString(); + bool rv = m_shell->webView()->focusedFrame()->isCommandEnabled(WebString::fromUTF8(command)); + result->set(rv); +} + +void LayoutTestController::setPopupBlockingEnabled(const CppArgumentList& arguments, CppVariant* result) +{ + if (arguments.size() > 0 && arguments[0].isBool()) { + bool blockPopups = arguments[0].toBoolean(); + m_shell->webView()->settings()->setJavaScriptCanOpenWindowsAutomatically(!blockPopups); + } + result->setNull(); +} + +void LayoutTestController::setUseDashboardCompatibilityMode(const CppArgumentList&, CppVariant* result) +{ + // We have no need to support Dashboard Compatibility Mode (mac-only) + result->setNull(); +} + +void LayoutTestController::setCustomPolicyDelegate(const CppArgumentList& arguments, CppVariant* result) +{ + if (arguments.size() > 0 && arguments[0].isBool()) { + bool enable = arguments[0].value.boolValue; + bool permissive = false; + if (arguments.size() > 1 && arguments[1].isBool()) + permissive = arguments[1].value.boolValue; + m_shell->webViewHost()->setCustomPolicyDelegate(enable, permissive); + } + result->setNull(); +} + +void LayoutTestController::waitForPolicyDelegate(const CppArgumentList&, CppVariant* result) +{ + m_shell->webViewHost()->waitForPolicyDelegate(); + m_waitUntilDone = true; + result->setNull(); +} + +void LayoutTestController::setWillSendRequestReturnsNullOnRedirect(const CppArgumentList& arguments, CppVariant* result) +{ + if (arguments.size() > 0 && arguments[0].isBool()) + m_shell->webViewHost()->setBlockRedirects(arguments[0].value.boolValue); + result->setNull(); +} + +void LayoutTestController::setWillSendRequestReturnsNull(const CppArgumentList& arguments, CppVariant* result) +{ + if (arguments.size() > 0 && arguments[0].isBool()) + m_shell->webViewHost()->setRequestReturnNull(arguments[0].value.boolValue); + result->setNull(); +} + +void LayoutTestController::pathToLocalResource(const CppArgumentList& arguments, CppVariant* result) +{ + result->setNull(); + if (arguments.size() <= 0 || !arguments[0].isString()) + return; + + string url = arguments[0].toString(); +#if OS(WINDOWS) + if (StartsWithASCII(url, "/tmp/", true)) { + // We want a temp file. + const unsigned tempPrefixLength = 5; + size_t bufferSize = MAX_PATH; + OwnArrayPtr tempPath(new WCHAR[bufferSize]); + DWORD tempLength = ::GetTempPathW(bufferSize, tempPath.get()); + if (tempLength + url.length() - tempPrefixLength + 1 > bufferSize) { + bufferSize = tempLength + url.length() - tempPrefixLength + 1; + tempPath.set(new WCHAR[bufferSize]); + tempLength = GetTempPathW(bufferSize, tempPath.get()); + ASSERT(tempLength < bufferSize); + } + std::string resultPath(WebString(tempPath.get(), tempLength).utf8()); + resultPath.append(url.substr(tempPrefixLength)); + result->set(resultPath); + return; + } +#endif + + // Some layout tests use file://// which we resolve as a UNC path. Normalize + // them to just file:///. + while (StartsWithASCII(url, "file:////", false)) + url = url.substr(0, 8) + url.substr(9); + result->set(webkit_support::RewriteLayoutTestsURL(url).spec()); +} + +void LayoutTestController::addFileToPasteboardOnDrag(const CppArgumentList&, CppVariant* result) +{ + result->setNull(); + m_shouldAddFileToPasteboard = true; +} + +void LayoutTestController::setStopProvisionalFrameLoads(const CppArgumentList&, CppVariant* result) +{ + result->setNull(); + m_stopProvisionalFrameLoads = true; +} + +void LayoutTestController::setSmartInsertDeleteEnabled(const CppArgumentList& arguments, CppVariant* result) +{ + if (arguments.size() > 0 && arguments[0].isBool()) + m_shell->webViewHost()->setSmartInsertDeleteEnabled(arguments[0].value.boolValue); + result->setNull(); +} + +void LayoutTestController::setSelectTrailingWhitespaceEnabled(const CppArgumentList& arguments, CppVariant* result) +{ + if (arguments.size() > 0 && arguments[0].isBool()) + m_shell->webViewHost()->setSelectTrailingWhitespaceEnabled(arguments[0].value.boolValue); + result->setNull(); +} + +bool LayoutTestController::pauseAnimationAtTimeOnElementWithId(const WebString& animationName, double time, const WebString& elementId) +{ + WebFrame* webFrame = m_shell->webView()->mainFrame(); + if (!webFrame) + return false; + + WebAnimationController* controller = webFrame->animationController(); + if (!controller) + return false; + + WebElement element = webFrame->document().getElementById(elementId); + if (element.isNull()) + return false; + return controller->pauseAnimationAtTime(element, animationName, time); +} + +bool LayoutTestController::pauseTransitionAtTimeOnElementWithId(const WebString& propertyName, double time, const WebString& elementId) +{ + WebFrame* webFrame = m_shell->webView()->mainFrame(); + if (!webFrame) + return false; + + WebAnimationController* controller = webFrame->animationController(); + if (!controller) + return false; + + WebElement element = webFrame->document().getElementById(elementId); + if (element.isNull()) + return false; + return controller->pauseTransitionAtTime(element, propertyName, time); +} + +bool LayoutTestController::elementDoesAutoCompleteForElementWithId(const WebString& elementId) +{ + WebFrame* webFrame = m_shell->webView()->mainFrame(); + if (!webFrame) + return false; + + WebElement element = webFrame->document().getElementById(elementId); + if (element.isNull() || !element.hasTagName("input")) + return false; + + WebInputElement inputElement = element.toElement(); + return inputElement.autoComplete(); +} + +int LayoutTestController::numberOfActiveAnimations() +{ + WebFrame* webFrame = m_shell->webView()->mainFrame(); + if (!webFrame) + return -1; + + WebAnimationController* controller = webFrame->animationController(); + if (!controller) + return -1; + + return controller->numberOfActiveAnimations(); +} + +void LayoutTestController::pauseAnimationAtTimeOnElementWithId(const CppArgumentList& arguments, CppVariant* result) +{ + result->set(false); + if (arguments.size() > 2 && arguments[0].isString() && arguments[1].isNumber() && arguments[2].isString()) { + WebString animationName = cppVariantToWebString(arguments[0]); + double time = arguments[1].toDouble(); + WebString elementId = cppVariantToWebString(arguments[2]); + result->set(pauseAnimationAtTimeOnElementWithId(animationName, time, elementId)); + } +} + +void LayoutTestController::pauseTransitionAtTimeOnElementWithId(const CppArgumentList& arguments, CppVariant* result) +{ + result->set(false); + if (arguments.size() > 2 && arguments[0].isString() && arguments[1].isNumber() && arguments[2].isString()) { + WebString propertyName = cppVariantToWebString(arguments[0]); + double time = arguments[1].toDouble(); + WebString elementId = cppVariantToWebString(arguments[2]); + result->set(pauseTransitionAtTimeOnElementWithId(propertyName, time, elementId)); + } +} + +void LayoutTestController::elementDoesAutoCompleteForElementWithId(const CppArgumentList& arguments, CppVariant* result) +{ + if (arguments.size() != 1 || !arguments[0].isString()) { + result->set(false); + return; + } + WebString elementId = cppVariantToWebString(arguments[0]); + result->set(elementDoesAutoCompleteForElementWithId(elementId)); +} + +void LayoutTestController::numberOfActiveAnimations(const CppArgumentList&, CppVariant* result) +{ + result->set(numberOfActiveAnimations()); +} + +void LayoutTestController::disableImageLoading(const CppArgumentList&, CppVariant* result) +{ + m_shell->webView()->settings()->setLoadsImagesAutomatically(false); + result->setNull(); +} + +void LayoutTestController::setIconDatabaseEnabled(const CppArgumentList&, CppVariant* result) +{ + // We don't use the WebKit icon database. + result->setNull(); +} + +// +// Unimplemented stubs +// + +void LayoutTestController::dumpAsWebArchive(const CppArgumentList& arguments, CppVariant* result) +{ + result->setNull(); +} + +void LayoutTestController::setMainFrameIsFirstResponder(const CppArgumentList& arguments, CppVariant* result) +{ + result->setNull(); +} + +void LayoutTestController::dumpSelectionRect(const CppArgumentList& arguments, CppVariant* result) +{ + m_dumpSelectionRect = true; + result->setNull(); +} + +void LayoutTestController::display(const CppArgumentList& arguments, CppVariant* result) +{ + WebViewHost* host = m_shell->webViewHost(); + const WebKit::WebSize& size = m_shell->webView()->size(); + WebRect rect(0, 0, size.width, size.height); + host->updatePaintRect(rect); + host->paintInvalidatedRegion(); + host->displayRepaintMask(); + result->setNull(); +} + +void LayoutTestController::testRepaint(const CppArgumentList&, CppVariant* result) +{ + m_testRepaint = true; + result->setNull(); +} + +void LayoutTestController::repaintSweepHorizontally(const CppArgumentList&, CppVariant* result) +{ + m_sweepHorizontally = true; + result->setNull(); +} + +void LayoutTestController::clearBackForwardList(const CppArgumentList& arguments, CppVariant* result) +{ + result->setNull(); +} + +void LayoutTestController::keepWebHistory(const CppArgumentList& arguments, CppVariant* result) +{ + result->setNull(); +} + +void LayoutTestController::storeWebScriptObject(const CppArgumentList& arguments, CppVariant* result) +{ + result->setNull(); +} + +void LayoutTestController::accessStoredWebScriptObject(const CppArgumentList& arguments, CppVariant* result) +{ + result->setNull(); +} + +void LayoutTestController::objCClassNameOf(const CppArgumentList& arguments, CppVariant* result) +{ + result->setNull(); +} + +void LayoutTestController::addDisallowedURL(const CppArgumentList& arguments, CppVariant* result) +{ + result->setNull(); +} +void LayoutTestController::setCallCloseOnWebViews(const CppArgumentList& arguments, CppVariant* result) +{ + result->setNull(); +} + +void LayoutTestController::setPrivateBrowsingEnabled(const CppArgumentList& arguments, CppVariant* result) +{ + result->setNull(); +} + +void LayoutTestController::setXSSAuditorEnabled(const CppArgumentList& arguments, CppVariant* result) +{ + if (arguments.size() > 0 && arguments[0].isBool()) + m_shell->webView()->settings()->setXSSAuditorEnabled(arguments[0].value.boolValue); + result->setNull(); +} + +void LayoutTestController::evaluateScriptInIsolatedWorld(const CppArgumentList& arguments, CppVariant* result) +{ + if (arguments.size() >= 2 && arguments[0].isNumber() && arguments[1].isString()) { + WebScriptSource source(WebString::fromUTF8(arguments[1].toString())); + // This relies on the iframe focusing itself when it loads. This is a bit + // sketchy, but it seems to be what other tests do. + m_shell->webView()->focusedFrame()->executeScriptInIsolatedWorld(arguments[0].toInt32(), &source, 1, 1); + } + result->setNull(); +} + +void LayoutTestController::setAllowUniversalAccessFromFileURLs(const CppArgumentList& arguments, CppVariant* result) +{ + if (arguments.size() > 0 && arguments[0].isBool()) + m_shell->webView()->settings()->setAllowUniversalAccessFromFileURLs(arguments[0].value.boolValue); + result->setNull(); +} + +void LayoutTestController::setAllowFileAccessFromFileURLs(const CppArgumentList& arguments, CppVariant* result) +{ + if (arguments.size() > 0 && arguments[0].isBool()) + m_shell->webView()->settings()->setAllowFileAccessFromFileURLs(arguments[0].value.boolValue); + result->setNull(); +} + +// Need these conversions because the format of the value for booleans +// may vary - for example, on mac "1" and "0" are used for boolean. +bool LayoutTestController::cppVariantToBool(const CppVariant& value) +{ + if (value.isBool()) + return value.toBoolean(); + if (value.isInt32()) + return value.toInt32(); + if (value.isString()) { + string valueString = value.toString(); + if (valueString == "true" || valueString == "1") + return true; + if (valueString == "false" || valueString == "0") + return false; + } + logErrorToConsole("Invalid value. Expected boolean value."); + return false; +} + +int32_t LayoutTestController::cppVariantToInt32(const CppVariant& value) +{ + if (value.isInt32()) + return value.toInt32(); + if (value.isString()) { + int number; + if (StringToInt(value.toString(), &number)) + return number; + } + logErrorToConsole("Invalid value for preference. Expected integer value."); + return 0; +} + +WebString LayoutTestController::cppVariantToWebString(const CppVariant& value) +{ + if (!value.isString()) { + logErrorToConsole("Invalid value for preference. Expected string value."); + return WebString(); + } + return WebString::fromUTF8(value.toString()); +} + +void LayoutTestController::overridePreference(const CppArgumentList& arguments, CppVariant* result) +{ + result->setNull(); + if (arguments.size() != 2 || !arguments[0].isString()) + return; + + string key = arguments[0].toString(); + CppVariant value = arguments[1]; + WebSettings* settings = m_shell->webView()->settings(); + if (key == "WebKitStandardFont") + settings->setStandardFontFamily(cppVariantToWebString(value)); + else if (key == "WebKitFixedFont") + settings->setFixedFontFamily(cppVariantToWebString(value)); + else if (key == "WebKitSerifFont") + settings->setSerifFontFamily(cppVariantToWebString(value)); + else if (key == "WebKitSansSerifFont") + settings->setSansSerifFontFamily(cppVariantToWebString(value)); + else if (key == "WebKitCursiveFont") + settings->setCursiveFontFamily(cppVariantToWebString(value)); + else if (key == "WebKitFantasyFont") + settings->setFantasyFontFamily(cppVariantToWebString(value)); + else if (key == "WebKitDefaultFontSize") + settings->setDefaultFontSize(cppVariantToInt32(value)); + else if (key == "WebKitDefaultFixedFontSize") + settings->setDefaultFixedFontSize(cppVariantToInt32(value)); + else if (key == "WebKitMinimumFontSize") + settings->setMinimumFontSize(cppVariantToInt32(value)); + else if (key == "WebKitMinimumLogicalFontSize") + settings->setMinimumLogicalFontSize(cppVariantToInt32(value)); + else if (key == "WebKitDefaultTextEncodingName") + settings->setDefaultTextEncodingName(cppVariantToWebString(value)); + else if (key == "WebKitJavaScriptEnabled") + settings->setJavaScriptEnabled(cppVariantToBool(value)); + else if (key == "WebKitWebSecurityEnabled") + settings->setWebSecurityEnabled(cppVariantToBool(value)); + else if (key == "WebKitJavaScriptCanOpenWindowsAutomatically") + settings->setJavaScriptCanOpenWindowsAutomatically(cppVariantToBool(value)); + else if (key == "WebKitDisplayImagesKey") + settings->setLoadsImagesAutomatically(cppVariantToBool(value)); + else if (key == "WebKitPluginsEnabled") + settings->setPluginsEnabled(cppVariantToBool(value)); + else if (key == "WebKitDOMPasteAllowedPreferenceKey") + settings->setDOMPasteAllowed(cppVariantToBool(value)); + else if (key == "WebKitDeveloperExtrasEnabledPreferenceKey") + settings->setDeveloperExtrasEnabled(cppVariantToBool(value)); + else if (key == "WebKitShrinksStandaloneImagesToFit") + settings->setShrinksStandaloneImagesToFit(cppVariantToBool(value)); + else if (key == "WebKitTextAreasAreResizable") + settings->setTextAreasAreResizable(cppVariantToBool(value)); + else if (key == "WebKitJavaEnabled") + settings->setJavaEnabled(cppVariantToBool(value)); + else if (key == "WebKitUsesPageCachePreferenceKey") + settings->setUsesPageCache(cppVariantToBool(value)); + else if (key == "WebKitXSSAuditorEnabled") + settings->setXSSAuditorEnabled(cppVariantToBool(value)); + else if (key == "WebKitLocalStorageEnabledPreferenceKey") + settings->setLocalStorageEnabled(cppVariantToBool(value)); + else if (key == "WebKitOfflineWebApplicationCacheEnabled") + settings->setOfflineWebApplicationCacheEnabled(cppVariantToBool(value)); + else if (key == "WebKitTabToLinksPreferenceKey") + m_shell->webView()->setTabsToLinks(cppVariantToBool(value)); + else if (key == "WebKitWebGLEnabled") + settings->setExperimentalWebGLEnabled(cppVariantToBool(value)); + else { + string message("Invalid name for preference: "); + message.append(key); + logErrorToConsole(message); + } +} + +void LayoutTestController::fallbackMethod(const CppArgumentList&, CppVariant* result) +{ + printf("CONSOLE MESSAGE: JavaScript ERROR: unknown method called on LayoutTestController\n"); + result->setNull(); +} + +void LayoutTestController::addOriginAccessWhitelistEntry(const CppArgumentList& arguments, CppVariant* result) +{ + result->setNull(); + + if (arguments.size() != 4 || !arguments[0].isString() || !arguments[1].isString() + || !arguments[2].isString() || !arguments[3].isBool()) + return; + + WebKit::WebURL url(GURL(arguments[0].toString())); + if (!url.isValid()) + return; + + WebSecurityPolicy::whiteListAccessFromOrigin(url, + WebString::fromUTF8(arguments[1].toString()), + WebString::fromUTF8(arguments[2].toString()), + arguments[3].toBoolean()); +} + +void LayoutTestController::clearAllDatabases(const CppArgumentList& arguments, CppVariant* result) +{ + result->setNull(); + webkit_support::ClearAllDatabases(); +} + +void LayoutTestController::setDatabaseQuota(const CppArgumentList& arguments, CppVariant* result) +{ + result->setNull(); + if ((arguments.size() >= 1) && arguments[0].isInt32()) + webkit_support::SetDatabaseQuota(arguments[0].toInt32()); +} + +void LayoutTestController::setPOSIXLocale(const CppArgumentList& arguments, CppVariant* result) +{ + result->setNull(); + if (arguments.size() == 1 && arguments[0].isString()) + setlocale(LC_ALL, arguments[0].toString().c_str()); +} + +void LayoutTestController::counterValueForElementById(const CppArgumentList& arguments, CppVariant* result) +{ + result->setNull(); + if (arguments.size() < 1 || !arguments[0].isString()) + return; + WebFrame* frame = m_shell->webView()->mainFrame(); + if (!frame) + return; + WebString counterValue = frame->counterValueForElementById(cppVariantToWebString(arguments[0])); + if (counterValue.isNull()) + return; + result->set(counterValue.utf8()); +} + +static bool parsePageSizeParameters(const CppArgumentList& arguments, + int argOffset, + float* pageWidthInPixels, + float* pageHeightInPixels) +{ + // WebKit is using the window width/height of DumpRenderTree as the + // default value of the page size. + // FIXME: share these values with other ports. + *pageWidthInPixels = 800; + *pageHeightInPixels = 600; + switch (arguments.size() - argOffset) { + case 2: + if (!arguments[argOffset].isNumber() || !arguments[1 + argOffset].isNumber()) + return false; + *pageWidthInPixels = static_cast(arguments[argOffset].toInt32()); + *pageHeightInPixels = static_cast(arguments[1 + argOffset].toInt32()); + // fall through. + case 0: + break; + default: + return false; + } + return true; +} + +void LayoutTestController::pageNumberForElementById(const CppArgumentList& arguments, CppVariant* result) +{ + result->setNull(); + float pageWidthInPixels = 0; + float pageHeightInPixels = 0; + if (!parsePageSizeParameters(arguments, 1, + &pageWidthInPixels, &pageHeightInPixels)) + return; + if (!arguments[0].isString()) + return; + WebFrame* frame = m_shell->webView()->mainFrame(); + if (!frame) + return; + result->set(frame->pageNumberForElementById(cppVariantToWebString(arguments[0]), + pageWidthInPixels, pageHeightInPixels)); +} + +void LayoutTestController::numberOfPages(const CppArgumentList& arguments, CppVariant* result) +{ + result->setNull(); + float pageWidthInPixels = 0; + float pageHeightInPixels = 0; + if (!parsePageSizeParameters(arguments, 0, &pageWidthInPixels, &pageHeightInPixels)) + return; + + WebFrame* frame = m_shell->webView()->mainFrame(); + if (!frame) + return; + WebSize size(pageWidthInPixels, pageHeightInPixels); + int numberOfPages = frame->printBegin(size); + frame->printEnd(); + result->set(numberOfPages); +} + +void LayoutTestController::logErrorToConsole(const std::string& text) +{ + m_shell->webViewHost()->didAddMessageToConsole( + WebConsoleMessage(WebConsoleMessage::LevelError, WebString::fromUTF8(text)), + WebString(), 0); +} + +void LayoutTestController::setTimelineProfilingEnabled(const CppArgumentList& arguments, CppVariant* result) +{ + result->setNull(); + if (arguments.size() < 1 || !arguments[0].isBool()) + return; + // FIXME: Should call TestShellDevToolsAgent::setTimelineProfilingEnabled(). +} + +void LayoutTestController::evaluateInWebInspector(const CppArgumentList& arguments, CppVariant* result) +{ + result->setNull(); + if (arguments.size() < 2 || !arguments[0].isInt32() || !arguments[1].isString()) + return; + // FIXME: Should call TestShellDevToolsAgent::evaluateInWebInspector(). +} + +void LayoutTestController::forceRedSelectionColors(const CppArgumentList& arguments, CppVariant* result) +{ + result->setNull(); + m_shell->webView()->setSelectionColors(0xffee0000, 0xff00ee00, 0xff000000, 0xffc0c0c0); +} + +void LayoutTestController::addUserScript(const CppArgumentList& arguments, CppVariant* result) +{ + result->setNull(); + if (arguments.size() < 1 || !arguments[0].isString() || !arguments[1].isBool()) + return; + m_shell->webView()->addUserScript(WebString::fromUTF8(arguments[0].toString()), arguments[1].toBoolean()); +} diff --git a/WebKitTools/DumpRenderTree/chromium/LayoutTestController.h b/WebKitTools/DumpRenderTree/chromium/LayoutTestController.h new file mode 100644 index 0000000..a8639da --- /dev/null +++ b/WebKitTools/DumpRenderTree/chromium/LayoutTestController.h @@ -0,0 +1,440 @@ +/* + * Copyright (C) 2010 Google Inc. All rights reserved. + * Copyright (C) 2010 Pawel Hajdan (phajdan.jr@chromium.org) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + LayoutTestController class: + Bound to a JavaScript window.layoutTestController object using the + CppBoundClass::bindToJavascript(), this allows layout tests that are run in + the test_shell (or, in principle, any web page loaded into a client app built + with this class) to control various aspects of how the tests are run and what + sort of output they produce. +*/ + +#ifndef LayoutTestController_h +#define LayoutTestController_h + +#include "CppBoundClass.h" +#include "base/timer.h" // FIXME: Remove this. +#include "public/WebString.h" +#include "public/WebURL.h" +#include + +class TestShell; + +class LayoutTestController : public CppBoundClass { +public: + // Builds the property and method lists needed to bind this class to a JS + // object. + LayoutTestController(TestShell*); + + // This function sets a flag that tells the test_shell to dump pages as + // plain text, rather than as a text representation of the renderer's state. + // It takes no arguments, and ignores any that may be present. + void dumpAsText(const CppArgumentList&, CppVariant*); + + // This function should set a flag that tells the test_shell to print a line + // of descriptive text for each database command. It should take no + // arguments, and ignore any that may be present. However, at the moment, we + // don't have any DB function that prints messages, so for now this function + // doesn't do anything. + void dumpDatabaseCallbacks(const CppArgumentList&, CppVariant*); + + // This function sets a flag that tells the test_shell to print a line of + // descriptive text for each editing command. It takes no arguments, and + // ignores any that may be present. + void dumpEditingCallbacks(const CppArgumentList&, CppVariant*); + + // This function sets a flag that tells the test_shell to print a line of + // descriptive text for each frame load callback. It takes no arguments, and + // ignores any that may be present. + void dumpFrameLoadCallbacks(const CppArgumentList&, CppVariant*); + + // This function sets a flag that tells the test_shell to print out a text + // representation of the back/forward list. It ignores all arguments. + void dumpBackForwardList(const CppArgumentList&, CppVariant*); + + // This function sets a flag that tells the test_shell to print out the + // scroll offsets of the child frames. It ignores all. + void dumpChildFrameScrollPositions(const CppArgumentList&, CppVariant*); + + // This function sets a flag that tells the test_shell to recursively + // dump all frames as plain text if the dumpAsText flag is set. + // It takes no arguments, and ignores any that may be present. + void dumpChildFramesAsText(const CppArgumentList&, CppVariant*); + + // This function sets a flag that tells the test_shell to dump all calls + // to window.status(). + // It takes no arguments, and ignores any that may be present. + void dumpWindowStatusChanges(const CppArgumentList&, CppVariant*); + + // When called with a boolean argument, this sets a flag that controls + // whether content-editable elements accept editing focus when an editing + // attempt is made. It ignores any additional arguments. + void setAcceptsEditing(const CppArgumentList&, CppVariant*); + + // Functions for dealing with windows. By default we block all new windows. + void windowCount(const CppArgumentList&, CppVariant*); + void setCanOpenWindows(const CppArgumentList&, CppVariant*); + void setCloseRemainingWindowsWhenComplete(const CppArgumentList&, CppVariant*); + + // By default, tests end when page load is complete. These methods are used + // to delay the completion of the test until notifyDone is called. + void waitUntilDone(const CppArgumentList&, CppVariant*); + void notifyDone(const CppArgumentList&, CppVariant*); + void notifyDoneTimedOut(); + + // Methods for adding actions to the work queue. Used in conjunction with + // waitUntilDone/notifyDone above. + void queueBackNavigation(const CppArgumentList&, CppVariant*); + void queueForwardNavigation(const CppArgumentList&, CppVariant*); + void queueReload(const CppArgumentList&, CppVariant*); + void queueLoadingScript(const CppArgumentList&, CppVariant*); + void queueNonLoadingScript(const CppArgumentList&, CppVariant*); + void queueLoad(const CppArgumentList&, CppVariant*); + + // Although this is named "objC" to match the Mac version, it actually tests + // the identity of its two arguments in C++. + void objCIdentityIsEqual(const CppArgumentList&, CppVariant*); + + // Changes the cookie policy from the default to allow all cookies. + void setAlwaysAcceptCookies(const CppArgumentList&, CppVariant*); + + // Gives focus to the window. + void setWindowIsKey(const CppArgumentList&, CppVariant*); + + // Method that controls whether pressing Tab key cycles through page elements + // or inserts a '\t' char in text area + void setTabKeyCyclesThroughElements(const CppArgumentList&, CppVariant*); + + // Passes through to WebPreferences which allows the user to have a custom + // style sheet. + void setUserStyleSheetEnabled(const CppArgumentList&, CppVariant*); + void setUserStyleSheetLocation(const CppArgumentList&, CppVariant*); + + // Puts Webkit in "dashboard compatibility mode", which is used in obscure + // Mac-only circumstances. It's not really necessary, and will most likely + // never be used by Chrome, but some layout tests depend on its presence. + void setUseDashboardCompatibilityMode(const CppArgumentList&, CppVariant*); + + // Causes navigation actions just printout the intended navigation instead + // of taking you to the page. This is used for cases like mailto, where you + // don't actually want to open the mail program. + void setCustomPolicyDelegate(const CppArgumentList&, CppVariant*); + + // Delays completion of the test until the policy delegate runs. + void waitForPolicyDelegate(const CppArgumentList&, CppVariant*); + + // Causes WillSendRequest to block redirects. + void setWillSendRequestReturnsNullOnRedirect(const CppArgumentList&, CppVariant*); + + // Causes WillSendRequest to return an empty request. + void setWillSendRequestReturnsNull(const CppArgumentList&, CppVariant*); + + // Converts a URL starting with file:///tmp/ to the local mapping. + void pathToLocalResource(const CppArgumentList&, CppVariant*); + + // Sets a bool such that when a drag is started, we fill the drag clipboard + // with a fake file object. + void addFileToPasteboardOnDrag(const CppArgumentList&, CppVariant*); + + // Executes an internal command (superset of document.execCommand() commands). + void execCommand(const CppArgumentList&, CppVariant*); + + // Checks if an internal command is currently available. + void isCommandEnabled(const CppArgumentList&, CppVariant*); + + // Set the WebPreference that controls webkit's popup blocking. + void setPopupBlockingEnabled(const CppArgumentList&, CppVariant*); + + // If true, causes provisional frame loads to be stopped for the remainder of + // the test. + void setStopProvisionalFrameLoads(const CppArgumentList&, CppVariant*); + + // Enable or disable smart insert/delete. This is enabled by default. + void setSmartInsertDeleteEnabled(const CppArgumentList&, CppVariant*); + + // Enable or disable trailing whitespace selection on double click. + void setSelectTrailingWhitespaceEnabled(const CppArgumentList&, CppVariant*); + + void pauseAnimationAtTimeOnElementWithId(const CppArgumentList&, CppVariant*); + void pauseTransitionAtTimeOnElementWithId(const CppArgumentList&, CppVariant*); + void elementDoesAutoCompleteForElementWithId(const CppArgumentList&, CppVariant*); + void numberOfActiveAnimations(const CppArgumentList&, CppVariant*); + + void disableImageLoading(const CppArgumentList&, CppVariant*); + + void setIconDatabaseEnabled(const CppArgumentList&, CppVariant*); + + void dumpSelectionRect(const CppArgumentList&, CppVariant*); + + // The following are only stubs. TODO(pamg): Implement any of these that + // are needed to pass the layout tests. + void dumpAsWebArchive(const CppArgumentList&, CppVariant*); + void dumpTitleChanges(const CppArgumentList&, CppVariant*); + void dumpResourceLoadCallbacks(const CppArgumentList&, CppVariant*); + void setMainFrameIsFirstResponder(const CppArgumentList&, CppVariant*); + void display(const CppArgumentList&, CppVariant*); + void testRepaint(const CppArgumentList&, CppVariant*); + void repaintSweepHorizontally(const CppArgumentList&, CppVariant*); + void clearBackForwardList(const CppArgumentList&, CppVariant*); + void keepWebHistory(const CppArgumentList&, CppVariant*); + void storeWebScriptObject(const CppArgumentList&, CppVariant*); + void accessStoredWebScriptObject(const CppArgumentList&, CppVariant*); + void objCClassNameOf(const CppArgumentList&, CppVariant*); + void addDisallowedURL(const CppArgumentList&, CppVariant*); + void setCallCloseOnWebViews(const CppArgumentList&, CppVariant*); + void setPrivateBrowsingEnabled(const CppArgumentList&, CppVariant*); + + void setXSSAuditorEnabled(const CppArgumentList&, CppVariant*); + void evaluateScriptInIsolatedWorld(const CppArgumentList&, CppVariant*); + void overridePreference(const CppArgumentList&, CppVariant*); + void setAllowUniversalAccessFromFileURLs(const CppArgumentList&, CppVariant*); + void setAllowFileAccessFromFileURLs(const CppArgumentList&, CppVariant*); + + + // The fallback method is called when a nonexistent method is called on + // the layout test controller object. + // It is usefull to catch typos in the JavaScript code (a few layout tests + // do have typos in them) and it allows the script to continue running in + // that case (as the Mac does). + void fallbackMethod(const CppArgumentList&, CppVariant*); + + // Allows layout tests to call SecurityOrigin::addOriginAccessWhitelistEntry(). + void addOriginAccessWhitelistEntry(const CppArgumentList&, CppVariant*); + + // Clears all databases. + void clearAllDatabases(const CppArgumentList&, CppVariant*); + // Sets the default quota for all origins + void setDatabaseQuota(const CppArgumentList&, CppVariant*); + + // Calls setlocale(LC_ALL, ...) for a specified locale. + // Resets between tests. + void setPOSIXLocale(const CppArgumentList&, CppVariant*); + + // Gets the value of the counter in the element specified by its ID. + void counterValueForElementById(const CppArgumentList&, CppVariant*); + + // Gets the number of page where the specified element will be put. + void pageNumberForElementById(const CppArgumentList&, CppVariant*); + + // Gets the number of pages to be printed. + void numberOfPages(const CppArgumentList&, CppVariant*); + + + // Allows layout tests to start Timeline profiling. + void setTimelineProfilingEnabled(const CppArgumentList&, CppVariant*); + + // Allows layout tests to exec scripts at WebInspector side. + void evaluateInWebInspector(const CppArgumentList&, CppVariant*); + + // Forces the selection colors for testing under Linux. + void forceRedSelectionColors(const CppArgumentList&, CppVariant*); + + // Adds a user script to be injected into new documents. + void addUserScript(const CppArgumentList&, CppVariant*); + +public: + // The following methods are not exposed to JavaScript. + void setWorkQueueFrozen(bool frozen) { m_workQueue.setFrozen(frozen); } + + bool shouldDumpAsText() { return m_dumpAsText; } + bool shouldDumpEditingCallbacks() { return m_dumpEditingCallbacks; } + bool shouldDumpFrameLoadCallbacks() { return m_dumpFrameLoadCallbacks; } + void setShouldDumpFrameLoadCallbacks(bool value) { m_dumpFrameLoadCallbacks = value; } + bool shouldDumpResourceLoadCallbacks() {return m_dumpResourceLoadCallbacks; } + bool shouldDumpStatusCallbacks() { return m_dumpWindowStatusChanges; } + bool shouldDumpSelectionRect() { return m_dumpSelectionRect; } + bool shouldDumpBackForwardList() { return m_dumpBackForwardList; } + bool shouldDumpTitleChanges() { return m_dumpTitleChanges; } + bool shouldDumpChildFrameScrollPositions() { return m_dumpChildFrameScrollPositions; } + bool shouldDumpChildFramesAsText() { return m_dumpChildFramesAsText; } + bool acceptsEditing() { return m_acceptsEditing; } + bool canOpenWindows() { return m_canOpenWindows; } + bool shouldAddFileToPasteboard() { return m_shouldAddFileToPasteboard; } + bool stopProvisionalFrameLoads() { return m_stopProvisionalFrameLoads; } + + bool testRepaint() const { return m_testRepaint; } + bool sweepHorizontally() const { return m_sweepHorizontally; } + + // Called by the webview delegate when the toplevel frame load is done. + void locationChangeDone(); + + // Called by the webview delegate when the policy delegate runs if the + // waitForPolicyDelegate was called. + void policyDelegateDone(); + + // Reinitializes all static values. The reset() method should be called + // before the start of each test (currently from + // TestShell::runFileTest). + void reset(); + + // A single item in the work queue. + class WorkItem { + public: + virtual ~WorkItem() {} + + // Returns true if this started a load. + virtual bool run(TestShell* shell) = 0; + }; + +private: + friend class WorkItem; + friend class WorkQueue; + + // Helper class for managing events queued by methods like queueLoad or + // queueScript. + class WorkQueue { + public: + WorkQueue(LayoutTestController* controller) : m_controller(controller) {} + virtual ~WorkQueue(); + void processWorkSoon(); + + // Reset the state of the class between tests. + void reset(); + + void addWork(WorkItem* work); + + void setFrozen(bool frozen) { m_frozen = frozen; } + bool isEmpty() { return m_queue.isEmpty(); } + + private: + void processWork(); + + base::OneShotTimer m_timer; + Deque m_queue; + bool m_frozen; + LayoutTestController* m_controller; + }; + + // Support for overridePreference. + bool cppVariantToBool(const CppVariant&); + int32_t cppVariantToInt32(const CppVariant&); + WebKit::WebString cppVariantToWebString(const CppVariant&); + + void logErrorToConsole(const std::string&); + void completeNotifyDone(bool isTimeout); + + bool pauseAnimationAtTimeOnElementWithId(const WebKit::WebString& animationName, double time, const WebKit::WebString& elementId); + bool pauseTransitionAtTimeOnElementWithId(const WebKit::WebString& propertyName, double time, const WebKit::WebString& elementId); + bool elementDoesAutoCompleteForElementWithId(const WebKit::WebString&); + int numberOfActiveAnimations(); + + // Used for test timeouts. + ScopedRunnableMethodFactory m_timeoutFactory; + + // Non-owning pointer. The LayoutTestController is owned by the host. + TestShell* m_shell; + + // If true, the test_shell will produce a plain text dump rather than a + // text representation of the renderer. + bool m_dumpAsText; + + // If true, the test_shell will write a descriptive line for each editing + // command. + bool m_dumpEditingCallbacks; + + // If true, the test_shell will draw the bounds of the current selection rect + // taking possible transforms of the selection rect into account. + bool m_dumpSelectionRect; + + // If true, the test_shell will output a descriptive line for each frame + // load callback. + bool m_dumpFrameLoadCallbacks; + + // If true, the test_shell will output a descriptive line for each resource + // load callback. + bool m_dumpResourceLoadCallbacks; + + // If true, the test_shell will produce a dump of the back forward list as + // well. + bool m_dumpBackForwardList; + + // If true, the test_shell will print out the child frame scroll offsets as + // well. + bool m_dumpChildFrameScrollPositions; + + // If true and if dump_as_text_ is true, the test_shell will recursively + // dump all frames as plain text. + bool m_dumpChildFramesAsText; + + // If true, the test_shell will dump all changes to window.status. + bool m_dumpWindowStatusChanges; + + // If true, output a message when the page title is changed. + bool m_dumpTitleChanges; + + // If true, the element will be treated as editable. This value is returned + // from various editing callbacks that are called just before edit operations + // are allowed. + bool m_acceptsEditing; + + // If true, new windows can be opened via javascript or by plugins. By + // default, set to false and can be toggled to true using + // setCanOpenWindows(). + bool m_canOpenWindows; + + // When reset is called, go through and close all but the main test shell + // window. By default, set to true but toggled to false using + // setCloseRemainingWindowsWhenComplete(). + bool m_closeRemainingWindows; + + // If true, pixel dump will be produced as a series of 1px-tall, view-wide + // individual paints over the height of the view. + bool m_testRepaint; + // If true and test_repaint_ is true as well, pixel dump will be produced as + // a series of 1px-wide, view-tall paints across the width of the view. + bool m_sweepHorizontally; + + // If true and a drag starts, adds a file to the drag&drop clipboard. + bool m_shouldAddFileToPasteboard; + + // If true, stops provisional frame loads during the + // DidStartProvisionalLoadForFrame callback. + bool m_stopProvisionalFrameLoads; + + // If true, don't dump output until notifyDone is called. + bool m_waitUntilDone; + + // To prevent infinite loops, only the first page of a test can add to a + // work queue (since we may well come back to that same page). + bool m_workQueueFrozen; + + WorkQueue m_workQueue; + + CppVariant m_globalFlag; + + // Bound variable counting the number of top URLs visited. + CppVariant m_webHistoryItemCount; + + WebKit::WebURL m_userStyleSheetLocation; +}; + +#endif // LayoutTestController_h diff --git a/WebKitTools/DumpRenderTree/chromium/LayoutTestHelper.mm b/WebKitTools/DumpRenderTree/chromium/LayoutTestHelper.mm new file mode 100644 index 0000000..e34cf5f --- /dev/null +++ b/WebKitTools/DumpRenderTree/chromium/LayoutTestHelper.mm @@ -0,0 +1,118 @@ +/* + * Copyright (C) 2010 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#import +#include +#include +#include + +// This is a simple helper app that changes the color sync profile to the +// generic profile and back when done. This program is managed by the layout +// test script, so it can do the job for multiple DumpRenderTree while they are +// running layout tests. + +static CMProfileRef userColorProfile = 0; + +static void saveCurrentColorProfile() +{ + CGDirectDisplayID displayID = CGMainDisplayID(); + CMProfileRef previousProfile; + CMError error = CMGetProfileByAVID((UInt32)displayID, &previousProfile); + if (error) { + NSLog(@"failed to get the current color profile, pixmaps won't match. " + @"Error: %d", (int)error); + } else { + userColorProfile = previousProfile; + } +} + +static void installLayoutTestColorProfile() +{ + // To make sure we get consistent colors (not dependent on the Main display), + // we force the generic rgb color profile. This cases a change the user can + // see. + + CGDirectDisplayID displayID = CGMainDisplayID(); + NSColorSpace* genericSpace = [NSColorSpace genericRGBColorSpace]; + CMProfileRef genericProfile = (CMProfileRef)[genericSpace colorSyncProfile]; + CMError error = CMSetProfileByAVID((UInt32)displayID, genericProfile); + if (error) { + NSLog(@"failed install the generic color profile, pixmaps won't match. " + @"Error: %d", (int)error); + } +} + +static void restoreUserColorProfile(void) +{ + if (!userColorProfile) + return; + CGDirectDisplayID displayID = CGMainDisplayID(); + CMError error = CMSetProfileByAVID((UInt32)displayID, userColorProfile); + CMCloseProfile(userColorProfile); + if (error) { + NSLog(@"Failed to restore color profile, use System Preferences -> " + @"Displays -> Color to reset. Error: %d", (int)error); + } + userColorProfile = 0; +} + +static void simpleSignalHandler(int sig) +{ + // Try to restore the color profile and try to go down cleanly + restoreUserColorProfile(); + exit(128 + sig); +} + +int main(int argc, char* argv[]) +{ + NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; + + // Hooks the ways we might get told to clean up... + signal(SIGINT, simpleSignalHandler); + signal(SIGHUP, simpleSignalHandler); + signal(SIGTERM, simpleSignalHandler); + + // Save off the current profile, and then install the layout test profile. + saveCurrentColorProfile(); + installLayoutTestColorProfile(); + + // Let the script know we're ready + printf("ready\n"); + fflush(stdout); + + // Wait for any key (or signal) + getchar(); + + // Restore the profile + restoreUserColorProfile(); + + [pool release]; + return 0; +} diff --git a/WebKitTools/DumpRenderTree/chromium/MockSpellCheck.cpp b/WebKitTools/DumpRenderTree/chromium/MockSpellCheck.cpp new file mode 100644 index 0000000..fe70cab --- /dev/null +++ b/WebKitTools/DumpRenderTree/chromium/MockSpellCheck.cpp @@ -0,0 +1,149 @@ +/* + * Copyright (C) 2010 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "MockSpellCheck.h" + +#include "public/WebString.h" +#include +#include + +using namespace WebCore; +using namespace WebKit; + +MockSpellCheck::MockSpellCheck() + : m_initialized(false) {} + +MockSpellCheck::~MockSpellCheck() {} + +static bool isNotASCIIAlpha(UChar ch) { return !isASCIIAlpha(ch); } + +bool MockSpellCheck::spellCheckWord(const WebString& text, int* misspelledOffset, int* misspelledLength) +{ + ASSERT(misspelledOffset); + ASSERT(misspelledLength); + + // Initialize this spellchecker. + initializeIfNeeded(); + + // Reset the result values as our spellchecker does. + *misspelledOffset = 0; + *misspelledLength = 0; + + // Convert to a String because we store String instances in + // m_misspelledWords and WebString has no find(). + const String stringText(text.data(), text.length()); + + // Extract the first possible English word from the given string. + // The given string may include non-ASCII characters or numbers. So, we + // should filter out such characters before start looking up our + // misspelled-word table. + // (This is a simple version of our SpellCheckWordIterator class.) + // If the given string doesn't include any ASCII characters, we can treat the + // string as valid one. + // Unfortunately, This implementation splits a contraction, i.e. "isn't" is + // split into two pieces "isn" and "t". This is OK because webkit tests + // don't have misspelled contractions. + int wordOffset = stringText.find(isASCIIAlpha); + if (wordOffset == -1) + return true; + int wordEnd = stringText.find(isNotASCIIAlpha, wordOffset); + int wordLength = wordEnd == -1 ? stringText.length() - wordOffset : wordEnd - wordOffset; + + // Look up our misspelled-word table to check if the extracted word is a + // known misspelled word, and return the offset and the length of the + // extracted word if this word is a known misspelled word. + // (See the comment in MockSpellCheck::initializeIfNeeded() why we use a + // misspelled-word table.) + String word = stringText.substring(wordOffset, wordLength); + if (!m_misspelledWords.contains(word)) + return true; + + *misspelledOffset = wordOffset; + *misspelledLength = wordLength; + return false; +} + +bool MockSpellCheck::initializeIfNeeded() +{ + // Exit if we have already initialized this object. + if (m_initialized) + return false; + + // Create a table that consists of misspelled words used in WebKit layout + // tests. + // Since WebKit layout tests don't have so many misspelled words as + // well-spelled words, it is easier to compare the given word with misspelled + // ones than to compare with well-spelled ones. + static const char* misspelledWords[] = { + // These words are known misspelled words in webkit tests. + // If there are other misspelled words in webkit tests, please add them in + // this array. + "foo", + "Foo", + "baz", + "fo", + "LibertyF", + "chello", + "xxxtestxxx", + "XXxxx", + "Textx", + "blockquoted", + "asd", + "Lorem", + "Nunc", + "Curabitur", + "eu", + "adlj", + "adaasj", + "sdklj", + "jlkds", + "jsaada", + "jlda", + "zz", + "contentEditable", + // The following words are used by unit tests. + "ifmmp", + "qwertyuiopasd", + "qwertyuiopasdf", + }; + + m_misspelledWords.clear(); + for (size_t i = 0; i < arraysize(misspelledWords); ++i) + m_misspelledWords.add(String::fromUTF8(misspelledWords[i]), false); + + // Mark as initialized to prevent this object from being initialized twice + // or more. + m_initialized = true; + + // Since this MockSpellCheck class doesn't download dictionaries, this + // function always returns false. + return false; +} diff --git a/WebKitTools/DumpRenderTree/chromium/MockSpellCheck.h b/WebKitTools/DumpRenderTree/chromium/MockSpellCheck.h new file mode 100644 index 0000000..8c2ba92 --- /dev/null +++ b/WebKitTools/DumpRenderTree/chromium/MockSpellCheck.h @@ -0,0 +1,82 @@ +/* + * Copyright (C) 2010 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef MockSpellCheck_h +#define MockSpellCheck_h + +#include +#include +#include + +namespace WebKit { +class WebString; +} + +// A mock implementation of a spell-checker used for WebKit tests. +// This class only implements the minimal functionarities required by WebKit +// tests, i.e. this class just compares the given string with known misspelled +// words in webkit tests and mark them as missspelled. +// Even though this is sufficent for webkit tests, this class is not suitable +// for any other usages. +class MockSpellCheck { +public: + MockSpellCheck(); + ~MockSpellCheck(); + + // Checks the spellings of the specified text. + // This function returns true if the text consists of valid words, and + // returns false if it includes invalid words. + // When the given text includes invalid words, this function sets the + // position of the first invalid word to misspelledOffset, and the length of + // the first invalid word to misspelledLength, respectively. + // For example, when the given text is " zz zz", this function sets 3 to + // misspelledOffset and 2 to misspelledLength, respectively. + bool spellCheckWord(const WebKit::WebString& text, + int* misspelledOffset, + int* misspelledLength); + +private: + // Initialize the internal resources if we need to initialize it. + // Initializing this object may take long time. To prevent from hurting + // the performance of test_shell, we initialize this object when + // SpellCheckWord() is called for the first time. + // To be compliant with SpellCheck:InitializeIfNeeded(), this function + // returns true if this object is downloading a dictionary, otherwise + // it returns false. + bool initializeIfNeeded(); + + // A table that consists of misspelled words. + HashMap m_misspelledWords; + + // A flag representing whether or not this object is initialized. + bool m_initialized; +}; + +#endif // MockSpellCheck_h diff --git a/WebKitTools/DumpRenderTree/chromium/PlainTextController.cpp b/WebKitTools/DumpRenderTree/chromium/PlainTextController.cpp new file mode 100644 index 0000000..6e6cf11 --- /dev/null +++ b/WebKitTools/DumpRenderTree/chromium/PlainTextController.cpp @@ -0,0 +1,79 @@ +/* + * Copyright (C) 2010 Google Inc. All rights reserved. + * Copyright (C) 2009 Pawel Hajdan (phajdan.jr@chromium.org) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "PlainTextController.h" + +#include "TestShell.h" +#include "public/WebBindings.h" +#include "public/WebRange.h" +#include "public/WebString.h" + +using namespace WebKit; + +PlainTextController::PlainTextController() +{ + // Initialize the map that associates methods of this class with the names + // they will use when called by JavaScript. The actual binding of those + // names to their methods will be done by calling bindToJavaScript() (defined + // by CppBoundClass, the parent to PlainTextController). + bindMethod("plainText", &PlainTextController::plainText); + + // The fallback method is called when an unknown method is invoked. + bindFallbackMethod(&PlainTextController::fallbackMethod); +} + +void PlainTextController::plainText(const CppArgumentList& arguments, CppVariant* result) +{ + result->setNull(); + + if (arguments.size() < 1 || !arguments[0].isObject()) + return; + + // Check that passed-in object is, in fact, a range. + NPObject* npobject = NPVARIANT_TO_OBJECT(arguments[0]); + if (!npobject) + return; + WebRange range; + if (!WebBindings::getRange(npobject, &range)) + return; + + // Extract the text using the Range's text() method + WebString text = range.toPlainText(); + result->set(text.utf8()); +} + +void PlainTextController::fallbackMethod(const CppArgumentList&, CppVariant* result) +{ + printf("CONSOLE MESSAGE: JavaScript ERROR: unknown method called on PlainTextController\n"); + result->setNull(); +} + diff --git a/WebKitTools/DumpRenderTree/chromium/PlainTextController.h b/WebKitTools/DumpRenderTree/chromium/PlainTextController.h new file mode 100644 index 0000000..3d3a04c --- /dev/null +++ b/WebKitTools/DumpRenderTree/chromium/PlainTextController.h @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2010 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef PlainTextController_h +#define PlainTextController_h + +#include "CppBoundClass.h" + +class TestShell; + +class PlainTextController : public CppBoundClass { +public: + // Builds the property and method lists needed to bind this class to a JS + // object. + explicit PlainTextController(); + + // JS callback methods. + void plainText(const CppArgumentList&, CppVariant*); + + // Fall-back method: called if an unknown method is invoked. + void fallbackMethod(const CppArgumentList&, CppVariant*); +}; + +#endif // PlainTextController_h + diff --git a/WebKitTools/DumpRenderTree/chromium/TestNavigationController.cpp b/WebKitTools/DumpRenderTree/chromium/TestNavigationController.cpp new file mode 100644 index 0000000..8b4f954 --- /dev/null +++ b/WebKitTools/DumpRenderTree/chromium/TestNavigationController.cpp @@ -0,0 +1,269 @@ +/* + * Copyright (C) 2010 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "TestNavigationController.h" + +#include "TestShell.h" +#include + +using namespace WebKit; +using namespace std; + +// ---------------------------------------------------------------------------- +// TestNavigationEntry + +TestNavigationEntry::TestNavigationEntry() + : m_pageID(-1) {} + +TestNavigationEntry::TestNavigationEntry( + int pageID, const WebURL& url, const WebString& title, const WebString& targetFrame) + : m_pageID(pageID) + , m_url(url) + , m_title(title) + , m_targetFrame(targetFrame) {} + +TestNavigationEntry::~TestNavigationEntry() {} + +void TestNavigationEntry::setContentState(const WebHistoryItem& state) +{ + m_state = state; +} + +// ---------------------------------------------------------------------------- +// TestNavigationController + +TestNavigationController::TestNavigationController(NavigationHost* host) + : m_pendingEntry(0) + , m_lastCommittedEntryIndex(-1) + , m_pendingEntryIndex(-1) + , m_host(host) + , m_maxPageID(-1) {} + +TestNavigationController::~TestNavigationController() +{ + discardPendingEntry(); +} + +void TestNavigationController::reset() +{ + m_entries.clear(); + discardPendingEntry(); + + m_lastCommittedEntryIndex = -1; +} + +void TestNavigationController::reload() +{ + // Base the navigation on where we are now... + int currentIndex = currentEntryIndex(); + + // If we are no where, then we can't reload. TODO(darin): We should add a + // CanReload method. + if (currentIndex == -1) + return; + + discardPendingEntry(); + + m_pendingEntryIndex = currentIndex; + navigateToPendingEntry(true); +} + +void TestNavigationController::goToOffset(int offset) +{ + int index = m_lastCommittedEntryIndex + offset; + if (index < 0 || index >= entryCount()) + return; + + goToIndex(index); +} + +void TestNavigationController::goToIndex(int index) +{ + ASSERT(index >= 0); + ASSERT(index < static_cast(m_entries.size())); + + discardPendingEntry(); + + m_pendingEntryIndex = index; + navigateToPendingEntry(false); +} + +void TestNavigationController::loadEntry(TestNavigationEntry* entry) +{ + // When navigating to a new page, we don't know for sure if we will actually + // end up leaving the current page. The new page load could for example + // result in a download or a 'no content' response (e.g., a mailto: URL). + discardPendingEntry(); + m_pendingEntry = entry; + navigateToPendingEntry(false); +} + + +TestNavigationEntry* TestNavigationController::lastCommittedEntry() const +{ + if (m_lastCommittedEntryIndex == -1) + return 0; + return m_entries[m_lastCommittedEntryIndex].get(); +} + +TestNavigationEntry* TestNavigationController::activeEntry() const +{ + TestNavigationEntry* entry = m_pendingEntry; + if (!entry) + entry = lastCommittedEntry(); + return entry; +} + +int TestNavigationController::currentEntryIndex() const +{ + if (m_pendingEntryIndex != -1) + return m_pendingEntryIndex; + return m_lastCommittedEntryIndex; +} + + +TestNavigationEntry* TestNavigationController::entryAtIndex(int index) const +{ + if (index < 0 || index >= entryCount()) + return 0; + return m_entries[index].get(); +} + +TestNavigationEntry* TestNavigationController::entryWithPageID(int32_t pageID) const +{ + int index = entryIndexWithPageID(pageID); + return (index != -1) ? m_entries[index].get() : 0; +} + +void TestNavigationController::didNavigateToEntry(TestNavigationEntry* entry) +{ + // If the entry is that of a page with PageID larger than any this Tab has + // seen before, then consider it a new navigation. + if (entry->pageID() > maxPageID()) { + insertEntry(entry); + return; + } + + // Otherwise, we just need to update an existing entry with matching PageID. + // If the existing entry corresponds to the entry which is pending, then we + // must update the current entry index accordingly. When navigating to the + // same URL, a new PageID is not created. + + int existingEntryIndex = entryIndexWithPageID(entry->pageID()); + TestNavigationEntry* existingEntry = (existingEntryIndex != -1) ? + m_entries[existingEntryIndex].get() : 0; + if (!existingEntry) { + // No existing entry, then simply ignore this navigation! + } else if (existingEntry == m_pendingEntry) { + // The given entry might provide a new URL... e.g., navigating back to a + // page in session history could have resulted in a new client redirect. + existingEntry->setURL(entry->URL()); + existingEntry->setContentState(entry->contentState()); + m_lastCommittedEntryIndex = m_pendingEntryIndex; + m_pendingEntryIndex = -1; + m_pendingEntry = 0; + } else if (m_pendingEntry && m_pendingEntry->pageID() == -1 + && GURL(m_pendingEntry->URL()) == GURL(existingEntry->URL().spec())) { + // Not a new navigation + discardPendingEntry(); + } else { + // The given entry might provide a new URL... e.g., navigating to a page + // might result in a client redirect, which should override the URL of the + // existing entry. + existingEntry->setURL(entry->URL()); + existingEntry->setContentState(entry->contentState()); + + // The navigation could have been issued by the renderer, so be sure that + // we update our current index. + m_lastCommittedEntryIndex = existingEntryIndex; + } + + delete entry; + updateMaxPageID(); +} + +void TestNavigationController::discardPendingEntry() +{ + if (m_pendingEntryIndex == -1) + delete m_pendingEntry; + m_pendingEntry = 0; + m_pendingEntryIndex = -1; +} + +void TestNavigationController::insertEntry(TestNavigationEntry* entry) +{ + discardPendingEntry(); + + // Prune any entry which are in front of the current entry + int currentSize = static_cast(m_entries.size()); + if (currentSize > 0) { + while (m_lastCommittedEntryIndex < (currentSize - 1)) { + m_entries.removeLast(); + currentSize--; + } + } + + m_entries.append(linked_ptr(entry)); + m_lastCommittedEntryIndex = static_cast(m_entries.size()) - 1; + updateMaxPageID(); +} + +int TestNavigationController::entryIndexWithPageID(int32 pageID) const +{ + for (int i = static_cast(m_entries.size()) - 1; i >= 0; --i) { + if (m_entries[i]->pageID() == pageID) + return i; + } + return -1; +} + +void TestNavigationController::navigateToPendingEntry(bool reload) +{ + // For session history navigations only the pending_entry_index_ is set. + if (!m_pendingEntry) { + ASSERT(m_pendingEntryIndex != -1); + m_pendingEntry = m_entries[m_pendingEntryIndex].get(); + } + + if (m_host->navigate(*m_pendingEntry, reload)) { + // Note: this is redundant if navigation completed synchronously because + // DidNavigateToEntry call this as well. + updateMaxPageID(); + } else + discardPendingEntry(); +} + +void TestNavigationController::updateMaxPageID() +{ + TestNavigationEntry* entry = activeEntry(); + if (entry) + m_maxPageID = max(m_maxPageID, entry->pageID()); +} diff --git a/WebKitTools/DumpRenderTree/chromium/TestNavigationController.h b/WebKitTools/DumpRenderTree/chromium/TestNavigationController.h new file mode 100644 index 0000000..bd3c2f4 --- /dev/null +++ b/WebKitTools/DumpRenderTree/chromium/TestNavigationController.h @@ -0,0 +1,204 @@ +/* + * Copyright (C) 2010 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef TestNavigationController_h +#define TestNavigationController_h + +#include "base/basictypes.h" +#include "base/linked_ptr.h" +#include "public/WebDataSource.h" +#include "public/WebHistoryItem.h" +#include "public/WebString.h" +#include "public/WebURL.h" +#include +#include + +// Associated with browser-initated navigations to hold tracking data. +class TestShellExtraData : public WebKit::WebDataSource::ExtraData { +public: + TestShellExtraData(int32_t pendingPageID) + : pendingPageID(pendingPageID) + , requestCommitted(false) {} + + // Contains the page_id for this navigation or -1 if there is none yet. + int32_t pendingPageID; + + // True if we have already processed the "DidCommitLoad" event for this + // request. Used by session history. + bool requestCommitted; +}; + +// Stores one back/forward navigation state for the test shell. +class TestNavigationEntry: public Noncopyable { +public: + TestNavigationEntry(); + TestNavigationEntry(int pageID, + const WebKit::WebURL&, + const WebKit::WebString& title, + const WebKit::WebString& targetFrame); + + // Virtual to allow test_shell to extend the class. + ~TestNavigationEntry(); + + // Set / Get the URI + void setURL(const WebKit::WebURL& url) { m_url = url; } + const WebKit::WebURL& URL() const { return m_url; } + + // Set / Get the title + void setTitle(const WebKit::WebString& title) { m_title = title; } + const WebKit::WebString& title() const { return m_title; } + + // Set / Get a state. + void setContentState(const WebKit::WebHistoryItem&); + const WebKit::WebHistoryItem& contentState() const { return m_state; } + + // Get the page id corresponding to the tab's state. + void setPageID(int pageID) { m_pageID = pageID; } + int32_t pageID() const { return m_pageID; } + + const WebKit::WebString& targetFrame() const { return m_targetFrame; } + +private: + // Describes the current page that the tab represents. This is not relevant + // for all tab contents types. + int32_t m_pageID; + + WebKit::WebURL m_url; + WebKit::WebString m_title; + WebKit::WebHistoryItem m_state; + WebKit::WebString m_targetFrame; +}; + +class NavigationHost { +public: + virtual bool navigate(const TestNavigationEntry&, bool reload) = 0; +}; + +// Test shell's NavigationController. The goal is to be as close to the Chrome +// version as possible. +class TestNavigationController: public Noncopyable { +public: + TestNavigationController(NavigationHost*); + ~TestNavigationController(); + + void reset(); + + // Causes the controller to reload the current (or pending) entry. + void reload(); + + // Causes the controller to go to the specified offset from current. Does + // nothing if out of bounds. + void goToOffset(int); + + // Causes the controller to go to the specified index. + void goToIndex(int); + + // Causes the controller to load the specified entry. The controller + // assumes ownership of the entry. + // NOTE: Do not pass an entry that the controller already owns! + void loadEntry(TestNavigationEntry*); + + // Returns the last committed entry, which may be null if there are no + // committed entries. + TestNavigationEntry* lastCommittedEntry() const; + + // Returns the number of entries in the NavigationControllerBase, excluding + // the pending entry if there is one. + int entryCount() const { return static_cast(m_entries.size()); } + + // Returns the active entry, which is the pending entry if a navigation is in + // progress or the last committed entry otherwise. NOTE: This can be 0!! + // + // If you are trying to get the current state of the NavigationControllerBase, + // this is the method you will typically want to call. + TestNavigationEntry* activeEntry() const; + + // Returns the index from which we would go back/forward or reload. This is + // the m_lastCommittedEntryIndex if m_pendingEntryIndex is -1. Otherwise, + // it is the m_pendingEntryIndex. + int currentEntryIndex() const; + + // Returns the entry at the specified index. Returns 0 if out of + // bounds. + TestNavigationEntry* entryAtIndex(int) const; + + // Return the entry with the corresponding type and page ID, or 0 if + // not found. + TestNavigationEntry* entryWithPageID(int32_t) const; + + // Returns the index of the last committed entry. + int lastCommittedEntryIndex() const { return m_lastCommittedEntryIndex; } + + // Used to inform us of a navigation being committed for a tab. We will take + // ownership of the entry. Any entry located forward to the current entry will + // be deleted. The new entry becomes the current entry. + void didNavigateToEntry(TestNavigationEntry*); + + // Used to inform us to discard its pending entry. + void discardPendingEntry(); + +private: + // Inserts an entry after the current position, removing all entries after it. + // The new entry will become the active one. + void insertEntry(TestNavigationEntry*); + + int maxPageID() const { return m_maxPageID; } + void navigateToPendingEntry(bool reload); + + // Return the index of the entry with the corresponding type and page ID, + // or -1 if not found. + int entryIndexWithPageID(int32_t) const; + + // Updates the max page ID with that of the given entry, if is larger. + void updateMaxPageID(); + + // List of NavigationEntry for this tab + typedef Vector > NavigationEntryList; + typedef NavigationEntryList::iterator NavigationEntryListIterator; + NavigationEntryList m_entries; + + // An entry we haven't gotten a response for yet. This will be discarded + // when we navigate again. It's used only so we know what the currently + // displayed tab is. + TestNavigationEntry* m_pendingEntry; + + // currently visible entry + int m_lastCommittedEntryIndex; + + // index of pending entry if it is in entries_, or -1 if pending_entry_ is a + // new entry (created by LoadURL). + int m_pendingEntryIndex; + + NavigationHost* m_host; + int m_maxPageID; +}; + +#endif // TestNavigationController_h + diff --git a/WebKitTools/DumpRenderTree/chromium/TestShell.cpp b/WebKitTools/DumpRenderTree/chromium/TestShell.cpp new file mode 100644 index 0000000..d2bc110 --- /dev/null +++ b/WebKitTools/DumpRenderTree/chromium/TestShell.cpp @@ -0,0 +1,614 @@ +/* + * Copyright (C) 2010 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "TestShell.h" + +#include "LayoutTestController.h" +#include "WebViewHost.h" +#include "base/md5.h" // FIXME: Wrap by webkit_support. +#include "base/string16.h" +#include "gfx/codec/png_codec.h" // FIXME: Remove dependecy. WebCore/platform/image-encoder is better? +#include "net/base/escape.h" // FIXME: Remove dependency. +#include "public/WebDataSource.h" +#include "public/WebDocument.h" +#include "public/WebElement.h" +#include "public/WebFrame.h" +#include "public/WebHistoryItem.h" +#include "public/WebRuntimeFeatures.h" +#include "public/WebScriptController.h" +#include "public/WebSettings.h" +#include "public/WebSize.h" +#include "public/WebString.h" +#include "public/WebURLRequest.h" +#include "public/WebURLResponse.h" +#include "public/WebView.h" +#include "skia/ext/bitmap_platform_device.h" +#include "skia/ext/platform_canvas.h" +#include "webkit/support/webkit_support.h" +#include +#include +#include + +using namespace WebKit; +using namespace std; + +// Content area size for newly created windows. +static const int testWindowWidth = 800; +static const int testWindowHeight = 600; + +// The W3C SVG layout tests use a different size than the other layout tests. +static const int SVGTestWindowWidth = 480; +static const int SVGTestWindowHeight = 360; + +static const char layoutTestsPattern[] = "/LayoutTests/"; +static const string::size_type layoutTestsPatternSize = sizeof(layoutTestsPattern) - 1; +static const char fileUrlPattern[] = "file:/"; +static const char fileTestPrefix[] = "(file test):"; +static const char dataUrlPattern[] = "data:"; +static const string::size_type dataUrlPatternSize = sizeof(dataUrlPattern) - 1; + +TestShell::TestShell() + : m_testIsPending(false) + , m_testIsPreparing(false) + , m_focusedWidget(0) +{ + m_accessibilityController.set(new AccessibilityController(this)); + m_layoutTestController.set(new LayoutTestController(this)); + m_eventSender.set(new EventSender(this)); + m_plainTextController.set(new PlainTextController()); + m_textInputController.set(new TextInputController(this)); + + m_webViewHost = createWebView(); + m_webView = m_webViewHost->webView(); +} + +TestShell::~TestShell() +{ + loadURL(GURL("about:blank")); + // Call GC twice to clean up garbage. + callJSGC(); + callJSGC(); + + // Destroy the WebView before its WebViewHost. + m_webView->close(); +} + +void TestShell::resetWebSettings(WebView& webView) +{ + // Match the settings used by Mac DumpRenderTree, with the exception of + // fonts. + WebSettings* settings = webView.settings(); +#if OS(MAC_OS_X) + WebString serif = WebString::fromUTF8("Times"); + settings->setCursiveFontFamily(WebString::fromUTF8("Apple Chancery")); + settings->setFantasyFontFamily(WebString::fromUTF8("Papyrus")); +#else + // NOTE: case matters here, this must be 'times new roman', else + // some layout tests fail. + WebString serif = WebString::fromUTF8("times new roman"); + + // These two fonts are picked from the intersection of + // Win XP font list and Vista font list : + // http://www.microsoft.com/typography/fonts/winxp.htm + // http://blogs.msdn.com/michkap/archive/2006/04/04/567881.aspx + // Some of them are installed only with CJK and complex script + // support enabled on Windows XP and are out of consideration here. + // (although we enabled both on our buildbots.) + // They (especially Impact for fantasy) are not typical cursive + // and fantasy fonts, but it should not matter for layout tests + // as long as they're available. + settings->setCursiveFontFamily(WebString::fromUTF8("Comic Sans MS")); + settings->setFantasyFontFamily(WebString::fromUTF8("Impact")); +#endif + settings->setSerifFontFamily(serif); + settings->setStandardFontFamily(serif); + settings->setFixedFontFamily(WebString::fromUTF8("Courier")); + + settings->setDefaultTextEncodingName(WebString::fromUTF8("ISO-8859-1")); + settings->setDefaultFontSize(16); + settings->setDefaultFixedFontSize(13); + settings->setMinimumFontSize(1); + settings->setMinimumLogicalFontSize(9); + settings->setJavaScriptCanOpenWindowsAutomatically(true); + settings->setDOMPasteAllowed(true); + settings->setDeveloperExtrasEnabled(false); + settings->setNeedsSiteSpecificQuirks(true); + settings->setShrinksStandaloneImagesToFit(false); + settings->setUsesEncodingDetector(false); + settings->setTextAreasAreResizable(false); + settings->setJavaEnabled(false); + settings->setAllowScriptsToCloseWindows(false); + settings->setXSSAuditorEnabled(false); + settings->setDownloadableBinaryFontsEnabled(true); + settings->setLocalStorageEnabled(true); + settings->setOfflineWebApplicationCacheEnabled(true); + settings->setAllowFileAccessFromFileURLs(true); + + // LayoutTests were written with Safari Mac in mind which does not allow + // tabbing to links by default. + webView.setTabsToLinks(false); + + // Allow those layout tests running as local files, i.e. under + // LayoutTests/http/tests/local, to access http server. + settings->setAllowUniversalAccessFromFileURLs(true); + + settings->setJavaScriptEnabled(true); + settings->setPluginsEnabled(true); + settings->setWebSecurityEnabled(true); + settings->setEditableLinkBehaviorNeverLive(); + settings->setFontRenderingModeNormal(); + settings->setShouldPaintCustomScrollbars(true); + settings->setTextDirectionSubmenuInclusionBehaviorNeverIncluded(); + + settings->setLoadsImagesAutomatically(true); + settings->setImagesEnabled(true); +} + +void TestShell::runFileTest(const TestParams& params) +{ + m_testIsPreparing = true; + m_params = params; + string testUrl = m_params.testUrl.spec(); + + bool inspectorTestMode = testUrl.find("/inspector/") != string::npos + || testUrl.find("\\inspector\\") != string::npos; + m_webView->settings()->setDeveloperExtrasEnabled(inspectorTestMode); + loadURL(m_params.testUrl); + + m_testIsPreparing = false; + waitTestFinished(); +} + +static inline bool isSVGTestURL(const WebURL& url) +{ + return url.isValid() && string(url.spec()).find("W3C-SVG-1.1") != string::npos; +} + +void TestShell::resizeWindowForTest(WebViewHost* window, const WebURL& url) +{ + int width, height; + if (isSVGTestURL(url)) { + width = SVGTestWindowWidth; + height = SVGTestWindowHeight; + } else { + width = testWindowWidth; + height = testWindowHeight; + } + window->setWindowRect(WebRect(0, 0, width + virtualWindowBorder * 2, height + virtualWindowBorder * 2)); +} + +void TestShell::resetTestController() +{ + m_accessibilityController->reset(); + m_layoutTestController->reset(); + m_eventSender->reset(); + m_webViewHost->reset(); +} + +void TestShell::loadURL(const WebURL& url) +{ + m_webViewHost->loadURLForFrame(url, WebString()); +} + +void TestShell::reload() +{ + m_webViewHost->navigationController()->reload(); +} + +void TestShell::goToOffset(int offset) +{ + m_webViewHost->navigationController()->goToOffset(offset); +} + +int TestShell::navigationEntryCount() const +{ + return m_webViewHost->navigationController()->entryCount(); +} + +void TestShell::callJSGC() +{ + m_webView->mainFrame()->collectGarbage(); +} + +void TestShell::setFocus(WebWidget* widget, bool enable) +{ + // Simulate the effects of InteractiveSetFocus(), which includes calling + // both setFocus() and setIsActive(). + if (enable) { + if (m_focusedWidget != widget) { + if (m_focusedWidget) + m_focusedWidget->setFocus(false); + webView()->setIsActive(enable); + widget->setFocus(enable); + m_focusedWidget = widget; + } + } else { + if (m_focusedWidget == widget) { + widget->setFocus(enable); + webView()->setIsActive(enable); + m_focusedWidget = 0; + } + } +} + +void TestShell::testFinished() +{ + if (!m_testIsPending) + return; + m_testIsPending = false; + dump(); + webkit_support::QuitMessageLoop(); +} + +void TestShell::testTimedOut() +{ + fprintf(stderr, "FAIL: Timed out waiting for notifyDone to be called\n"); + fprintf(stdout, "FAIL: Timed out waiting for notifyDone to be called\n"); + testFinished(); +} + +static string dumpDocumentText(WebFrame* frame) +{ + // We use the document element's text instead of the body text here because + // not all documents have a body, such as XML documents. + WebElement documentElement = frame->document().documentElement(); + if (documentElement.isNull()) + return string(); + return documentElement.innerText().utf8(); +} + +static string dumpFramesAsText(WebFrame* frame, bool recursive) +{ + string result; + + // Add header for all but the main frame. Skip empty frames. + if (frame->parent() && !frame->document().documentElement().isNull()) { + result.append("\n--------\nFrame: '"); + result.append(frame->name().utf8().data()); + result.append("'\n--------\n"); + } + + result.append(dumpDocumentText(frame)); + result.append("\n"); + + if (recursive) { + for (WebFrame* child = frame->firstChild(); child; child = child->nextSibling()) + result.append(dumpFramesAsText(child, recursive)); + } + + return result; +} + +static void dumpFrameScrollPosition(WebFrame* frame, bool recursive) +{ + WebSize offset = frame->scrollOffset(); + if (offset.width > 0 || offset.height > 0) { + if (frame->parent()) + printf("frame '%s' ", frame->name().utf8().data()); + printf("scrolled to %d,%d\n", offset.width, offset.height); + } + + if (!recursive) + return; + for (WebFrame* child = frame->firstChild(); child; child = child->nextSibling()) + dumpFrameScrollPosition(child, recursive); +} + +struct ToLower { + char16 operator()(char16 c) { return tolower(c); } +}; + +// FIXME: Eliminate std::transform(), std::vector, and std::sort(). + +// Returns True if item1 < item2. +static bool HistoryItemCompareLess(const WebHistoryItem& item1, const WebHistoryItem& item2) +{ + string16 target1 = item1.target(); + string16 target2 = item2.target(); + std::transform(target1.begin(), target1.end(), target1.begin(), ToLower()); + std::transform(target2.begin(), target2.end(), target2.begin(), ToLower()); + return target1 < target2; +} + +static string dumpHistoryItem(const WebHistoryItem& item, int indent, bool isCurrent) +{ + string result; + + if (isCurrent) { + result.append("curr->"); + result.append(indent - 6, ' '); // 6 == "curr->".length() + } else { + result.append(indent, ' '); + } + + string url = item.urlString().utf8(); + size_t pos; + if (!url.find(fileUrlPattern) && ((pos = url.find(layoutTestsPattern)) != string::npos)) { + // adjust file URLs to match upstream results. + url.replace(0, pos + layoutTestsPatternSize, fileTestPrefix); + } else if (!url.find(dataUrlPattern)) { + // URL-escape data URLs to match results upstream. + string path = EscapePath(url.substr(dataUrlPatternSize)); + url.replace(dataUrlPatternSize, url.length(), path); + } + + result.append(url); + if (!item.target().isEmpty()) { + result.append(" (in frame \""); + result.append(item.target().utf8()); + result.append("\")"); + } + if (item.isTargetItem()) + result.append(" **nav target**"); + result.append("\n"); + + const WebVector& children = item.children(); + if (!children.isEmpty()) { + // Must sort to eliminate arbitrary result ordering which defeats + // reproducible testing. + // FIXME: WebVector should probably just be a std::vector!! + std::vector sortedChildren; + for (size_t i = 0; i < children.size(); ++i) + sortedChildren.push_back(children[i]); + std::sort(sortedChildren.begin(), sortedChildren.end(), HistoryItemCompareLess); + for (size_t i = 0; i < sortedChildren.size(); ++i) + result += dumpHistoryItem(sortedChildren[i], indent + 4, false); + } + + return result; +} + +static void dumpBackForwardList(const TestNavigationController& navigationController, string& result) +{ + result.append("\n============== Back Forward List ==============\n"); + for (int index = 0; index < navigationController.entryCount(); ++index) { + int currentIndex = navigationController.lastCommittedEntryIndex(); + WebHistoryItem historyItem = navigationController.entryAtIndex(index)->contentState(); + if (historyItem.isNull()) { + historyItem.initialize(); + historyItem.setURLString(navigationController.entryAtIndex(index)->URL().spec().utf16()); + } + result.append(dumpHistoryItem(historyItem, 8, index == currentIndex)); + } + result.append("===============================================\n"); +} + +string TestShell::dumpAllBackForwardLists() +{ + string result; + for (unsigned i = 0; i < m_windowList.size(); ++i) + dumpBackForwardList(*m_windowList[i]->navigationController(), result); + return result; +} + +void TestShell::dump() +{ + WebScriptController::flushConsoleMessages(); + + // Dump the requested representation. + WebFrame* frame = m_webView->mainFrame(); + if (!frame) + return; + bool shouldDumpAsText = m_layoutTestController->shouldDumpAsText(); + bool dumpedAnything = false; + if (m_params.dumpTree) { + dumpedAnything = true; + printf("Content-Type: text/plain\n"); + // Text output: the test page can request different types of output + // which we handle here. + if (!shouldDumpAsText) { + // Plain text pages should be dumped as text + string mimeType = frame->dataSource()->response().mimeType().utf8(); + shouldDumpAsText = mimeType == "text/plain"; + } + if (shouldDumpAsText) { + bool recursive = m_layoutTestController->shouldDumpChildFramesAsText(); + string dataUtf8 = dumpFramesAsText(frame, recursive); + if (fwrite(dataUtf8.c_str(), 1, dataUtf8.size(), stdout) != dataUtf8.size()) + FATAL("Short write to stdout, disk full?\n"); + } else { + printf("%s", frame->renderTreeAsText().utf8().data()); + bool recursive = m_layoutTestController->shouldDumpChildFrameScrollPositions(); + dumpFrameScrollPosition(frame, recursive); + } + if (m_layoutTestController->shouldDumpBackForwardList()) + printf("%s", dumpAllBackForwardLists().c_str()); + } + if (dumpedAnything && m_params.printSeparators) + printf("#EOF\n"); + + if (m_params.dumpPixels && !shouldDumpAsText) { + // Image output: we write the image data to the file given on the + // command line (for the dump pixels argument), and the MD5 sum to + // stdout. + dumpedAnything = true; + m_webView->layout(); + if (m_layoutTestController->testRepaint()) { + WebSize viewSize = m_webView->size(); + int width = viewSize.width; + int height = viewSize.height; + if (m_layoutTestController->sweepHorizontally()) { + for (WebRect column(0, 0, 1, height); column.x < width; column.x++) + m_webViewHost->paintRect(column); + } else { + for (WebRect line(0, 0, width, 1); line.y < height; line.y++) + m_webViewHost->paintRect(line); + } + } else + m_webViewHost->paintInvalidatedRegion(); + + // See if we need to draw the selection bounds rect. Selection bounds + // rect is the rect enclosing the (possibly transformed) selection. + // The rect should be drawn after everything is laid out and painted. + if (m_layoutTestController->shouldDumpSelectionRect()) { + // If there is a selection rect - draw a red 1px border enclosing rect + WebRect wr = frame->selectionBoundsRect(); + if (!wr.isEmpty()) { + // Render a red rectangle bounding selection rect + SkPaint paint; + paint.setColor(0xFFFF0000); // Fully opaque red + paint.setStyle(SkPaint::kStroke_Style); + paint.setFlags(SkPaint::kAntiAlias_Flag); + paint.setStrokeWidth(1.0f); + SkIRect rect; // Bounding rect + rect.set(wr.x, wr.y, wr.x + wr.width, wr.y + wr.height); + m_webViewHost->canvas()->drawIRect(rect, paint); + } + } + + string md5sum = dumpImage(m_webViewHost->canvas(), m_params.pixelHash); + } + printf("#EOF\n"); // For the image. + fflush(stdout); + fflush(stderr); +} + +string TestShell::dumpImage(skia::PlatformCanvas* canvas, const string& expectedHash) +{ + skia::BitmapPlatformDevice& device = + static_cast(canvas->getTopPlatformDevice()); + const SkBitmap& sourceBitmap = device.accessBitmap(false); + + SkAutoLockPixels sourceBitmapLock(sourceBitmap); + + // Fix the alpha. The expected PNGs on Mac have an alpha channel, so we want + // to keep it. On Windows, the alpha channel is wrong since text/form control + // drawing may have erased it in a few places. So on Windows we force it to + // opaque and also don't write the alpha channel for the reference. Linux + // doesn't have the wrong alpha like Windows, but we ignore it anyway. +#if OS(WINDOWS) + bool discardTransparency = true; + device.makeOpaque(0, 0, sourceBitmap.width(), sourceBitmap.height()); +#elif OS(MAC_OS_X) + bool discardTransparency = false; +#elif OS(UNIX) + bool discardTransparency = true; +#endif + + // Compute MD5 sum. We should have done this before calling + // device.makeOpaque on Windows. Because we do it after the call, there are + // some images that are the pixel identical on windows and other platforms + // but have different MD5 sums. At this point, rebaselining all the windows + // tests is too much of a pain, so we just check in different baselines. + MD5Context ctx; + MD5Init(&ctx); + MD5Update(&ctx, sourceBitmap.getPixels(), sourceBitmap.getSize()); + + MD5Digest digest; + MD5Final(&digest, &ctx); + string md5hash = MD5DigestToBase16(digest); + printf("\nActualHash: %s\n", md5hash.c_str()); + if (!expectedHash.empty()) + printf("\nExpectedHash: %s\n", expectedHash.c_str()); + + // Only encode and dump the png if the hashes don't match. Encoding the image + // is really expensive. + if (md5hash.compare(expectedHash)) { + std::vector png; + gfx::PNGCodec::ColorFormat colorFormat = gfx::PNGCodec::FORMAT_BGRA; + gfx::PNGCodec::Encode( + reinterpret_cast(sourceBitmap.getPixels()), + colorFormat, sourceBitmap.width(), sourceBitmap.height(), + static_cast(sourceBitmap.rowBytes()), discardTransparency, &png); + + printf("Content-Type: image/png\n"); + printf("Content-Length: %u\n", png.size()); + // Write to disk. + if (fwrite(&png[0], 1, png.size(), stdout) != png.size()) + FATAL("Short write to stdout.\n"); + } + + return md5hash; +} + +void TestShell::bindJSObjectsToWindow(WebFrame* frame) +{ + m_accessibilityController->bindToJavascript(frame, WebString::fromUTF8("accessibilityController")); + m_layoutTestController->bindToJavascript(frame, WebString::fromUTF8("layoutTestController")); + m_eventSender->bindToJavascript(frame, WebString::fromUTF8("eventSender")); + m_plainTextController->bindToJavascript(frame, WebString::fromUTF8("plainText")); + m_textInputController->bindToJavascript(frame, WebString::fromUTF8("textInputController")); +} + +int TestShell::layoutTestTimeout() +{ + return 10 * 1000; +} + +WebViewHost* TestShell::createWebView() +{ + return createNewWindow(WebURL()); +} + +WebViewHost* TestShell::createNewWindow(const WebURL& url) +{ + WebViewHost* host = new WebViewHost(this); + WebView* view = WebView::create(host); + host->setWebWidget(view); + resetWebSettings(*view); + view->initializeMainFrame(host); + m_windowList.append(host); + host->loadURLForFrame(url, WebString()); + return host; +} + +void TestShell::closeWindow(WebViewHost* window) +{ + size_t i = m_windowList.find(window); + if (i == notFound) { + ASSERT_NOT_REACHED(); + return; + } + m_windowList.remove(i); + window->webWidget()->close(); + delete window; +} + +void TestShell::closeRemainingWindows() +{ + // Iterate through the window list and close everything except the main + // ihwindow. We don't want to delete elements as we're iterating, so we copy + // to a temp vector first. + Vector windowsToDelete; + for (unsigned i = 0; i < m_windowList.size(); ++i) { + if (m_windowList[i] != webViewHost()) + windowsToDelete.append(m_windowList[i]); + } + ASSERT(windowsToDelete.size() + 1 == m_windowList.size()); + for (unsigned i = 0; i < windowsToDelete.size(); ++i) + closeWindow(windowsToDelete[i]); + ASSERT(m_windowList.size() == 1); +} + +int TestShell::windowCount() +{ + return m_windowList.size(); +} diff --git a/WebKitTools/DumpRenderTree/chromium/TestShell.h b/WebKitTools/DumpRenderTree/chromium/TestShell.h new file mode 100644 index 0000000..c6a5b2e --- /dev/null +++ b/WebKitTools/DumpRenderTree/chromium/TestShell.h @@ -0,0 +1,149 @@ +/* + * Copyright (C) 2010 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "AccessibilityController.h" +#include "EventSender.h" +#include "LayoutTestController.h" +#include "PlainTextController.h" +#include "TextInputController.h" +#include "WebViewHost.h" +#include +#include +#include + +// TestShell is a container of global variables and has bridge functions between +// various objects. Only one instance is created in one DRT process. + +namespace WebKit { +class WebFrame; +class WebPreferences; +class WebView; +class WebURL; +} +namespace skia { +class PlatformCanvas; +} + +struct TestParams { + bool dumpTree; + bool dumpPixels; + bool printSeparators; + WebKit::WebURL testUrl; + std::string pixelFileName; + std::string pixelHash; + + TestParams() + : dumpTree(true) + , dumpPixels(false) + , printSeparators(false) {} +}; + +class TestShell { +public: + TestShell(); + ~TestShell(); + // The main WebView. + WebKit::WebView* webView() const { return m_webView; } + // Returns the host for the main WebView. + WebViewHost* webViewHost() const { return m_webViewHost; } + LayoutTestController* layoutTestController() const { return m_layoutTestController.get(); } + AccessibilityController* accessibilityController() const { return m_accessibilityController.get(); } + + void bindJSObjectsToWindow(WebKit::WebFrame*); + void runFileTest(const TestParams&); + void callJSGC(); + void resetTestController(); + void waitTestFinished(); + + // Operations to the main window. + void loadURL(const WebKit::WebURL& url); + void reload(); + void goToOffset(int offset); + int navigationEntryCount() const; + + void setFocus(WebKit::WebWidget*, bool enable); + bool shouldDumpFrameLoadCallbacks() const { return (m_testIsPreparing || m_testIsPending) && layoutTestController()->shouldDumpFrameLoadCallbacks(); } + bool shouldDumpResourceLoadCallbacks() const { return (m_testIsPreparing || m_testIsPending) && layoutTestController()->shouldDumpResourceLoadCallbacks(); } + void setIsLoading(bool flag) { m_isLoading = flag; } + + // Called by the LayoutTestController to signal test completion. + void testFinished(); + // Called by LayoutTestController when a test hits the timeout, but does not + // cause a hang. We can avoid killing TestShell in this case and still dump + // the test results. + void testTimedOut(); + +#if defined(OS_WIN) + // Access to the finished event. Used by the static WatchDog thread. + HANDLE finishedEvent() { return m_finishedEvent; } +#endif + + // Get the timeout for running a test in milliseconds. + static int layoutTestTimeout(); + static int layoutTestTimeoutForWatchDog() { return layoutTestTimeout() + 1000; } + + WebViewHost* createWebView(); + WebViewHost* createNewWindow(const WebKit::WebURL&); + void closeWindow(WebViewHost*); + void closeRemainingWindows(); + int windowCount(); + static void resizeWindowForTest(WebViewHost*, const WebKit::WebURL&); + + static const int virtualWindowBorder = 3; + +private: + static void resetWebSettings(WebKit::WebView&); + void dump(); + std::string dumpAllBackForwardLists(); + static std::string dumpImage(skia::PlatformCanvas*, const std::string& expectedHash); + + bool m_testIsPending; + bool m_testIsPreparing; + bool m_isLoading; + WebKit::WebView* m_webView; + WebKit::WebWidget* m_focusedWidget; + WebViewHost* m_webViewHost; + OwnPtr m_accessibilityController; + OwnPtr m_eventSender; + OwnPtr m_layoutTestController; + OwnPtr m_plainTextController; + OwnPtr m_textInputController; + TestParams m_params; + + // List of all windows in this process. + // The main window should be put into windowList[0]. + typedef Vector WindowList; + WindowList m_windowList; + +#if defined(OS_WIN) + // Used by the watchdog to know when it's finished. + HANDLE m_finishedEvent; +#endif +}; diff --git a/WebKitTools/DumpRenderTree/chromium/TestShellGtk.cpp b/WebKitTools/DumpRenderTree/chromium/TestShellGtk.cpp new file mode 100644 index 0000000..d71881a --- /dev/null +++ b/WebKitTools/DumpRenderTree/chromium/TestShellGtk.cpp @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2010 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "TestShell.h" + +#include "webkit/support/webkit_support.h" +#include + +static void AlarmHandler(int signatl) +{ + // If the alarm alarmed, kill the process since we have a really bad hang. + puts("\n#TEST_TIMED_OUT\n"); + puts("#EOF\n"); + fflush(stdout); + exit(0); +} + +void TestShell::waitTestFinished() +{ + ASSERT(!m_testIsPending); + + m_testIsPending = true; + + // Install an alarm signal handler that will kill us if we time out. + signal(SIGALRM, AlarmHandler); + alarm(layoutTestTimeoutForWatchDog() / 1000); + + // TestFinished() will post a quit message to break this loop when the page + // finishes loading. + while (m_testIsPending) + webkit_support::RunMessageLoop(); + + // Remove the alarm. + alarm(0); + signal(SIGALRM, SIG_DFL); +} diff --git a/WebKitTools/DumpRenderTree/chromium/TestShellMac.mm b/WebKitTools/DumpRenderTree/chromium/TestShellMac.mm new file mode 100644 index 0000000..ec8dbac --- /dev/null +++ b/WebKitTools/DumpRenderTree/chromium/TestShellMac.mm @@ -0,0 +1,125 @@ +/* + * Copyright (C) 2010 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" + +#include "TestShell.h" +#include "webkit/support/webkit_support.h" +#import + +// A class to be the target/selector of the "watchdog" thread that ensures +// pages timeout if they take too long and tells the test harness via stdout. +@interface WatchDogTarget : NSObject { +@private + NSTimeInterval _timeout; +} +// |timeout| is in seconds +- (id)initWithTimeout:(NSTimeInterval)timeout; +// serves as the "run" method of a NSThread. +- (void)run:(id)sender; +@end + +@implementation WatchDogTarget + +- (id)initWithTimeout:(NSTimeInterval)timeout +{ + if ((self = [super init])) + _timeout = timeout; + return self; +} + +- (void)run:(id)ignore +{ + NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; + + // check for debugger, just bail if so. We don't want the timeouts hitting + // when we're trying to track down an issue. + if (webkit_support::BeingDebugged()) + return; + + NSThread* currentThread = [NSThread currentThread]; + + // Wait to be cancelled. If we are that means the test finished. If it hasn't, + // then we need to tell the layout script we timed out and start again. + NSDate* limitDate = [NSDate dateWithTimeIntervalSinceNow:_timeout]; + while ([(NSDate*)[NSDate date] compare:limitDate] == NSOrderedAscending && + ![currentThread isCancelled]) { + // sleep for a small increment then check again + NSDate* incrementDate = [NSDate dateWithTimeIntervalSinceNow:1.0]; + [NSThread sleepUntilDate:incrementDate]; + } + if (![currentThread isCancelled]) { + // Print a warning to be caught by the layout-test script. + // Note: the layout test driver may or may not recognize + // this as a timeout. + puts("#TEST_TIMED_OUT\n"); + puts("#EOF\n"); + fflush(stdout); + exit(0); + } + + [pool release]; +} + +@end + +void TestShell::waitTestFinished() +{ + ASSERT(!m_testIsPending); + + m_testIsPending = true; + + // Create a watchdog thread which just sets a timer and + // kills the process if it times out. This catches really + // bad hangs where the shell isn't coming back to the + // message loop. If the watchdog is what catches a + // timeout, it can't do anything except terminate the test + // shell, which is unfortunate. + // Windows multiplies by 2.5, but that causes us to run for far, far too + // long. We use the passed value and let the scripts flag override + // the value as needed. + NSTimeInterval timeoutSeconds = layoutTestTimeoutForWatchDog() / 1000; + WatchDogTarget* watchdog = [[[WatchDogTarget alloc] + initWithTimeout:timeoutSeconds] autorelease]; + NSThread* thread = [[NSThread alloc] initWithTarget:watchdog + selector:@selector(run:) + object:nil]; + [thread start]; + + // TestFinished() will post a quit message to break this loop when the page + // finishes loading. + while (m_testIsPending) + webkit_support::RunMessageLoop(); + + // Tell the watchdog that we're finished. No point waiting to re-join, it'll + // die on its own. + [thread cancel]; + [thread release]; +} diff --git a/WebKitTools/DumpRenderTree/chromium/TestShellWin.cpp b/WebKitTools/DumpRenderTree/chromium/TestShellWin.cpp new file mode 100644 index 0000000..2d806a2 --- /dev/null +++ b/WebKitTools/DumpRenderTree/chromium/TestShellWin.cpp @@ -0,0 +1,100 @@ +/* + * Copyright (C) 2010 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "TestShell.h" + +#include "webkit/support/webkit_support.h" +#include + +// Default timeout in ms for file page loads when in layout test mode. +const int kDefaultFileTestTimeoutMillisecs = 10 * 1000; +const int kDefaultWatchDogTimeoutMillisecs = kDefaultFileTestTimeoutMillisecs + 1 * 1000; + +// Thread main to run for the thread which just tests for timeout. +unsigned int __stdcall watchDogThread(void *arg) +{ + // If we're debugging a layout test, don't timeout. + if (::IsDebuggerPresent()) + return 0; + + TestShell* shell = static_cast(arg); + // FIXME: Do we need user-specified time settings as with the original + // Chromium implementation? + DWORD timeout = static_cast(kDefaultWatchDogTimeoutMillisecs); + DWORD rv = WaitForSingleObject(shell->finishedEvent(), timeout); + if (rv == WAIT_TIMEOUT) { + // Print a warning to be caught by the layout-test script. + // Note: the layout test driver may or may not recognize + // this as a timeout. + puts("#TEST_TIMED_OUT\n"); + puts("#EOF\n"); + fflush(stdout); + TerminateProcess(GetCurrentProcess(), 0); + } + // Finished normally. + return 0; +} + +void TestShell::waitTestFinished() +{ + DCHECK(!m_testIsPending) << "cannot be used recursively"; + + m_testIsPending = true; + + // Create a watchdog thread which just sets a timer and + // kills the process if it times out. This catches really + // bad hangs where the shell isn't coming back to the + // message loop. If the watchdog is what catches a + // timeout, it can't do anything except terminate the test + // shell, which is unfortunate. + m_finishedEvent = CreateEvent(0, TRUE, FALSE, 0); + DCHECK(m_finishedEvent); + + HANDLE threadHandle = reinterpret_cast(_beginthreadex( + 0, + 0, + &watchDogThread, + this, + 0, + 0)); + DCHECK(threadHandle); + + // TestFinished() will post a quit message to break this loop when the page + // finishes loading. + while (m_testIsPending) + webkit_support::RunMessageLoop(); + + // Tell the watchdog that we are finished. + SetEvent(m_finishedEvent); + + // Wait to join the watchdog thread. (up to 1s, then quit) + WaitForSingleObject(threadHandle, 1000); +} diff --git a/WebKitTools/DumpRenderTree/chromium/TestWebWorker.h b/WebKitTools/DumpRenderTree/chromium/TestWebWorker.h new file mode 100644 index 0000000..899514e --- /dev/null +++ b/WebKitTools/DumpRenderTree/chromium/TestWebWorker.h @@ -0,0 +1,88 @@ +/* + * Copyright (C) 2010 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef TestWebWorker_h +#define TestWebWorker_h + +#include "public/WebMessagePortChannel.h" +#include "public/WebWorker.h" +#include "public/WebWorkerClient.h" +#include + +namespace WebKit { +class WebNotificationPresenter; +class WebString; +class WebURL; +} + +class TestWebWorker : public WebKit::WebWorker, + public WebKit::WebWorkerClient, + public WTF::RefCounted { +public: + TestWebWorker() + { + ref(); + // The initial counter value should be 2. One for a worker object, + // another for a worker context object. We need to call ref() just once + // because the default counter value of RefCounted is 1. + } + + // WebWorker methods: + virtual void startWorkerContext(const WebKit::WebURL&, const WebKit::WebString&, const WebKit::WebString&) {} + virtual void terminateWorkerContext() {} + virtual void postMessageToWorkerContext(const WebKit::WebString&, const WebKit::WebMessagePortChannelArray&) {} + virtual void workerObjectDestroyed() + { + // Releases the reference held for worker object. + deref(); + } + virtual void clientDestroyed() {} + + // WebWorkerClient methods: + virtual void postMessageToWorkerObject(const WebKit::WebString&, const WebKit::WebMessagePortChannelArray&) {} + virtual void postExceptionToWorkerObject(const WebKit::WebString&, int, const WebKit::WebString&) {} + virtual void postConsoleMessageToWorkerObject(int, int, int, int, const WebKit::WebString&, int, const WebKit::WebString&) {} + virtual void confirmMessageFromWorkerObject(bool) {} + virtual void reportPendingActivity(bool) {} + virtual void workerContextClosed() {} + virtual void workerContextDestroyed() + { + // Releases the reference held for worker context object. + deref(); + } + virtual WebKit::WebWorker* createWorker(WebKit::WebWorkerClient*) { return 0; } + virtual WebKit::WebNotificationPresenter* notificationPresenter() { return 0; } + +private: + ~TestWebWorker() {} + friend class WTF::RefCounted; +}; + +#endif // TestWebWorker_h diff --git a/WebKitTools/DumpRenderTree/chromium/TextInputController.cpp b/WebKitTools/DumpRenderTree/chromium/TextInputController.cpp new file mode 100644 index 0000000..63c4719 --- /dev/null +++ b/WebKitTools/DumpRenderTree/chromium/TextInputController.cpp @@ -0,0 +1,216 @@ +/* + * Copyright (C) 2010 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "TextInputController.h" + +#include "TestShell.h" +#include "public/WebFrame.h" +#include "public/WebRange.h" +#include "public/WebString.h" +#include "public/WebView.h" +#include +#include + +using namespace WebKit; +using namespace std; + +TestShell* TextInputController::testShell = 0; + +TextInputController::TextInputController(TestShell* shell) +{ + // Set static testShell variable. Be careful not to assign testShell to new + // windows which are temporary. + if (!testShell) + testShell = shell; + + bindMethod("insertText", &TextInputController::insertText); + bindMethod("doCommand", &TextInputController::doCommand); + bindMethod("setMarkedText", &TextInputController::setMarkedText); + bindMethod("unmarkText", &TextInputController::unmarkText); + bindMethod("hasMarkedText", &TextInputController::hasMarkedText); + bindMethod("conversationIdentifier", &TextInputController::conversationIdentifier); + bindMethod("substringFromRange", &TextInputController::substringFromRange); + bindMethod("attributedSubstringFromRange", &TextInputController::attributedSubstringFromRange); + bindMethod("markedRange", &TextInputController::markedRange); + bindMethod("selectedRange", &TextInputController::selectedRange); + bindMethod("firstRectForCharacterRange", &TextInputController::firstRectForCharacterRange); + bindMethod("characterIndexForPoint", &TextInputController::characterIndexForPoint); + bindMethod("validAttributesForMarkedText", &TextInputController::validAttributesForMarkedText); + bindMethod("makeAttributedString", &TextInputController::makeAttributedString); +} + +WebFrame* TextInputController::getMainFrame() +{ + return testShell->webView()->mainFrame(); +} + +void TextInputController::insertText(const CppArgumentList& arguments, CppVariant* result) +{ + result->setNull(); + + WebFrame* mainFrame = getMainFrame(); + if (!mainFrame) + return; + if (arguments.size() < 1 || !arguments[0].isString()) + return; + + if (mainFrame->hasMarkedText()) { + mainFrame->unmarkText(); + mainFrame->replaceSelection(WebString()); + } + mainFrame->insertText(WebString::fromUTF8(arguments[0].toString())); +} + +void TextInputController::doCommand(const CppArgumentList& arguments, CppVariant* result) +{ + result->setNull(); + + WebFrame* mainFrame = getMainFrame(); + if (!mainFrame) + return; + + if (arguments.size() >= 1 && arguments[0].isString()) + mainFrame->executeCommand(WebString::fromUTF8(arguments[0].toString())); +} + +void TextInputController::setMarkedText(const CppArgumentList& arguments, CppVariant* result) +{ + result->setNull(); + + WebFrame* mainFrame = getMainFrame(); + if (!mainFrame) + return; + + if (arguments.size() >= 3 && arguments[0].isString() + && arguments[1].isNumber() && arguments[2].isNumber()) { + mainFrame->setMarkedText(WebString::fromUTF8(arguments[0].toString()), + arguments[1].toInt32(), + arguments[2].toInt32()); + } +} + +void TextInputController::unmarkText(const CppArgumentList&, CppVariant* result) +{ + result->setNull(); + + WebFrame* mainFrame = getMainFrame(); + if (!mainFrame) + return; + + mainFrame->unmarkText(); +} + +void TextInputController::hasMarkedText(const CppArgumentList&, CppVariant* result) +{ + result->setNull(); + + WebFrame* mainFrame = getMainFrame(); + if (!mainFrame) + return; + + result->set(mainFrame->hasMarkedText()); +} + +void TextInputController::conversationIdentifier(const CppArgumentList&, CppVariant* result) +{ + // FIXME: Implement this. + result->setNull(); +} + +void TextInputController::substringFromRange(const CppArgumentList&, CppVariant* result) +{ + // FIXME: Implement this. + result->setNull(); +} + +void TextInputController::attributedSubstringFromRange(const CppArgumentList&, CppVariant* result) +{ + // FIXME: Implement this. + result->setNull(); +} + +void TextInputController::markedRange(const CppArgumentList&, CppVariant* result) +{ + result->setNull(); + + WebFrame* mainFrame = getMainFrame(); + if (!mainFrame) + return; + + WebRange range = mainFrame->markedRange(); + char buffer[30]; + snprintf(buffer, 30, "%d,%d", range.startOffset(), range.endOffset()); + result->set(string(buffer)); +} + +void TextInputController::selectedRange(const CppArgumentList&, CppVariant* result) +{ + result->setNull(); + + WebFrame* mainFrame = getMainFrame(); + if (!mainFrame) + return; + + WebRange range = mainFrame->selectionRange(); + char buffer[30]; + snprintf(buffer, 30, "%d,%d", range.startOffset(), range.endOffset()); + result->set(string(buffer)); +} + +void TextInputController::firstRectForCharacterRange(const CppArgumentList&, CppVariant* result) +{ + // FIXME: Implement this. + result->setNull(); +} + +void TextInputController::characterIndexForPoint(const CppArgumentList&, CppVariant* result) +{ + // FIXME: Implement this. + result->setNull(); +} + +void TextInputController::validAttributesForMarkedText(const CppArgumentList&, CppVariant* result) +{ + result->setNull(); + + WebFrame* mainFrame = getMainFrame(); + if (!mainFrame) + return; + + result->set("NSUnderline,NSUnderlineColor,NSMarkedClauseSegment," + "NSTextInputReplacementRangeAttributeName"); +} + +void TextInputController::makeAttributedString(const CppArgumentList&, CppVariant* result) +{ + // FIXME: Implement this. + result->setNull(); +} diff --git a/WebKitTools/DumpRenderTree/chromium/TextInputController.h b/WebKitTools/DumpRenderTree/chromium/TextInputController.h new file mode 100644 index 0000000..a912ba1 --- /dev/null +++ b/WebKitTools/DumpRenderTree/chromium/TextInputController.h @@ -0,0 +1,149 @@ +/* + * Copyright (C) 2010 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +<<<<<<< HEAD:WebCore/bindings/v8/custom/V8NavigatorCustom.cpp +#include "config.h" +#include "V8Navigator.h" + +#include "RuntimeEnabledFeatures.h" +#include "V8DOMWindow.h" +#include "V8DOMWrapper.h" + +#if PLATFORM(ANDROID) +#include "ExceptionCode.h" +#include "V8CustomApplicationInstalledCallback.h" +#include "V8Proxy.h" +#endif + +namespace WebCore { + +v8::Handle toV8(Navigator* impl) +{ + if (!impl) + return v8::Null(); + v8::Handle wrapper = getDOMObjectMap().get(impl); + if (wrapper.IsEmpty()) { + wrapper = V8Navigator::wrap(impl); + if (!wrapper.IsEmpty()) + V8DOMWrapper::setHiddenWindowReference(impl->frame(), V8DOMWindow::navigatorIndex, wrapper); + } + return wrapper; +} + +#if PLATFORM(ANDROID) && ENABLE(APPLICATION_INSTALLED) + +static PassRefPtr createApplicationInstalledCallback( + v8::Local value, bool& succeeded) +{ + succeeded = true; + + if (!value->IsFunction()) { + succeeded = false; + throwError("The second argument should be a function"); + return 0; + } + + Frame* frame = V8Proxy::retrieveFrameForCurrentContext(); + return V8CustomApplicationInstalledCallback::create(value, frame); +} + +v8::Handle V8Navigator::isApplicationInstalledCallback(const v8::Arguments& args) +{ + INC_STATS("DOM.isApplicationInstalled()"); + bool succeeded = false; + + if (args.Length() < 2) + return throwError("Two arguments required: an application name and a callback.", V8Proxy::SyntaxError); + + if (!args[0]->IsString()) + return throwError("The first argument should be a string."); + + RefPtr callback = + createApplicationInstalledCallback(args[1], succeeded); + if (!succeeded) + return v8::Undefined(); + + ASSERT(callback); + + Navigator* navigator = V8Navigator::toNative(args.Holder()); + if (!navigator->isApplicationInstalled(toWebCoreString(args[0]), callback.release())) + return throwError(INVALID_STATE_ERR); + + return v8::Undefined(); +} + +#endif // PLATFORM(ANDROID) && ENABLE(APPLICATION_INSTALLED) + +} // namespace WebCore +======= +// TextInputController is bound to window.textInputController in Javascript +// when DRT is running. Layout tests use it to exercise various corners of +// text input. + +#ifndef TextInputController_h +#define TextInputController_h + +#include "CppBoundClass.h" + +class TestShell; + +namespace WebKit { +class WebFrame; +} + +class TextInputController : public CppBoundClass { +public: + TextInputController(TestShell*); + + void insertText(const CppArgumentList&, CppVariant*); + void doCommand(const CppArgumentList&, CppVariant*); + void setMarkedText(const CppArgumentList&, CppVariant*); + void unmarkText(const CppArgumentList&, CppVariant*); + void hasMarkedText(const CppArgumentList&, CppVariant*); + void conversationIdentifier(const CppArgumentList&, CppVariant*); + void substringFromRange(const CppArgumentList&, CppVariant*); + void attributedSubstringFromRange(const CppArgumentList&, CppVariant*); + void markedRange(const CppArgumentList&, CppVariant*); + void selectedRange(const CppArgumentList&, CppVariant*); + void firstRectForCharacterRange(const CppArgumentList&, CppVariant*); + void characterIndexForPoint(const CppArgumentList&, CppVariant*); + void validAttributesForMarkedText(const CppArgumentList&, CppVariant*); + void makeAttributedString(const CppArgumentList&, CppVariant*); + +private: + // Returns the test shell's main WebFrame. + static WebKit::WebFrame* getMainFrame(); + + // Non-owning pointer. The TextInputController is owned by the TestShell. + static TestShell* testShell; +}; + +#endif // TextInputController_h +>>>>>>> webkit.org at r58033:WebKitTools/DumpRenderTree/chromium/TextInputController.h diff --git a/WebKitTools/DumpRenderTree/chromium/WebViewHost.cpp b/WebKitTools/DumpRenderTree/chromium/WebViewHost.cpp new file mode 100644 index 0000000..95b1c7e --- /dev/null +++ b/WebKitTools/DumpRenderTree/chromium/WebViewHost.cpp @@ -0,0 +1,1316 @@ +/* + * Copyright (C) 2010 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "WebViewHost.h" + +#include "LayoutTestController.h" +#include "TestNavigationController.h" +#include "TestShell.h" +#include "TestWebWorker.h" +#include "net/base/net_errors.h" // FIXME: can we remove this? +#include "public/WebCString.h" +#include "public/WebConsoleMessage.h" +#include "public/WebContextMenuData.h" +#include "public/WebDataSource.h" +#include "public/WebDragData.h" +#include "public/WebFrame.h" +#include "public/WebHistoryItem.h" +#include "public/WebNode.h" +#include "public/WebRange.h" +#include "public/WebRect.h" +#include "public/WebScreenInfo.h" +#include "public/WebSize.h" +#include "public/WebStorageNamespace.h" +#include "public/WebURLRequest.h" +#include "public/WebURLResponse.h" +#include "public/WebView.h" +#include "skia/ext/platform_canvas.h" +#include "webkit/support/webkit_support.h" +#include + +using namespace WebKit; +using namespace skia; +using namespace std; + +static const int screenWidth = 1920; +static const int screenHeight = 1080; +static const int screenUnavailableBorder = 8; + +// WebNavigationType debugging strings taken from PolicyDelegate.mm. +static const char* linkClickedString = "link clicked"; +static const char* formSubmittedString = "form submitted"; +static const char* backForwardString = "back/forward"; +static const char* reloadString = "reload"; +static const char* formResubmittedString = "form resubmitted"; +static const char* otherString = "other"; +static const char* illegalString = "illegal value"; + +static int nextPageID = 1; + +// Used to write a platform neutral file:/// URL by only taking the filename +// (e.g., converts "file:///tmp/foo.txt" to just "foo.txt"). +static string urlSuitableForTestResult(const string& url) +{ + if (url.empty() || string::npos == url.find("file://")) + return url; + + size_t pos = url.rfind('/'); + if (pos == string::npos) { +#if OS(WINDOWS) + pos = url.rfind('\\'); + if (pos == string::npos) + pos = 0; +#else + pos = 0; +#endif + } + string filename = url.substr(pos + 1); + if (filename.empty()) + return "file:"; // A WebKit test has this in its expected output. + return filename; +} + +// Used to write a platform neutral file:/// URL by taking the +// filename and its directory. (e.g., converts +// "file:///tmp/foo/bar.txt" to just "bar.txt"). +static string descriptionSuitableForTestResult(const string& url) +{ + if (url.empty() || string::npos == url.find("file://")) + return url; + + size_t pos = url.rfind('/'); + if (pos == string::npos || !pos) + return "ERROR:" + url; + pos = url.rfind('/', pos - 1); + if (pos == string::npos) + return "ERROR:" + url; + + return url.substr(pos + 1); +} + +// Adds a file called "DRTFakeFile" to |data_object| (CF_HDROP). Use to fake +// dragging a file. +static void addDRTFakeFileToDataObject(WebDragData* dragData) +{ + dragData->appendToFileNames(WebString::fromUTF8("DRTFakeFile")); +} + +// Get a debugging string from a WebNavigationType. +static const char* webNavigationTypeToString(WebNavigationType type) +{ + switch (type) { + case WebKit::WebNavigationTypeLinkClicked: + return linkClickedString; + case WebKit::WebNavigationTypeFormSubmitted: + return formSubmittedString; + case WebKit::WebNavigationTypeBackForward: + return backForwardString; + case WebKit::WebNavigationTypeReload: + return reloadString; + case WebKit::WebNavigationTypeFormResubmitted: + return formResubmittedString; + case WebKit::WebNavigationTypeOther: + return otherString; + } + return illegalString; +} + +static string URLDescription(const GURL& url) +{ + if (url.SchemeIs("file")) + return url.ExtractFileName(); + return url.possibly_invalid_spec(); +} + +static void printResponseDescription(const WebURLResponse& response) +{ + if (response.isNull()) { + fputs("(null)", stdout); + return; + } + string url = response.url().spec(); + printf("", + descriptionSuitableForTestResult(url).c_str(), + response.httpStatusCode()); +} + +static void printErrorDescription(const WebURLError& error) +{ + string domain = error.domain.utf8(); + int code = error.reason; + + if (domain == net::kErrorDomain) { + domain = "NSURLErrorDomain"; + switch (error.reason) { + case net::ERR_ABORTED: + code = -999; + break; + case net::ERR_UNSAFE_PORT: + // Our unsafe port checking happens at the network stack level, but we + // make this translation here to match the behavior of stock WebKit. + domain = "WebKitErrorDomain"; + code = 103; + break; + case net::ERR_ADDRESS_INVALID: + case net::ERR_ADDRESS_UNREACHABLE: + code = -1004; + break; + } + } else + LOG_ERROR("Unknown error domain"); + + printf("", + domain.c_str(), code, error.unreachableURL.spec().data()); +} + +static void printNodeDescription(const WebNode& node, int exception) +{ + if (exception) { + fputs("ERROR", stdout); + return; + } + if (node.isNull()) { + fputs("(null)", stdout); + return; + } + fputs(node.nodeName().utf8().data(), stdout); + const WebNode& parent = node.parentNode(); + if (!parent.isNull()) { + fputs(" > ", stdout); + printNodeDescription(parent, 0); + } +} + +static void printRangeDescription(const WebRange& range) +{ + if (range.isNull()) { + fputs("(null)", stdout); + return; + } + printf("range from %d of ", range.startOffset()); + int exception = 0; + WebNode startNode = range.startContainer(exception); + printNodeDescription(startNode, exception); + printf(" to %d of ", range.endOffset()); + WebNode endNode = range.endContainer(exception); + printNodeDescription(endNode, exception); +} + +static string editingActionDescription(WebEditingAction action) +{ + switch (action) { + case WebKit::WebEditingActionTyped: + return "WebViewInsertActionTyped"; + case WebKit::WebEditingActionPasted: + return "WebViewInsertActionPasted"; + case WebKit::WebEditingActionDropped: + return "WebViewInsertActionDropped"; + } + return "(UNKNOWN ACTION)"; +} + +static string textAffinityDescription(WebTextAffinity affinity) +{ + switch (affinity) { + case WebKit::WebTextAffinityUpstream: + return "NSSelectionAffinityUpstream"; + case WebKit::WebTextAffinityDownstream: + return "NSSelectionAffinityDownstream"; + } + return "(UNKNOWN AFFINITY)"; +} + +// WebViewClient ------------------------------------------------------------- + +WebView* WebViewHost::createView(WebFrame*) +{ + if (!layoutTestController()->canOpenWindows()) + return 0; + return m_shell->createWebView()->webView(); +} + +WebWidget* WebViewHost::createPopupMenu(bool) +{ + return 0; +} + +WebWidget* WebViewHost::createPopupMenu(const WebPopupMenuInfo&) +{ + return 0; +} + +WebStorageNamespace* WebViewHost::createSessionStorageNamespace() +{ + return WebKit::WebStorageNamespace::createSessionStorageNamespace(); +} + +void WebViewHost::didAddMessageToConsole(const WebConsoleMessage& message, const WebString& sourceName, unsigned sourceLine) +{ + // This matches win DumpRenderTree's UIDelegate.cpp. + string newMessage; + if (!message.text.isEmpty()) { + newMessage = message.text.utf8(); + size_t fileProtocol = newMessage.find("file://"); + if (fileProtocol != string::npos) { + newMessage = newMessage.substr(0, fileProtocol) + + urlSuitableForTestResult(newMessage.substr(fileProtocol)); + } + } + printf("CONSOLE MESSAGE: line %d: %s\n", sourceLine, newMessage.data()); +} + +void WebViewHost::didStartLoading() +{ + m_shell->setIsLoading(true); +} + +void WebViewHost::didStopLoading() +{ + m_shell->setIsLoading(false); +} + +// The output from these methods in layout test mode should match that +// expected by the layout tests. See EditingDelegate.m in DumpRenderTree. + +bool WebViewHost::shouldBeginEditing(const WebRange& range) +{ + if (layoutTestController()->shouldDumpEditingCallbacks()) { + fputs("EDITING DELEGATE: shouldBeginEditingInDOMRange:", stdout); + printRangeDescription(range); + fputs("\n", stdout); + } + return layoutTestController()->acceptsEditing(); +} + +bool WebViewHost::shouldEndEditing(const WebRange& range) +{ + if (layoutTestController()->shouldDumpEditingCallbacks()) { + fputs("EDITING DELEGATE: shouldEndEditingInDOMRange:", stdout); + printRangeDescription(range); + fputs("\n", stdout); + } + return layoutTestController()->acceptsEditing(); +} + +bool WebViewHost::shouldInsertNode(const WebNode& node, const WebRange& range, WebEditingAction action) +{ + if (layoutTestController()->shouldDumpEditingCallbacks()) { + fputs("EDITING DELEGATE: shouldInsertNode:", stdout); + printNodeDescription(node, 0); + fputs(" replacingDOMRange:", stdout); + printRangeDescription(range); + printf(" givenAction:%s\n", editingActionDescription(action).c_str()); + } + return layoutTestController()->acceptsEditing(); +} + +bool WebViewHost::shouldInsertText(const WebString& text, const WebRange& range, WebEditingAction action) +{ + if (layoutTestController()->shouldDumpEditingCallbacks()) { + printf("EDITING DELEGATE: shouldInsertText:%s replacingDOMRange:", text.utf8().data()); + printRangeDescription(range); + printf(" givenAction:%s\n", editingActionDescription(action).c_str()); + } + return layoutTestController()->acceptsEditing(); +} + +bool WebViewHost::shouldChangeSelectedRange( + const WebRange& fromRange, const WebRange& toRange, WebTextAffinity affinity, bool stillSelecting) +{ + if (layoutTestController()->shouldDumpEditingCallbacks()) { + fputs("EDITING DELEGATE: shouldChangeSelectedDOMRange:", stdout); + printRangeDescription(fromRange); + fputs(" toDOMRange:", stdout); + printRangeDescription(toRange); + printf(" affinity:%s stillSelecting:%s\n", + textAffinityDescription(affinity).c_str(), + (stillSelecting ? "TRUE" : "FALSE")); + } + return layoutTestController()->acceptsEditing(); +} + +bool WebViewHost::shouldDeleteRange(const WebRange& range) +{ + if (layoutTestController()->shouldDumpEditingCallbacks()) { + fputs("EDITING DELEGATE: shouldDeleteDOMRange:", stdout); + printRangeDescription(range); + fputs("\n", stdout); + } + return layoutTestController()->acceptsEditing(); +} + +bool WebViewHost::shouldApplyStyle(const WebString& style, const WebRange& range) +{ + if (layoutTestController()->shouldDumpEditingCallbacks()) { + printf("EDITING DELEGATE: shouldApplyStyle:%s toElementsInDOMRange:", style.utf8().data()); + printRangeDescription(range); + fputs("\n", stdout); + } + return layoutTestController()->acceptsEditing(); +} + +bool WebViewHost::isSmartInsertDeleteEnabled() +{ + return m_smartInsertDeleteEnabled; +} + +bool WebViewHost::isSelectTrailingWhitespaceEnabled() +{ + return m_selectTrailingWhitespaceEnabled; +} + +void WebViewHost::didBeginEditing() +{ + if (!layoutTestController()->shouldDumpEditingCallbacks()) + return; + fputs("EDITING DELEGATE: webViewDidBeginEditing:WebViewDidBeginEditingNotification\n", stdout); +} + +void WebViewHost::didChangeSelection(bool isEmptySelection) +{ + if (layoutTestController()->shouldDumpEditingCallbacks()) + fputs("EDITING DELEGATE: webViewDidChangeSelection:WebViewDidChangeSelectionNotification\n", stdout); + // No need to update clipboard with the selected text in DRT. +} + +void WebViewHost::didChangeContents() +{ + if (!layoutTestController()->shouldDumpEditingCallbacks()) + return; + fputs("EDITING DELEGATE: webViewDidChange:WebViewDidChangeNotification\n", stdout); +} + +void WebViewHost::didEndEditing() +{ + if (!layoutTestController()->shouldDumpEditingCallbacks()) + return; + fputs("EDITING DELEGATE: webViewDidEndEditing:WebViewDidEndEditingNotification\n", stdout); +} + +bool WebViewHost::handleCurrentKeyboardEvent() +{ + if (m_editCommandName.empty()) + return false; + WebFrame* frame = webView()->focusedFrame(); + if (!frame) + return false; + + return frame->executeCommand(WebString::fromUTF8(m_editCommandName), WebString::fromUTF8(m_editCommandValue)); +} + +void WebViewHost::spellCheck(const WebString& text, int& misspelledOffset, int& misspelledLength) +{ + // Check the spelling of the given text. +#if OS(MAC_OS_X) + // FIXME: rebaseline layout-test results of Windows and Linux so we + // can enable this mock spellchecker on them. + m_spellcheck.spellCheckWord(text, &misspelledOffset, &misspelledLength); +#endif +} + +WebString WebViewHost::autoCorrectWord(const WebString&) +{ + // Returns an empty string as Mac WebKit ('WebKitSupport/WebEditorClient.mm') + // does. (If this function returns a non-empty string, WebKit replaces the + // given misspelled string with the result one. This process executes some + // editor commands and causes layout-test failures.) + return WebString(); +} + +void WebViewHost::runModalAlertDialog(WebFrame*, const WebString& message) +{ + printf("ALERT: %s\n", message.utf8().data()); +} + +bool WebViewHost::runModalConfirmDialog(WebFrame*, const WebString& message) +{ + printf("CONFIRM: %s\n", message.utf8().data()); + return true; +} + +bool WebViewHost::runModalPromptDialog(WebFrame* frame, const WebString& message, + const WebString& defaultValue, WebString*) +{ + printf("PROMPT: %s, default text: %s\n", message.utf8().data(), defaultValue.utf8().data()); + return true; +} + +bool WebViewHost::runModalBeforeUnloadDialog(WebFrame*, const WebString&) +{ + return true; // Allow window closure. +} + +void WebViewHost::showContextMenu(WebFrame*, const WebContextMenuData&) +{ +} + + +void WebViewHost::setStatusText(const WebString& text) +{ + if (!layoutTestController()->shouldDumpStatusCallbacks()) + return; + // When running tests, write to stdout. + printf("UI DELEGATE STATUS CALLBACK: setStatusText:%s\n", text.utf8().data()); +} + +void WebViewHost::startDragging(const WebPoint& mouseCoords, const WebDragData& data, WebDragOperationsMask mask) +{ + WebDragData mutableDragData = data; + if (layoutTestController()->shouldAddFileToPasteboard()) { + // Add a file called DRTFakeFile to the drag&drop clipboard. + addDRTFakeFileToDataObject(&mutableDragData); + } + + // When running a test, we need to fake a drag drop operation otherwise + // Windows waits for real mouse events to know when the drag is over. + EventSender::doDragDrop(mouseCoords, mutableDragData, mask); +} + +void WebViewHost::navigateBackForwardSoon(int offset) +{ + navigationController()->goToOffset(offset); +} + +int WebViewHost::historyBackListCount() +{ + return navigationController()->lastCommittedEntryIndex(); +} + +int WebViewHost::historyForwardListCount() +{ + int currentIndex =navigationController()->lastCommittedEntryIndex(); + return navigationController()->entryCount() - currentIndex - 1; +} + +void WebViewHost::focusAccessibilityObject(const WebAccessibilityObject& object) +{ + m_shell->accessibilityController()->setFocusedElement(object); +} + +// WebWidgetClient ----------------------------------------------------------- + +void WebViewHost::didInvalidateRect(const WebRect& rect) +{ + if (m_isPainting) + LOG_ERROR("unexpected invalidation while painting"); + updatePaintRect(rect); +} + +void WebViewHost::didScrollRect(int, int, const WebRect& clipRect) +{ + // This is used for optimizing painting when the renderer is scrolled. We're + // currently not doing any optimizations so just invalidate the region. + didInvalidateRect(clipRect); +} + +void WebViewHost::didFocus() +{ + m_shell->setFocus(webWidget(), true); +} + +void WebViewHost::didBlur() +{ + m_shell->setFocus(webWidget(), false); +} + +WebScreenInfo WebViewHost::screenInfo() +{ + // We don't need to set actual values. + WebScreenInfo info; + info.depth = 24; + info.depthPerComponent = 8; + info.isMonochrome = false; + info.rect = WebRect(0, 0, screenWidth, screenHeight); + // Use values different from info.rect for testing. + info.availableRect = WebRect(screenUnavailableBorder, screenUnavailableBorder, + screenWidth - screenUnavailableBorder * 2, + screenHeight - screenUnavailableBorder * 2); + return info; +} + +void WebViewHost::show(WebNavigationPolicy) +{ + m_hasWindow = true; + WebSize size = webWidget()->size(); + updatePaintRect(WebRect(0, 0, size.width, size.height)); +} + +void WebViewHost::closeWidgetSoon() +{ + m_hasWindow = false; + m_shell->closeWindow(this); +} + +void WebViewHost::didChangeCursor(const WebCursorInfo& cursorInfo) +{ + if (!hasWindow()) + return; + m_currentCursor = cursorInfo; +} + +WebRect WebViewHost::windowRect() +{ + return m_windowRect; +} + +void WebViewHost::setWindowRect(const WebRect& rect) +{ + m_windowRect = rect; + const int border2 = TestShell::virtualWindowBorder * 2; + if (m_windowRect.width <= border2) + m_windowRect.width = 1 + border2; + if (m_windowRect.height <= border2) + m_windowRect.height = 1 + border2; + int width = m_windowRect.width - border2; + int height = m_windowRect.height - border2; + discardBackingStore(); + webWidget()->resize(WebSize(width, height)); + updatePaintRect(WebRect(0, 0, width, height)); +} + +WebRect WebViewHost::rootWindowRect() +{ + return windowRect(); +} + +WebRect WebViewHost::windowResizerRect() +{ + // Not necessary. + return WebRect(); +} + +void WebViewHost::runModal() +{ + // FIXME: Should we implement this in DRT? +} + +// WebFrameClient ------------------------------------------------------------ + +WebPlugin* WebViewHost::createPlugin(WebFrame* frame, const WebPluginParams& params) +{ + return webkit_support::CreateWebPlugin(frame, params); +} + +WebWorker* WebViewHost::createWorker(WebFrame*, WebWorkerClient*) +{ + return new TestWebWorker(); +} + +WebMediaPlayer* WebViewHost::createMediaPlayer(WebFrame* frame, WebMediaPlayerClient* client) +{ + return webkit_support::CreateMediaPlayer(frame, client); +} + +bool WebViewHost::allowPlugins(WebFrame* frame, bool enabledPerSettings) +{ + return enabledPerSettings; +} + +bool WebViewHost::allowImages(WebFrame* frame, bool enabledPerSettings) +{ + return enabledPerSettings; +} + +void WebViewHost::loadURLExternally(WebFrame*, const WebURLRequest& request, WebNavigationPolicy policy) +{ + ASSERT(policy != WebKit::WebNavigationPolicyCurrentTab); + WebViewHost* another = m_shell->createNewWindow(request.url()); + if (another) + another->show(policy); +} + +WebNavigationPolicy WebViewHost::decidePolicyForNavigation( + WebFrame*, const WebURLRequest& request, + WebNavigationType type, const WebNode& originatingNode, + WebNavigationPolicy defaultPolicy, bool isRedirect) +{ + WebNavigationPolicy result; + if (!m_policyDelegateEnabled) + return defaultPolicy; + + printf("Policy delegate: attempt to load %s with navigation type '%s'", + URLDescription(request.url()).c_str(), webNavigationTypeToString(type)); + if (!originatingNode.isNull()) { + fputs(" originating from ", stdout); + printNodeDescription(originatingNode, 0); + } + fputs("\n", stdout); + if (m_policyDelegateIsPermissive) + result = WebKit::WebNavigationPolicyCurrentTab; + else + result = WebKit::WebNavigationPolicyIgnore; + + if (m_policyDelegateShouldNotifyDone) + layoutTestController()->policyDelegateDone(); + return result; +} + +bool WebViewHost::canHandleRequest(WebFrame*, const WebURLRequest& request) +{ + GURL url = request.url(); + // Just reject the scheme used in + // LayoutTests/http/tests/misc/redirect-to-external-url.html + return !url.SchemeIs("spaceballs"); +} + +WebURLError WebViewHost::cannotHandleRequestError(WebFrame*, const WebURLRequest& request) +{ + WebURLError error; + // A WebKit layout test expects the following values. + // unableToImplementPolicyWithError() below prints them. + error.domain = WebString::fromUTF8("WebKitErrorDomain"); + error.reason = 101; + error.unreachableURL = request.url(); + return error; +} + +WebURLError WebViewHost::cancelledError(WebFrame*, const WebURLRequest& request) +{ + WebURLError error; + error.domain = WebString::fromUTF8(net::kErrorDomain); + error.reason = net::ERR_ABORTED; + error.unreachableURL = request.url(); + return error; +} + +void WebViewHost::unableToImplementPolicyWithError(WebFrame* frame, const WebURLError& error) +{ + printf("Policy delegate: unable to implement policy with error domain '%s', " + "error code %d, in frame '%s'\n", + error.domain.utf8().data(), error.reason, frame->name().utf8().data()); +} + +void WebViewHost::willPerformClientRedirect(WebFrame* frame, const WebURL& from, const WebURL& to, + double interval, double fire_time) +{ + if (!m_shell->shouldDumpFrameLoadCallbacks()) + return; + printFrameDescription(frame); + printf(" - willPerformClientRedirectToURL: %s \n", to.spec().data()); +} + +void WebViewHost::didCancelClientRedirect(WebFrame* frame) +{ + if (!m_shell->shouldDumpFrameLoadCallbacks()) + return; + printFrameDescription(frame); + fputs(" - didCancelClientRedirectForFrame\n", stdout); +} + +void WebViewHost::didCreateDataSource(WebFrame*, WebDataSource* ds) +{ + ds->setExtraData(m_pendingExtraData.release()); +} + +void WebViewHost::didStartProvisionalLoad(WebFrame* frame) +{ + if (m_shell->shouldDumpFrameLoadCallbacks()) { + printFrameDescription(frame); + fputs(" - didStartProvisionalLoadForFrame\n", stdout); + } + + if (!m_topLoadingFrame) + m_topLoadingFrame = frame; + + if (layoutTestController()->stopProvisionalFrameLoads()) { + printFrameDescription(frame); + fputs(" - stopping load in didStartProvisionalLoadForFrame callback\n", stdout); + frame->stopLoading(); + } + updateAddressBar(frame->view()); +} + +void WebViewHost::didReceiveServerRedirectForProvisionalLoad(WebFrame* frame) +{ + if (m_shell->shouldDumpFrameLoadCallbacks()) { + printFrameDescription(frame); + fputs(" - didReceiveServerRedirectForProvisionalLoadForFrame\n", stdout); + } + updateAddressBar(frame->view()); +} + +void WebViewHost::didFailProvisionalLoad(WebFrame* frame, const WebURLError& error) +{ + if (m_shell->shouldDumpFrameLoadCallbacks()) { + printFrameDescription(frame); + fputs(" - didFailProvisionalLoadWithError\n", stdout); + } + + locationChangeDone(frame); + + // Don't display an error page if we're running layout tests, because + // DumpRenderTree doesn't. +} + +void WebViewHost::didCommitProvisionalLoad(WebFrame* frame, bool isNewNavigation) +{ + if (m_shell->shouldDumpFrameLoadCallbacks()) { + printFrameDescription(frame); + fputs(" - didCommitLoadForFrame\n", stdout); + } + updateForCommittedLoad(frame, isNewNavigation); +} + +void WebViewHost::didClearWindowObject(WebFrame* frame) +{ + m_shell->bindJSObjectsToWindow(frame); +} + +void WebViewHost::didReceiveTitle(WebFrame* frame, const WebString& title) +{ + WebCString title8 = title.utf8(); + + if (m_shell->shouldDumpFrameLoadCallbacks()) { + printFrameDescription(frame); + printf(" - didReceiveTitle: %s\n", title8.data()); + } + + if (layoutTestController()->shouldDumpTitleChanges()) + printf("TITLE CHANGED: %s\n", title8.data()); + + setPageTitle(title); +} + +void WebViewHost::didFinishDocumentLoad(WebFrame* frame) +{ + if (m_shell->shouldDumpFrameLoadCallbacks()) { + printFrameDescription(frame); + fputs(" - didFinishDocumentLoadForFrame\n", stdout); + } else { + unsigned pendingUnloadEvents = frame->unloadListenerCount(); + if (pendingUnloadEvents) { + printFrameDescription(frame); + printf(" - has %u onunload handler(s)\n", pendingUnloadEvents); + } + } +} + +void WebViewHost::didHandleOnloadEvents(WebFrame* frame) +{ + if (m_shell->shouldDumpFrameLoadCallbacks()) { + printFrameDescription(frame); + fputs(" - didHandleOnloadEventsForFrame\n", stdout); + } +} + +void WebViewHost::didFailLoad(WebFrame* frame, const WebURLError& error) +{ + if (m_shell->shouldDumpFrameLoadCallbacks()) { + printFrameDescription(frame); + fputs(" - didFailLoadWithError\n", stdout); + } + locationChangeDone(frame); +} + +void WebViewHost::didFinishLoad(WebFrame* frame) +{ + if (m_shell->shouldDumpFrameLoadCallbacks()) { + printFrameDescription(frame); + fputs(" - didFinishLoadForFrame\n", stdout); + } + updateAddressBar(frame->view()); + locationChangeDone(frame); +} + +void WebViewHost::didChangeLocationWithinPage(WebFrame* frame, bool isNewNavigation) +{ + frame->dataSource()->setExtraData(m_pendingExtraData.release()); + if (m_shell->shouldDumpFrameLoadCallbacks()) { + printFrameDescription(frame); + fputs(" - didChangeLocationWithinPageForFrame\n", stdout); + } + updateForCommittedLoad(frame, isNewNavigation); +} + +void WebViewHost::assignIdentifierToRequest(WebFrame*, unsigned identifier, const WebURLRequest& request) +{ + if (!m_shell->shouldDumpResourceLoadCallbacks()) + return; + m_resourceIdentifierMap.set(identifier, descriptionSuitableForTestResult(request.url().spec())); +} + +void WebViewHost::willSendRequest(WebFrame*, unsigned identifier, WebURLRequest& request, const WebURLResponse& redirectResponse) +{ + // Need to use GURL for host() and SchemeIs() + GURL url = request.url(); + string requestURL = url.possibly_invalid_spec(); + + if (layoutTestController()->shouldDumpResourceLoadCallbacks()) { + GURL mainDocumentURL = request.firstPartyForCookies(); + printResourceDescription(identifier); + printf(" - willSendRequest redirectResponse %s\n", + descriptionSuitableForTestResult(requestURL).c_str(), + URLDescription(mainDocumentURL).c_str(), + request.httpMethod().utf8().data()); + printResponseDescription(redirectResponse); + fputs("\n", stdout); + } + + if (!redirectResponse.isNull() && m_blocksRedirects) { + fputs("Returning null for this redirect\n", stdout); + // To block the request, we set its URL to an empty one. + request.setURL(WebURL()); + return; + } + + if (m_requestReturnNull) { + // To block the request, we set its URL to an empty one. + request.setURL(WebURL()); + return; + } + + string host = url.host(); + // 255.255.255.255 is used in some tests that expect to get back an error. + if (!host.empty() && (url.SchemeIs("http") || url.SchemeIs("https")) + && host != "127.0.0.1" + && host != "255.255.255.255" + && host != "localhost") { + printf("Blocked access to external URL %s\n", requestURL.c_str()); + + // To block the request, we set its URL to an empty one. + request.setURL(WebURL()); + return; + } + + // Set the new substituted URL. + request.setURL(webkit_support::RewriteLayoutTestsURL(request.url().spec())); +} + +void WebViewHost::didReceiveResponse(WebFrame*, unsigned identifier, const WebURLResponse& response) +{ + if (!m_shell->shouldDumpResourceLoadCallbacks()) + return; + printResourceDescription(identifier); + fputs(" - didReceiveResponse ", stdout); + printResponseDescription(response); + fputs("\n", stdout); +} + +void WebViewHost::didFinishResourceLoad(WebFrame*, unsigned identifier) +{ + if (m_shell->shouldDumpResourceLoadCallbacks()) { + printResourceDescription(identifier); + fputs(" - didFinishLoading\n", stdout); + } + m_resourceIdentifierMap.remove(identifier); +} + +void WebViewHost::didFailResourceLoad(WebFrame*, unsigned identifier, const WebURLError& error) +{ + if (m_shell->shouldDumpResourceLoadCallbacks()) { + printResourceDescription(identifier); + fputs(" - didFailLoadingWithError: ", stdout); + printErrorDescription(error); + fputs("\n", stdout); + } + m_resourceIdentifierMap.remove(identifier); +} + +void WebViewHost::didDisplayInsecureContent(WebFrame*) +{ + if (m_shell->shouldDumpFrameLoadCallbacks()) + fputs("didDisplayInsecureContent\n", stdout); +} + +void WebViewHost::didRunInsecureContent(WebFrame*, const WebSecurityOrigin& origin) +{ + if (m_shell->shouldDumpFrameLoadCallbacks()) + fputs("didRunInsecureContent\n", stdout); +} + +bool WebViewHost::allowScript(WebFrame*, bool enabledPerSettings) +{ + return enabledPerSettings; +} + +// Public functions ----------------------------------------------------------- + +WebViewHost::WebViewHost(TestShell* shell) + : m_policyDelegateEnabled(false) + , m_policyDelegateIsPermissive(false) + , m_policyDelegateShouldNotifyDone(false) + , m_shell(shell) + , m_topLoadingFrame(0) + , m_hasWindow(false) + , m_pageId(-1) + , m_lastPageIdUpdated(-1) + , m_smartInsertDeleteEnabled(true) +#if OS(WINDOWS) + , m_selectTrailingWhitespaceEnabled(true) +#else + , m_selectTrailingWhitespaceEnabled(false) +#endif + , m_blocksRedirects(false) + , m_requestReturnNull(false) + , m_isPainting(false) + , m_webWidget(0) +{ + m_navigationController.set(new TestNavigationController(this)); +} + +WebViewHost::~WebViewHost() +{ +} + +WebView* WebViewHost::webView() const +{ + ASSERT(m_webWidget); + // DRT does not support popup widgets. So m_webWidget is always a WebView. + return static_cast(m_webWidget); +} + +WebWidget* WebViewHost::webWidget() const +{ + ASSERT(m_webWidget); + return m_webWidget; +} + +void WebViewHost::reset() +{ + // Do a little placement new dance... + TestShell* shell = m_shell; + WebWidget* widget = m_webWidget; + this->~WebViewHost(); + new (this) WebViewHost(shell); + setWebWidget(widget); +} + +void WebViewHost::setSelectTrailingWhitespaceEnabled(bool enabled) +{ + m_selectTrailingWhitespaceEnabled = enabled; + // In upstream WebKit, smart insert/delete is mutually exclusive with select + // trailing whitespace, however, we allow both because Chromium on Windows + // allows both. +} + +void WebViewHost::setSmartInsertDeleteEnabled(bool enabled) +{ + m_smartInsertDeleteEnabled = enabled; + // In upstream WebKit, smart insert/delete is mutually exclusive with select + // trailing whitespace, however, we allow both because Chromium on Windows + // allows both. +} + +void WebViewHost::setCustomPolicyDelegate(bool isCustom, bool isPermissive) +{ + m_policyDelegateEnabled = isCustom; + m_policyDelegateIsPermissive = isPermissive; +} + +void WebViewHost::waitForPolicyDelegate() +{ + m_policyDelegateEnabled = true; + m_policyDelegateShouldNotifyDone = true; +} + +void WebViewHost::setEditCommand(const string& name, const string& value) +{ + m_editCommandName = name; + m_editCommandValue = value; +} + +void WebViewHost::clearEditCommand() +{ + m_editCommandName.clear(); + m_editCommandValue.clear(); +} + +void WebViewHost::loadURLForFrame(const WebURL& url, const WebString& frameName) +{ + if (!url.isValid()) + return; + TestShell::resizeWindowForTest(this, url); + navigationController()->loadEntry(new TestNavigationEntry(-1, url, WebString(), frameName)); +} + +bool WebViewHost::navigate(const TestNavigationEntry& entry, bool reload) +{ + // Get the right target frame for the entry. + WebFrame* frame = webView()->mainFrame(); + if (!entry.targetFrame().isEmpty()) + frame = webView()->findFrameByName(entry.targetFrame()); + + // TODO(mpcomplete): should we clear the target frame, or should + // back/forward navigations maintain the target frame? + + // A navigation resulting from loading a javascript URL should not be + // treated as a browser initiated event. Instead, we want it to look as if + // the page initiated any load resulting from JS execution. + if (!GURL(entry.URL()).SchemeIs("javascript")) + setPendingExtraData(new TestShellExtraData(entry.pageID())); + + // If we are reloading, then WebKit will use the state of the current page. + // Otherwise, we give it the state to navigate to. + if (reload) { + frame->reload(false); + } else if (!entry.contentState().isNull()) { + ASSERT(entry.pageID() != -1); + frame->loadHistoryItem(entry.contentState()); + } else { + ASSERT(entry.pageID() == -1); + frame->loadRequest(WebURLRequest(entry.URL())); + } + + // In case LoadRequest failed before DidCreateDataSource was called. + setPendingExtraData(0); + + // Restore focus to the main frame prior to loading new request. + // This makes sure that we don't have a focused iframe. Otherwise, that + // iframe would keep focus when the SetFocus called immediately after + // LoadRequest, thus making some tests fail (see http://b/issue?id=845337 + // for more details). + webView()->setFocusedFrame(frame); + m_shell->setFocus(webView(), true); + + return true; +} + +// Private functions ---------------------------------------------------------- + +LayoutTestController* WebViewHost::layoutTestController() const +{ + return m_shell->layoutTestController(); +} + +void WebViewHost::updateAddressBar(WebView* webView) +{ + WebFrame* mainFrame = webView->mainFrame(); + WebDataSource* dataSource = mainFrame->dataSource(); + if (!dataSource) + dataSource = mainFrame->provisionalDataSource(); + if (!dataSource) + return; + + setAddressBarURL(dataSource->request().firstPartyForCookies()); +} + +void WebViewHost::locationChangeDone(WebFrame* frame) +{ + if (frame != m_topLoadingFrame) + return; + m_topLoadingFrame = 0; + layoutTestController()->locationChangeDone(); +} + +void WebViewHost::updateForCommittedLoad(WebFrame* frame, bool isNewNavigation) +{ + // Code duplicated from RenderView::DidCommitLoadForFrame. + TestShellExtraData* extraData = static_cast(frame->dataSource()->extraData()); + + if (isNewNavigation) { + // New navigation. + updateSessionHistory(frame); + m_pageId = nextPageID++; + } else if (extraData && extraData->pendingPageID != -1 && !extraData->requestCommitted) { + // This is a successful session history navigation! + updateSessionHistory(frame); + m_pageId = extraData->pendingPageID; + } + + // Don't update session history multiple times. + if (extraData) + extraData->requestCommitted = true; + + updateURL(frame); +} + +void WebViewHost::updateURL(WebFrame* frame) +{ + WebDataSource* ds = frame->dataSource(); + ASSERT(ds); + const WebURLRequest& request = ds->request(); + OwnPtr entry(new TestNavigationEntry); + + // The referrer will be empty on https->http transitions. It + // would be nice if we could get the real referrer from somewhere. + entry->setPageID(m_pageId); + if (ds->hasUnreachableURL()) + entry->setURL(ds->unreachableURL()); + else + entry->setURL(request.url()); + + const WebHistoryItem& historyItem = frame->currentHistoryItem(); + if (!historyItem.isNull()) + entry->setContentState(historyItem); + + navigationController()->didNavigateToEntry(entry.release()); + m_lastPageIdUpdated = max(m_lastPageIdUpdated, m_pageId); +} + +void WebViewHost::updateSessionHistory(WebFrame* frame) +{ + // If we have a valid page ID at this point, then it corresponds to the page + // we are navigating away from. Otherwise, this is the first navigation, so + // there is no past session history to record. + if (m_pageId == -1) + return; + + TestNavigationEntry* entry = static_cast(navigationController()->entryWithPageID(m_pageId)); + if (!entry) + return; + + const WebHistoryItem& historyItem = webView()->mainFrame()->previousHistoryItem(); + if (historyItem.isNull()) + return; + + entry->setContentState(historyItem); +} + +void WebViewHost::printFrameDescription(WebFrame* webframe) +{ + string name8 = webframe->name().utf8(); + if (webframe == webView()->mainFrame()) { + if (!name8.length()) { + fputs("main frame", stdout); + return; + } + printf("main frame \"%s\"", name8.c_str()); + return; + } + if (!name8.length()) { + fputs("frame (anonymous)", stdout); + return; + } + printf("frame \"%s\"", name8.c_str()); +} + +void WebViewHost::printResourceDescription(unsigned identifier) +{ + ResourceMap::iterator it = m_resourceIdentifierMap.find(identifier); + printf("%s", it != m_resourceIdentifierMap.end() ? it->second.c_str() : ""); +} + +void WebViewHost::setPendingExtraData(TestShellExtraData* extraData) +{ + m_pendingExtraData.set(extraData); +} + +void WebViewHost::setPageTitle(const WebString&) +{ + // Nothing to do in layout test. +} + +void WebViewHost::setAddressBarURL(const WebURL&) +{ + // Nothing to do in layout test. +} + +// Painting functions --------------------------------------------------------- + +void WebViewHost::updatePaintRect(const WebRect& rect) +{ + // m_paintRect = m_paintRect U rect + if (rect.isEmpty()) + return; + if (m_paintRect.isEmpty()) { + m_paintRect = rect; + return; + } + int left = min(m_paintRect.x, rect.x); + int top = min(m_paintRect.y, rect.y); + int right = max(m_paintRect.x + m_paintRect.width, rect.x + rect.width); + int bottom = max(m_paintRect.y + m_paintRect.height, rect.y + rect.height); + m_paintRect = WebRect(left, top, right - left, bottom - top); +} + +void WebViewHost::paintRect(const WebRect& rect) +{ + ASSERT(!m_isPainting); + ASSERT(canvas()); + m_isPainting = true; +#if PLATFORM(CG) + webWidget()->paint(m_canvas->getTopPlatformDevice().GetBitmapContext(), rect); +#else + webWidget()->paint(m_canvas.get(), rect); +#endif + m_isPainting = false; +} + +void WebViewHost::paintInvalidatedRegion() +{ + webWidget()->layout(); + WebSize widgetSize = webWidget()->size(); + WebRect clientRect(0, 0, widgetSize.width, widgetSize.height); + + // Paint the canvas if necessary. Allow painting to generate extra rects the + // first time we call it. This is necessary because some WebCore rendering + // objects update their layout only when painted. + // Store the total area painted in total_paint. Then tell the gdk window + // to update that area after we're done painting it. + for (int i = 0; i < 2; ++i) { + // m_paintRect = intersect(m_paintRect , clientRect) + int left = max(m_paintRect.x, clientRect.x); + int top = max(m_paintRect.y, clientRect.y); + int right = min(m_paintRect.x + m_paintRect.width, clientRect.x + clientRect.width); + int bottom = min(m_paintRect.y + m_paintRect.height, clientRect.y + clientRect.height); + if (left >= right || top >= bottom) + m_paintRect = WebRect(); + else + m_paintRect = WebRect(left, top, right - left, bottom - top); + + if (m_paintRect.isEmpty()) + continue; + WebRect rect(m_paintRect); + m_paintRect = WebRect(); + paintRect(rect); + if (i == 1) + LOG_ERROR("painting caused additional invalidations"); + } + ASSERT(m_paintRect.isEmpty()); +} + +PlatformCanvas* WebViewHost::canvas() +{ + if (m_canvas) + return m_canvas.get(); + WebSize widgetSize = webWidget()->size(); + resetScrollRect(); + m_paintRect = WebRect(0, 0, widgetSize.width, widgetSize.height); + m_canvas.set(new PlatformCanvas(widgetSize.width, widgetSize.height, true)); + return m_canvas.get(); +} + +void WebViewHost::resetScrollRect() +{ +} + +void WebViewHost::discardBackingStore() +{ + m_canvas.clear(); +} + +// Paints the entire canvas a semi-transparent black (grayish). This is used +// by the layout tests in fast/repaint. The alpha value matches upstream. +void WebViewHost::displayRepaintMask() +{ + canvas()->drawARGB(167, 0, 0, 0); +} diff --git a/WebKitTools/DumpRenderTree/chromium/WebViewHost.h b/WebKitTools/DumpRenderTree/chromium/WebViewHost.h new file mode 100644 index 0000000..fd1e0f5 --- /dev/null +++ b/WebKitTools/DumpRenderTree/chromium/WebViewHost.h @@ -0,0 +1,271 @@ +/* + * Copyright (C) 2010 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef WebViewHost_h +#define WebViewHost_h + +#include "MockSpellCheck.h" +#include "TestNavigationController.h" +#include "public/WebCursorInfo.h" +#include "public/WebFrameClient.h" +#include "public/WebViewClient.h" +#include +#include + +class LayoutTestController; +class TestShell; +namespace WebKit { +class WebFrame; +class WebURL; +struct WebURLError; +struct WebRect; +} +namespace skia { +class PlatformCanvas; +} + +class WebViewHost : public WebKit::WebViewClient, public WebKit::WebFrameClient, public NavigationHost { + public: + WebViewHost(TestShell* shell); + ~WebViewHost(); + void setWebWidget(WebKit::WebWidget* widget) { m_webWidget = widget; } + WebKit::WebView* webView() const; + WebKit::WebWidget* webWidget() const; + void reset(); + void setSelectTrailingWhitespaceEnabled(bool); + void setSmartInsertDeleteEnabled(bool); + void waitForPolicyDelegate(); + void setCustomPolicyDelegate(bool, bool); + WebKit::WebFrame* topLoadingFrame() { return m_topLoadingFrame; } + void setBlockRedirects(bool block) { m_blocksRedirects = block; } + void setRequestReturnNull(bool returnNull) { m_requestReturnNull = returnNull; } + void setEditCommand(const std::string& name, const std::string& value); + void clearEditCommand(); + void setPendingExtraData(TestShellExtraData*); + + void paintRect(const WebKit::WebRect&); + void updatePaintRect(const WebKit::WebRect&); + void paintInvalidatedRegion(); + skia::PlatformCanvas* canvas(); + void displayRepaintMask(); + + void loadURLForFrame(const WebKit::WebURL&, const WebKit::WebString& frameName); + TestNavigationController* navigationController() { return m_navigationController.get(); } + + // NavigationHost + virtual bool navigate(const TestNavigationEntry&, bool reload); + + // WebKit::WebViewClient + virtual WebKit::WebView* createView(WebKit::WebFrame*); + virtual WebKit::WebWidget* createPopupMenu(bool activatable); + virtual WebKit::WebWidget* createPopupMenu(const WebKit::WebPopupMenuInfo&); + virtual WebKit::WebStorageNamespace* createSessionStorageNamespace(); + virtual void didAddMessageToConsole(const WebKit::WebConsoleMessage&, const WebKit::WebString& sourceName, unsigned sourceLine); + virtual void didStartLoading(); + virtual void didStopLoading(); + virtual bool shouldBeginEditing(const WebKit::WebRange&); + virtual bool shouldEndEditing(const WebKit::WebRange&); + virtual bool shouldInsertNode(const WebKit::WebNode&, const WebKit::WebRange&, WebKit::WebEditingAction); + virtual bool shouldInsertText(const WebKit::WebString&, const WebKit::WebRange&, WebKit::WebEditingAction); + virtual bool shouldChangeSelectedRange(const WebKit::WebRange& from, const WebKit::WebRange& to, WebKit::WebTextAffinity, bool stillSelecting); + virtual bool shouldDeleteRange(const WebKit::WebRange&); + virtual bool shouldApplyStyle(const WebKit::WebString& style, const WebKit::WebRange&); + virtual bool isSmartInsertDeleteEnabled(); + virtual bool isSelectTrailingWhitespaceEnabled(); + virtual void didBeginEditing(); + virtual void didChangeSelection(bool isSelectionEmpty); + virtual void didChangeContents(); + virtual void didEndEditing(); + virtual bool handleCurrentKeyboardEvent(); + virtual void spellCheck(const WebKit::WebString&, int& offset, int& length); + virtual WebKit::WebString autoCorrectWord(const WebKit::WebString&); + virtual void runModalAlertDialog(WebKit::WebFrame*, const WebKit::WebString&); + virtual bool runModalConfirmDialog(WebKit::WebFrame*, const WebKit::WebString&); + virtual bool runModalPromptDialog(WebKit::WebFrame*, const WebKit::WebString& message, const WebKit::WebString& defaultValue, WebKit::WebString* actualValue); + virtual bool runModalBeforeUnloadDialog(WebKit::WebFrame*, const WebKit::WebString&); + virtual void showContextMenu(WebKit::WebFrame*, const WebKit::WebContextMenuData&); + virtual void setStatusText(const WebKit::WebString&); + virtual void startDragging(const WebKit::WebPoint&, const WebKit::WebDragData&, WebKit::WebDragOperationsMask); + virtual void navigateBackForwardSoon(int offset); + virtual int historyBackListCount(); + virtual int historyForwardListCount(); + virtual void focusAccessibilityObject(const WebKit::WebAccessibilityObject&); + + // WebKit::WebWidgetClient + virtual void didInvalidateRect(const WebKit::WebRect&); + virtual void didScrollRect(int dx, int dy, const WebKit::WebRect&); + virtual void didFocus(); + virtual void didBlur(); + virtual void didChangeCursor(const WebKit::WebCursorInfo&); + virtual void closeWidgetSoon(); + virtual void show(WebKit::WebNavigationPolicy); + virtual void runModal(); + virtual WebKit::WebRect windowRect(); + virtual void setWindowRect(const WebKit::WebRect&); + virtual WebKit::WebRect rootWindowRect(); + virtual WebKit::WebRect windowResizerRect(); + virtual WebKit::WebScreenInfo screenInfo(); + + // WebKit::WebFrameClient + virtual WebKit::WebPlugin* createPlugin(WebKit::WebFrame*, const WebKit::WebPluginParams&); + virtual WebKit::WebWorker* createWorker(WebKit::WebFrame*, WebKit::WebWorkerClient*); + virtual WebKit::WebMediaPlayer* createMediaPlayer(WebKit::WebFrame*, WebKit::WebMediaPlayerClient*); + virtual bool allowPlugins(WebKit::WebFrame*, bool enabledPerSettings); + virtual bool allowImages(WebKit::WebFrame*, bool enabledPerSettings); + virtual void loadURLExternally(WebKit::WebFrame*, const WebKit::WebURLRequest&, WebKit::WebNavigationPolicy); + virtual WebKit::WebNavigationPolicy decidePolicyForNavigation( + WebKit::WebFrame*, const WebKit::WebURLRequest&, + WebKit::WebNavigationType, const WebKit::WebNode&, + WebKit::WebNavigationPolicy, bool isRedirect); + virtual bool canHandleRequest(WebKit::WebFrame*, const WebKit::WebURLRequest&); + virtual WebKit::WebURLError cannotHandleRequestError(WebKit::WebFrame*, const WebKit::WebURLRequest&); + virtual WebKit::WebURLError cancelledError(WebKit::WebFrame*, const WebKit::WebURLRequest&); + virtual void unableToImplementPolicyWithError(WebKit::WebFrame*, const WebKit::WebURLError&); + virtual void willPerformClientRedirect( + WebKit::WebFrame*, const WebKit::WebURL& from, const WebKit::WebURL& to, + double interval, double fireTime); + virtual void didCancelClientRedirect(WebKit::WebFrame*); + virtual void didCreateDataSource(WebKit::WebFrame*, WebKit::WebDataSource*); + virtual void didStartProvisionalLoad(WebKit::WebFrame*); + virtual void didReceiveServerRedirectForProvisionalLoad(WebKit::WebFrame*); + virtual void didFailProvisionalLoad(WebKit::WebFrame*, const WebKit::WebURLError&); + virtual void didCommitProvisionalLoad(WebKit::WebFrame*, bool isNewNavigation); + virtual void didClearWindowObject(WebKit::WebFrame*); + virtual void didReceiveTitle(WebKit::WebFrame*, const WebKit::WebString&); + virtual void didFinishDocumentLoad(WebKit::WebFrame*); + virtual void didHandleOnloadEvents(WebKit::WebFrame*); + virtual void didFailLoad(WebKit::WebFrame*, const WebKit::WebURLError&); + virtual void didFinishLoad(WebKit::WebFrame*); + virtual void didChangeLocationWithinPage(WebKit::WebFrame*, bool isNewNavigation); + virtual void assignIdentifierToRequest(WebKit::WebFrame*, unsigned identifier, const WebKit::WebURLRequest&); + virtual void willSendRequest(WebKit::WebFrame*, unsigned identifier, WebKit::WebURLRequest&, const WebKit::WebURLResponse&); + virtual void didReceiveResponse(WebKit::WebFrame*, unsigned identifier, const WebKit::WebURLResponse&); + virtual void didFinishResourceLoad(WebKit::WebFrame*, unsigned identifier); + virtual void didFailResourceLoad(WebKit::WebFrame*, unsigned identifier, const WebKit::WebURLError&); + virtual void didDisplayInsecureContent(WebKit::WebFrame*); + virtual void didRunInsecureContent(WebKit::WebFrame*, const WebKit::WebSecurityOrigin&); + virtual bool allowScript(WebKit::WebFrame*, bool enabledPerSettings); + +private: + LayoutTestController* layoutTestController() const; + + // Called the title of the page changes. + // Can be used to update the title of the window. + void setPageTitle(const WebKit::WebString&); + + // Called when the URL of the page changes. + // Extracts the URL and forwards on to SetAddressBarURL(). + void updateAddressBar(WebKit::WebView*); + + // Called when the URL of the page changes. + // Should be used to update the text of the URL bar. + void setAddressBarURL(const WebKit::WebURL&); + + // In the Mac code, this is called to trigger the end of a test after the + // page has finished loading. From here, we can generate the dump for the + // test. + void locationChangeDone(WebKit::WebFrame*); + + void updateForCommittedLoad(WebKit::WebFrame*, bool isNewNavigation); + void updateURL(WebKit::WebFrame*); + void updateSessionHistory(WebKit::WebFrame*); + + // Dumping a frame to the console. + void printFrameDescription(WebKit::WebFrame*); + + bool hasWindow() const { return m_hasWindow; } + void resetScrollRect(); + void discardBackingStore(); + + // Causes navigation actions just printout the intended navigation instead + // of taking you to the page. This is used for cases like mailto, where you + // don't actually want to open the mail program. + bool m_policyDelegateEnabled; + + // Toggles the behavior of the policy delegate. If true, then navigations + // will be allowed. Otherwise, they will be ignored (dropped). + bool m_policyDelegateIsPermissive; + + // If true, the policy delegate will signal layout test completion. + bool m_policyDelegateShouldNotifyDone; + + // Non-owning pointer. The WebViewHost instance is owned by this TestShell instance. + TestShell* m_shell; + + // This delegate works for the following widget. + WebKit::WebWidget* m_webWidget; + + // This is non-0 IFF a load is in progress. + WebKit::WebFrame* m_topLoadingFrame; + + // For tracking session history. See RenderView. + int m_pageId; + int m_lastPageIdUpdated; + + OwnPtr m_pendingExtraData; + + // Maps resource identifiers to a descriptive string. + typedef HashMap ResourceMap; + ResourceMap m_resourceIdentifierMap; + void printResourceDescription(unsigned identifier); + + WebKit::WebCursorInfo m_currentCursor; + + bool m_hasWindow; + WebKit::WebRect m_windowRect; + + // true if we want to enable smart insert/delete. + bool m_smartInsertDeleteEnabled; + + // true if we want to enable selection of trailing whitespaces + bool m_selectTrailingWhitespaceEnabled; + + // true if we should block any redirects + bool m_blocksRedirects; + + // true if we should block (set an empty request for) any requests + bool m_requestReturnNull; + + // Edit command associated to the current keyboard event. + std::string m_editCommandName; + std::string m_editCommandValue; + + // The mock spellchecker used in spellCheck(). + MockSpellCheck m_spellcheck; + + // Painting. + OwnPtr m_canvas; + WebKit::WebRect m_paintRect; + bool m_isPainting; + + OwnPtr m_navigationController; +}; + +#endif // WebViewHost_h diff --git a/WebKitTools/DumpRenderTree/chromium/config.h b/WebKitTools/DumpRenderTree/chromium/config.h new file mode 100644 index 0000000..6029532 --- /dev/null +++ b/WebKitTools/DumpRenderTree/chromium/config.h @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2010 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef config_h +#define config_h + +// To avoid confict of LOG in wtf/Assertions.h and LOG in base/logging.h, +// skip base/loggin.h by defining BASE_LOGGING_H_ and define some macros +// provided by base/logging.h. +// FIXME: Remove this hack! +#include +#define BASE_LOGGING_H_ +#define CHECK(condition) while (false && (condition)) std::cerr +#define DCHECK(condition) while (false && (condition)) std::cerr +#define DCHECK_EQ(a, b) while (false && (a) == (b)) std::cerr +#define DCHECK_NE(a, b) while (false && (a) != (b)) std::cerr + +#include + +// JS_EXPORTDATA is needed to inlucde wtf/WTFString.h. +#if OS(WINDOWS) && !COMPILER(GCC) +#define JS_EXPORTDATA __declspec(dllimport) +#else +#define JS_EXPORTDATA +#endif + +#endif // config_h diff --git a/WebKitTools/DumpRenderTree/fonts/ColorBits-A.png b/WebKitTools/DumpRenderTree/fonts/ColorBits-A.png new file mode 100644 index 0000000..8b9319c Binary files /dev/null and b/WebKitTools/DumpRenderTree/fonts/ColorBits-A.png differ diff --git a/WebKitTools/DumpRenderTree/fonts/ColorBits.ttf b/WebKitTools/DumpRenderTree/fonts/ColorBits.ttf new file mode 100644 index 0000000..cd919e8 Binary files /dev/null and b/WebKitTools/DumpRenderTree/fonts/ColorBits.ttf differ diff --git a/WebKitTools/DumpRenderTree/gtk/AccessibilityControllerGtk.cpp b/WebKitTools/DumpRenderTree/gtk/AccessibilityControllerGtk.cpp index 12653fc..ab9f021 100644 --- a/WebKitTools/DumpRenderTree/gtk/AccessibilityControllerGtk.cpp +++ b/WebKitTools/DumpRenderTree/gtk/AccessibilityControllerGtk.cpp @@ -46,6 +46,12 @@ AccessibilityController::~AccessibilityController() { } +AccessibilityUIElement AccessibilityController::elementAtPoint(int x, int y) +{ + // FIXME: implement + return 0; +} + AccessibilityUIElement AccessibilityController::focusedElement() { AtkObject* accessible = webkit_web_frame_get_focused_accessible_element(mainFrame); diff --git a/WebKitTools/DumpRenderTree/gtk/AccessibilityUIElementGtk.cpp b/WebKitTools/DumpRenderTree/gtk/AccessibilityUIElementGtk.cpp index abfe115..fabada3 100644 --- a/WebKitTools/DumpRenderTree/gtk/AccessibilityUIElementGtk.cpp +++ b/WebKitTools/DumpRenderTree/gtk/AccessibilityUIElementGtk.cpp @@ -75,6 +75,26 @@ void AccessibilityUIElement::getChildrenWithRange(Vector } } +int AccessibilityUIElement::rowCount() +{ + if (!m_element) + return 0; + + ASSERT(ATK_IS_TABLE(m_element)); + + return atk_table_get_n_rows(ATK_TABLE(m_element)); +} + +int AccessibilityUIElement::columnCount() +{ + if (!m_element) + return 0; + + ASSERT(ATK_IS_TABLE(m_element)); + + return atk_table_get_n_columns(ATK_TABLE(m_element)); +} + int AccessibilityUIElement::childrenCount() { if (!m_element) @@ -204,6 +224,11 @@ JSStringRef AccessibilityUIElement::language() return JSStringCreateWithCharacters(0, 0); } +JSStringRef AccessibilityUIElement::helpText() const +{ + return 0; +} + double AccessibilityUIElement::x() { int x, y; @@ -492,6 +517,11 @@ void AccessibilityUIElement::decrement() // FIXME: implement } +void AccessibilityUIElement::press() +{ + // FIXME: implement +} + void AccessibilityUIElement::showMenu() { // FIXME: implement @@ -558,6 +588,11 @@ bool AccessibilityUIElement::addNotificationListener(JSObjectRef functionCallbac return false; } +void AccessibilityUIElement::removeNotificationListener() +{ + // FIXME: implement +} + bool AccessibilityUIElement::isSelectable() const { // FIXME: implement diff --git a/WebKitTools/DumpRenderTree/gtk/DumpRenderTree.cpp b/WebKitTools/DumpRenderTree/gtk/DumpRenderTree.cpp index a2fc79b..e37613d 100644 --- a/WebKitTools/DumpRenderTree/gtk/DumpRenderTree.cpp +++ b/WebKitTools/DumpRenderTree/gtk/DumpRenderTree.cpp @@ -117,6 +117,11 @@ static bool shouldOpenWebInspector(const char* pathOrURL) return strstr(pathOrURL, "inspector/"); } +static bool shouldEnableDeveloperExtras(const char* pathOrURL) +{ + return shouldOpenWebInspector(pathOrURL) || strstr(pathOrURL, "inspector-enabled/"); +} + void dumpFrameScrollPosition(WebKitWebFrame* frame) { @@ -319,6 +324,7 @@ static void resetDefaultsToConsistentValues() "enable-html5-database", TRUE, "enable-html5-local-storage", TRUE, "enable-xss-auditor", FALSE, + "enable-spatial-navigation", FALSE, "javascript-can-open-windows-automatically", TRUE, "enable-offline-web-application-cache", TRUE, "enable-universal-access-from-file-uris", TRUE, @@ -335,6 +341,7 @@ static void resetDefaultsToConsistentValues() "enable-page-cache", FALSE, "auto-resize-window", TRUE, "enable-java-applet", FALSE, + "enable-plugins", TRUE, NULL); webkit_web_frame_clear_main_frame_name(mainFrame); @@ -346,6 +353,16 @@ static void resetDefaultsToConsistentValues() webkit_reset_origin_access_white_lists(); +#ifdef HAVE_LIBSOUP_2_29_90 + SoupSession* session = webkit_get_default_session(); + SoupCookieJar* jar = reinterpret_cast(soup_session_get_feature(session, SOUP_TYPE_COOKIE_JAR)); + + // We only create the jar when the soup backend needs to do + // HTTP. Should we initialize it earlier, perhaps? + if (jar) + g_object_set(G_OBJECT(jar), SOUP_COOKIE_JAR_ACCEPT_POLICY, SOUP_COOKIE_JAR_ACCEPT_NO_THIRD_PARTY, NULL); +#endif + setlocale(LC_ALL, ""); } @@ -468,8 +485,11 @@ static void runTest(const string& testPathOrURL) if (shouldLogFrameLoadDelegates(pathOrURL.c_str())) gLayoutTestController->setDumpFrameLoadCallbacks(true); - if (shouldOpenWebInspector(pathOrURL.c_str())) - gLayoutTestController->showWebInspector(); + if (shouldEnableDeveloperExtras(pathOrURL.c_str())) { + gLayoutTestController->setDeveloperExtrasEnabled(true); + if (shouldOpenWebInspector(pathOrURL.c_str())) + gLayoutTestController->showWebInspector(); + } WorkQueue::shared()->clear(); WorkQueue::shared()->setFrozen(false); @@ -502,7 +522,8 @@ static void runTest(const string& testPathOrURL) gtk_main(); - if (shouldOpenWebInspector(pathOrURL.c_str())) + // If developer extras enabled Web Inspector may have been open by the test. + if (shouldEnableDeveloperExtras(pathOrURL.c_str())) gLayoutTestController->closeWebInspector(); // Also check if we still have opened webViews and free them. @@ -550,7 +571,7 @@ static char* getFrameNameSuitableForTestResult(WebKitWebView* view, WebKitWebFra // This is a bit strange. Shouldn't web_frame_get_name return NULL? if (frameName && (frameName[0] != '\0')) { char* tmp = g_strdup_printf("main frame \"%s\"", frameName); - g_free (frameName); + g_free(frameName); frameName = tmp; } else { g_free(frameName); @@ -559,20 +580,31 @@ static char* getFrameNameSuitableForTestResult(WebKitWebView* view, WebKitWebFra } else if (!frameName || (frameName[0] == '\0')) { g_free(frameName); frameName = g_strdup("frame (anonymous)"); + } else { + char* tmp = g_strdup_printf("frame \"%s\"", frameName); + g_free(frameName); + frameName = tmp; } return frameName; } +static void webViewLoadCommitted(WebKitWebView* view, WebKitWebFrame* frame, void*) +{ + if (!done && gLayoutTestController->dumpFrameLoadCallbacks()) { + char* frameName = getFrameNameSuitableForTestResult(view, frame); + printf("%s - didCommitLoadForFrame\n", frameName); + g_free(frameName); + } +} + + static void webViewLoadFinished(WebKitWebView* view, WebKitWebFrame* frame, void*) { - if (!done && !gLayoutTestController->dumpFrameLoadCallbacks()) { - guint pendingFrameUnloadEvents = webkit_web_frame_get_pending_unload_event_count(frame); - if (pendingFrameUnloadEvents) { - char* frameName = getFrameNameSuitableForTestResult(view, frame); - printf("%s - has %u onunload handler(s)\n", frameName, pendingFrameUnloadEvents); - g_free(frameName); - } + if (!done && gLayoutTestController->dumpFrameLoadCallbacks()) { + char* frameName = getFrameNameSuitableForTestResult(view, frame); + printf("%s - didFinishLoadForFrame\n", frameName); + g_free(frameName); } if (frame != topLoadingFrame) @@ -589,6 +621,31 @@ static void webViewLoadFinished(WebKitWebView* view, WebKitWebFrame* frame, void dump(); } +static void webViewDocumentLoadFinished(WebKitWebView* view, WebKitWebFrame* frame, void*) +{ + if (!done && gLayoutTestController->dumpFrameLoadCallbacks()) { + char* frameName = getFrameNameSuitableForTestResult(view, frame); + printf("%s - didFinishDocumentLoadForFrame\n", frameName); + g_free(frameName); + } else if (!done) { + guint pendingFrameUnloadEvents = webkit_web_frame_get_pending_unload_event_count(frame); + if (pendingFrameUnloadEvents) { + char* frameName = getFrameNameSuitableForTestResult(view, frame); + printf("%s - has %u onunload handler(s)\n", frameName, pendingFrameUnloadEvents); + g_free(frameName); + } + } +} + +static void webViewOnloadEvent(WebKitWebView* view, WebKitWebFrame* frame, void*) +{ + if (!done && gLayoutTestController->dumpFrameLoadCallbacks()) { + char* frameName = getFrameNameSuitableForTestResult(view, frame); + printf("%s - didHandleOnloadEventsForFrame\n", frameName); + g_free(frameName); + } +} + static void webViewWindowObjectCleared(WebKitWebView* view, WebKitWebFrame* frame, JSGlobalContextRef context, JSObjectRef windowObject, gpointer data) { JSValueRef exception = 0; @@ -611,7 +668,26 @@ static void webViewWindowObjectCleared(WebKitWebView* view, WebKitWebFrame* fram static gboolean webViewConsoleMessage(WebKitWebView* view, const gchar* message, unsigned int line, const gchar* sourceId, gpointer data) { - fprintf(stdout, "CONSOLE MESSAGE: line %d: %s\n", line, message); + gchar* testMessage = 0; + const gchar* uriScheme; + + // Tests expect only the filename part of local URIs + uriScheme = g_strstr_len(message, -1, "file://"); + if (uriScheme) { + GString* tempString = g_string_sized_new(strlen(message)); + gchar* filename = g_strrstr(uriScheme, G_DIR_SEPARATOR_S); + + if (filename) { + filename += strlen(G_DIR_SEPARATOR_S); + tempString = g_string_append_len(tempString, message, (uriScheme - message)); + tempString = g_string_append_len(tempString, filename, strlen(filename)); + testMessage = g_string_free(tempString, FALSE); + } + } + + fprintf(stdout, "CONSOLE MESSAGE: line %d: %s\n", line, testMessage ? testMessage : message); + g_free(testMessage); + return TRUE; } @@ -725,6 +801,19 @@ static void databaseQuotaExceeded(WebKitWebView* view, WebKitWebFrame* frame, We webkit_security_origin_set_web_database_quota(origin, 5 * 1024 * 1024); } +static bool +geolocationPolicyDecisionRequested(WebKitWebView*, WebKitWebFrame*, WebKitGeolocationPolicyDecision* decision) +{ + if (!gLayoutTestController->isGeolocationPermissionSet()) + return FALSE; + if (gLayoutTestController->geolocationPermission()) + webkit_geolocation_policy_allow(decision); + else + webkit_geolocation_policy_deny(decision); + + return TRUE; +} + static WebKitWebView* webViewCreate(WebKitWebView*, WebKitWebFrame*); @@ -764,6 +853,7 @@ static WebKitWebView* createWebView() g_object_connect(G_OBJECT(view), "signal::load-started", webViewLoadStarted, 0, "signal::load-finished", webViewLoadFinished, 0, + "signal::load-committed", webViewLoadCommitted, 0, "signal::window-object-cleared", webViewWindowObjectCleared, 0, "signal::console-message", webViewConsoleMessage, 0, "signal::script-alert", webViewScriptAlert, 0, @@ -775,6 +865,9 @@ static WebKitWebView* createWebView() "signal::create-web-view", webViewCreate, 0, "signal::close-web-view", webViewClose, 0, "signal::database-quota-exceeded", databaseQuotaExceeded, 0, + "signal::document-load-finished", webViewDocumentLoadFinished, 0, + "signal::geolocation-policy-decision-requested", geolocationPolicyDecisionRequested, 0, + "signal::onload-event", webViewOnloadEvent, 0, NULL); WebKitWebInspector* inspector = webkit_web_view_get_inspector(view); @@ -806,11 +899,22 @@ static WebKitWebView* webViewCreate(WebKitWebView* view, WebKitWebFrame* frame) return newWebView; } +static void logHandler(const gchar* domain, GLogLevelFlags level, const gchar* message, gpointer data) +{ + if (level < G_LOG_LEVEL_DEBUG) + fprintf(stderr, "%s\n", message); +} + int main(int argc, char* argv[]) { g_thread_init(NULL); gtk_init(&argc, &argv); + // Some plugins might try to use the GLib logger for printing debug + // messages. This will cause tests to fail because of unexpected output. + // We squelch all debug messages sent to the logger. + g_log_set_default_handler(logHandler, 0); + #if PLATFORM(X11) FcInit(); initializeFonts(); diff --git a/WebKitTools/DumpRenderTree/gtk/EventSender.cpp b/WebKitTools/DumpRenderTree/gtk/EventSender.cpp index 458e0ba..9c27d8c 100644 --- a/WebKitTools/DumpRenderTree/gtk/EventSender.cpp +++ b/WebKitTools/DumpRenderTree/gtk/EventSender.cpp @@ -557,8 +557,18 @@ static JSValueRef keyDownCallback(JSContextRef context, JSObjectRef function, JS event.key.state = state; event.key.window = GTK_WIDGET(view)->window; + // When synthesizing an event, an invalid hardware_keycode value + // can cause it to be badly processed by Gtk+. + GdkKeymapKey* keys; + gint n_keys; + if (gdk_keymap_get_entries_for_keyval(gdk_keymap_get_default(), gdkKeySym, &keys, &n_keys)) { + event.key.hardware_keycode = keys[0].keycode; + g_free(keys); + } + gboolean return_val; event.key.type = GDK_KEY_PRESS; + g_signal_emit_by_name(view, "key-press-event", &event.key, &return_val); event.key.type = GDK_KEY_RELEASE; @@ -567,47 +577,49 @@ static JSValueRef keyDownCallback(JSContextRef context, JSObjectRef function, JS return JSValueMakeUndefined(context); } -static JSValueRef textZoomInCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception) +static void zoomIn(gboolean fullContentsZoom) { WebKitWebView* view = webkit_web_frame_get_web_view(mainFrame); if (!view) - return JSValueMakeUndefined(context); + return; + webkit_web_view_set_full_content_zoom(view, fullContentsZoom); gfloat currentZoom = webkit_web_view_get_zoom_level(view); webkit_web_view_set_zoom_level(view, currentZoom * zoomMultiplierRatio); - - return JSValueMakeUndefined(context); } -static JSValueRef textZoomOutCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception) +static void zoomOut(gboolean fullContentsZoom) { WebKitWebView* view = webkit_web_frame_get_web_view(mainFrame); if (!view) - return JSValueMakeUndefined(context); + return; + webkit_web_view_set_full_content_zoom(view, fullContentsZoom); gfloat currentZoom = webkit_web_view_get_zoom_level(view); webkit_web_view_set_zoom_level(view, currentZoom / zoomMultiplierRatio); +} +static JSValueRef textZoomInCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception) +{ + zoomIn(FALSE); return JSValueMakeUndefined(context); } -static JSValueRef zoomPageInCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception) +static JSValueRef textZoomOutCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception) { - WebKitWebView* view = webkit_web_frame_get_web_view(mainFrame); - if (!view) - return JSValueMakeUndefined(context); + zoomOut(FALSE); + return JSValueMakeUndefined(context); +} - webkit_web_view_zoom_in(view); +static JSValueRef zoomPageInCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception) +{ + zoomIn(TRUE); return JSValueMakeUndefined(context); } static JSValueRef zoomPageOutCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception) { - WebKitWebView* view = webkit_web_frame_get_web_view(mainFrame); - if (!view) - return JSValueMakeUndefined(context); - - webkit_web_view_zoom_out(view); + zoomOut(TRUE); return JSValueMakeUndefined(context); } diff --git a/WebKitTools/DumpRenderTree/gtk/LayoutTestControllerGtk.cpp b/WebKitTools/DumpRenderTree/gtk/LayoutTestControllerGtk.cpp index 668b852..58d2631 100644 --- a/WebKitTools/DumpRenderTree/gtk/LayoutTestControllerGtk.cpp +++ b/WebKitTools/DumpRenderTree/gtk/LayoutTestControllerGtk.cpp @@ -56,7 +56,9 @@ unsigned int webkit_worker_thread_count(void); void webkit_white_list_access_from_origin(const gchar* sourceOrigin, const gchar* destinationProtocol, const gchar* destinationHost, bool allowDestinationSubdomains); gchar* webkit_web_frame_counter_value_for_element_by_id(WebKitWebFrame* frame, const gchar* id); int webkit_web_frame_page_number_for_element_by_id(WebKitWebFrame* frame, const gchar* id, float pageWidth, float pageHeight); +int webkit_web_frame_number_of_pages(WebKitWebFrame* frame, float pageWidth, float pageHeight); void webkit_web_inspector_execute_script(WebKitWebInspector* inspector, long callId, const gchar* script); +gchar* webkit_web_frame_marker_text_for_list_item(WebKitWebFrame* frame, JSContextRef context, JSValueRef nodeObject); } static gchar* copyWebSettingKey(gchar* preferenceKey) @@ -65,12 +67,13 @@ static gchar* copyWebSettingKey(gchar* preferenceKey) if (!keyTable) { // If you add a pref here, make sure you reset the value in - // DumpRenderTree::resetWebViewToConsistentStateBeforeTesting. + // DumpRenderTree::resetDefaultsToConsistentValues. keyTable = g_hash_table_new(g_str_hash, g_str_equal); g_hash_table_insert(keyTable, g_strdup("WebKitJavaScriptEnabled"), g_strdup("enable-scripts")); g_hash_table_insert(keyTable, g_strdup("WebKitDefaultFontSize"), g_strdup("default-font-size")); g_hash_table_insert(keyTable, g_strdup("WebKitEnableCaretBrowsing"), g_strdup("enable-caret-browsing")); g_hash_table_insert(keyTable, g_strdup("WebKitUsesPageCachePreferenceKey"), g_strdup("enable-page-cache")); + g_hash_table_insert(keyTable, g_strdup("WebKitPluginsEnabled"), g_strdup("enable-plugins")); } return g_strdup(static_cast(g_hash_table_lookup(keyTable, preferenceKey))); @@ -141,6 +144,19 @@ void LayoutTestController::keepWebHistory() // FIXME: implement } +JSValueRef LayoutTestController::computedStyleIncludingVisitedInfo(JSContextRef context, JSValueRef value) +{ + // FIXME: Implement this. + return JSValueMakeUndefined(context); +} + +JSRetainPtr LayoutTestController::layerTreeAsText() const +{ + // FIXME: implement + JSRetainPtr string(Adopt, JSStringCreateWithUTF8CString("")); + return string; +} + int LayoutTestController::pageNumberForElementById(JSStringRef id, float pageWidth, float pageHeight) { gchar* idGChar = JSStringCopyUTF8CString(id); @@ -149,10 +165,9 @@ int LayoutTestController::pageNumberForElementById(JSStringRef id, float pageWid return pageNumber; } -int LayoutTestController::numberOfPages(float, float) +int LayoutTestController::numberOfPages(float pageWidth, float pageHeight) { - // FIXME: implement - return -1; + return webkit_web_frame_number_of_pages(mainFrame, pageWidth, pageHeight); } size_t LayoutTestController::webHistoryItemCount() @@ -210,7 +225,19 @@ void LayoutTestController::setAcceptsEditing(bool acceptsEditing) void LayoutTestController::setAlwaysAcceptCookies(bool alwaysAcceptCookies) { - // FIXME: Implement this (and restore the default value before running each test in DumpRenderTree.cpp). +#ifdef HAVE_LIBSOUP_2_29_90 + SoupSession* session = webkit_get_default_session(); + SoupCookieJar* jar = reinterpret_cast(soup_session_get_feature(session, SOUP_TYPE_COOKIE_JAR)); + + SoupCookieJarAcceptPolicy policy; + + if (alwaysAcceptCookies) + policy = SOUP_COOKIE_JAR_ACCEPT_ALWAYS; + else + policy = SOUP_COOKIE_JAR_ACCEPT_NO_THIRD_PARTY; + + g_object_set(G_OBJECT(jar), SOUP_COOKIE_JAR_ACCEPT_POLICY, policy, NULL); +#endif } void LayoutTestController::setCustomPolicyDelegate(bool setDelegate, bool permissive) @@ -224,7 +251,12 @@ void LayoutTestController::waitForPolicyDelegate() setWaitToDump(true); } -void LayoutTestController::whiteListAccessFromOrigin(JSStringRef sourceOrigin, JSStringRef protocol, JSStringRef host, bool includeSubdomains) +void LayoutTestController::setScrollbarPolicy(JSStringRef orientation, JSStringRef policy) +{ + // FIXME: implement +} + +void LayoutTestController::addOriginAccessWhitelistEntry(JSStringRef sourceOrigin, JSStringRef protocol, JSStringRef host, bool includeSubdomains) { gchar* sourceOriginGChar = JSStringCopyUTF8CString(sourceOrigin); gchar* protocolGChar = JSStringCopyUTF8CString(protocol); @@ -235,6 +267,11 @@ void LayoutTestController::whiteListAccessFromOrigin(JSStringRef sourceOrigin, J g_free(hostGChar); } +void LayoutTestController::removeOriginAccessWhitelistEntry(JSStringRef sourceOrigin, JSStringRef protocol, JSStringRef host, bool includeSubdomains) +{ + // FIXME: implement +} + void LayoutTestController::setMainFrameIsFirstResponder(bool flag) { // FIXME: implement @@ -303,7 +340,7 @@ static gboolean waitToDumpWatchdogFired(void*) void LayoutTestController::setWaitToDump(bool waitUntilDone) { - static const int timeoutSeconds = 15; + static const int timeoutSeconds = 30; m_waitToDump = waitUntilDone; if (m_waitToDump && !waitToDumpWatchdog) @@ -334,11 +371,20 @@ void LayoutTestController::setXSSAuditorEnabled(bool flag) g_object_set(G_OBJECT(settings), "enable-xss-auditor", flag, NULL); } -void LayoutTestController::setFrameSetFlatteningEnabled(bool flag) +void LayoutTestController::setFrameFlatteningEnabled(bool flag) { // FIXME: implement } +void LayoutTestController::setSpatialNavigationEnabled(bool flag) +{ + WebKitWebView* view = webkit_web_frame_get_web_view(mainFrame); + ASSERT(view); + + WebKitWebSettings* settings = webkit_web_view_get_settings(view); + g_object_set(G_OBJECT(settings), "enable-spatial-navigation", flag, NULL); +} + void LayoutTestController::setAllowUniversalAccessFromFileURLs(bool flag) { WebKitWebView* view = webkit_web_frame_get_web_view(mainFrame); @@ -390,8 +436,7 @@ void LayoutTestController::setJavaScriptProfilingEnabled(bool flag) WebKitWebView* view = webkit_web_frame_get_web_view(mainFrame); ASSERT(view); - WebKitWebSettings* settings = webkit_web_view_get_settings(view); - g_object_set(G_OBJECT(settings), "enable-developer-extras", flag, NULL); + setDeveloperExtrasEnabled(flag); WebKitWebInspector* inspector = webkit_web_view_get_inspector(view); g_object_set(G_OBJECT(inspector), "javascript-profiling-enabled", flag, NULL); @@ -556,24 +601,28 @@ void LayoutTestController::addUserStyleSheet(JSStringRef source) printf("LayoutTestController::addUserStyleSheet not implemented.\n"); } -void LayoutTestController::showWebInspector() +void LayoutTestController::setDeveloperExtrasEnabled(bool enabled) { WebKitWebView* webView = webkit_web_frame_get_web_view(mainFrame); WebKitWebSettings* webSettings = webkit_web_view_get_settings(webView); + + g_object_set(webSettings, "enable-developer-extras", enabled, NULL); +} + +void LayoutTestController::showWebInspector() +{ + WebKitWebView* webView = webkit_web_frame_get_web_view(mainFrame); WebKitWebInspector* inspector = webkit_web_view_get_inspector(webView); - g_object_set(webSettings, "enable-developer-extras", TRUE, NULL); webkit_web_inspector_show(inspector); } void LayoutTestController::closeWebInspector() { WebKitWebView* webView = webkit_web_frame_get_web_view(mainFrame); - WebKitWebSettings* webSettings = webkit_web_view_get_settings(webView); WebKitWebInspector* inspector = webkit_web_view_get_inspector(webView); webkit_web_inspector_close(inspector); - g_object_set(webSettings, "enable-developer-extras", FALSE, NULL); } void LayoutTestController::evaluateInWebInspector(long callId, JSStringRef script) @@ -596,7 +645,37 @@ void LayoutTestController::removeAllVisitedLinks() // FIXME: Implement this. } +bool LayoutTestController::callShouldCloseOnWebView() +{ + // FIXME: Implement for testing fix for https://bugs.webkit.org/show_bug.cgi?id=27481 + return false; +} + void LayoutTestController::apiTestNewWindowDataLoadBaseURL(JSStringRef utf8Data, JSStringRef baseURL) { } + +void LayoutTestController::apiTestGoToCurrentBackForwardItem() +{ + +} + +void LayoutTestController::setWebViewEditable(bool) +{ +} + +JSRetainPtr LayoutTestController::markerTextForListItem(JSContextRef context, JSValueRef nodeObject) const +{ + gchar* markerTextGChar = webkit_web_frame_marker_text_for_list_item(mainFrame, context, nodeObject); + if (!markerTextGChar) + return 0; + + JSRetainPtr markerText(Adopt, JSStringCreateWithUTF8CString(markerTextGChar)); + g_free(markerTextGChar); + return markerText; +} + +void LayoutTestController::authenticateSession(JSStringRef, JSStringRef, JSStringRef) +{ +} diff --git a/WebKitTools/DumpRenderTree/mac/AccessibilityControllerMac.mm b/WebKitTools/DumpRenderTree/mac/AccessibilityControllerMac.mm index 4d2da6e..9d7edef 100644 --- a/WebKitTools/DumpRenderTree/mac/AccessibilityControllerMac.mm +++ b/WebKitTools/DumpRenderTree/mac/AccessibilityControllerMac.mm @@ -40,6 +40,12 @@ AccessibilityController::~AccessibilityController() { } +AccessibilityUIElement AccessibilityController::elementAtPoint(int x, int y) +{ + id accessibilityObject = [[[mainFrame frameView] documentView] accessibilityHitTest:NSMakePoint(x, y)]; + return AccessibilityUIElement(accessibilityObject); +} + AccessibilityUIElement AccessibilityController::focusedElement() { // FIXME: we could do some caching here. diff --git a/WebKitTools/DumpRenderTree/mac/AccessibilityUIElementMac.mm b/WebKitTools/DumpRenderTree/mac/AccessibilityUIElementMac.mm index e9361f2..a39dabb 100644 --- a/WebKitTools/DumpRenderTree/mac/AccessibilityUIElementMac.mm +++ b/WebKitTools/DumpRenderTree/mac/AccessibilityUIElementMac.mm @@ -57,32 +57,11 @@ typedef void (*AXPostedNotificationCallback)(id element, NSString* notification, @interface NSObject (WebKitAccessibilityAdditions) - (NSArray *)accessibilityArrayAttributeValues:(NSString *)attribute index:(NSUInteger)index maxCount:(NSUInteger)maxCount; -- (void)accessibilitySetPostedNotificationCallback:(AXPostedNotificationCallback)function withContext:(void*)context; +- (void)accessibilitySetShouldRepostNotifications:(BOOL)repost; - (NSUInteger)accessibilityIndexOfChild:(id)child; +- (NSUInteger)accessibilityArrayAttributeCount:(NSString *)attribute; @end -AccessibilityUIElement::AccessibilityUIElement(PlatformUIElement element) - : m_element(element) - , m_notificationFunctionCallback(0) -{ - [m_element retain]; -} - -AccessibilityUIElement::AccessibilityUIElement(const AccessibilityUIElement& other) - : m_element(other.m_element) - , m_notificationFunctionCallback(0) -{ - [m_element retain]; -} - -AccessibilityUIElement::~AccessibilityUIElement() -{ - // Make sure that our notification callback does not stick around. - if (m_notificationFunctionCallback) - [m_element accessibilitySetPostedNotificationCallback:0 withContext:0]; - [m_element release]; -} - @interface NSString (JSStringRefAdditions) + (NSString *)stringWithJSStringRef:(JSStringRef)jsStringRef; - (JSStringRef)createJSStringRef; @@ -106,6 +85,88 @@ AccessibilityUIElement::~AccessibilityUIElement() @end +@interface AccessibilityNotificationHandler : NSObject +{ + id m_platformElement; + JSObjectRef m_notificationFunctionCallback; +} + +@end + +@implementation AccessibilityNotificationHandler + +- (id)initWithPlatformElement:(id)platformElement +{ + self = [super init]; + + m_platformElement = platformElement; + + // Once an object starts requesting notifications, it's on for the duration of the program. + // This is to avoid any race conditions between tests turning this flag on and off. Instead + // AccessibilityNotificationHandler can just listen when they want to. + [m_platformElement accessibilitySetShouldRepostNotifications:YES]; + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(_notificationReceived:) name:@"AXDRTNotification" object:nil]; + + return self; +} + +- (void)dealloc +{ + [[NSNotificationCenter defaultCenter] removeObserver:self]; + JSValueUnprotect([mainFrame globalContext], m_notificationFunctionCallback); + m_notificationFunctionCallback = 0; + + [super dealloc]; +} + +- (void)_notificationReceived:(NSNotification *)notification +{ + NSString *notificationName = [[notification userInfo] objectForKey:@"notificationName"]; + if (!notificationName) + return; + + JSRetainPtr jsNotification(Adopt, [notificationName createJSStringRef]); + JSValueRef argument = JSValueMakeString([mainFrame globalContext], jsNotification.get()); + JSObjectCallAsFunction([mainFrame globalContext], m_notificationFunctionCallback, 0, 1, &argument, 0); +} + +- (void)setCallback:(JSObjectRef)callback +{ + if (!callback) + return; + + // Release the old callback. + if (m_notificationFunctionCallback) + JSValueUnprotect([mainFrame globalContext], m_notificationFunctionCallback); + + m_notificationFunctionCallback = callback; + JSValueProtect([mainFrame globalContext], m_notificationFunctionCallback); +} + +@end + +AccessibilityUIElement::AccessibilityUIElement(PlatformUIElement element) + : m_element(element) + , m_notificationHandler(0) +{ + // FIXME: ap@webkit.org says ObjC objects need to be CFRetained/CFRelease to be GC-compliant on the mac. + [m_element retain]; +} + +AccessibilityUIElement::AccessibilityUIElement(const AccessibilityUIElement& other) + : m_element(other.m_element) + , m_notificationHandler(0) +{ + [m_element retain]; +} + +AccessibilityUIElement::~AccessibilityUIElement() +{ + // The notification handler should be nil because removeNotificationListener() should have been called in the test. + ASSERT(!m_notificationHandler); + [m_element release]; +} + static NSString* descriptionOfValue(id valueObject, id focusedAccessibilityObject) { if (!valueObject) @@ -439,6 +500,12 @@ JSStringRef AccessibilityUIElement::language() return concatenateAttributeAndValue(@"AXLanguage", description); } +JSStringRef AccessibilityUIElement::helpText() const +{ + id description = descriptionOfValue([m_element accessibilityAttributeValue:NSAccessibilityHelpAttribute], m_element); + return concatenateAttributeAndValue(@"AXHelp", description); +} + double AccessibilityUIElement::x() { NSValue* positionValue = [m_element accessibilityAttributeValue:NSAccessibilityPositionAttribute]; @@ -676,6 +743,16 @@ JSStringRef AccessibilityUIElement::attributesOfHeader() return descriptionOfElements(headerVector); } +int AccessibilityUIElement::rowCount() +{ + return [m_element accessibilityArrayAttributeCount:NSAccessibilityRowsAttribute]; +} + +int AccessibilityUIElement::columnCount() +{ + return [m_element accessibilityArrayAttributeCount:NSAccessibilityColumnsAttribute]; +} + int AccessibilityUIElement::indexInTable() { NSNumber* indexNumber = [m_element accessibilityAttributeValue:NSAccessibilityIndexAttribute]; @@ -736,6 +813,11 @@ void AccessibilityUIElement::showMenu() [m_element accessibilityPerformAction:NSAccessibilityShowMenuAction]; } +void AccessibilityUIElement::press() +{ + [m_element accessibilityPerformAction:NSAccessibilityPressAction]; +} + JSStringRef AccessibilityUIElement::accessibilityValue() const { // FIXME: implement @@ -758,28 +840,30 @@ JSStringRef AccessibilityUIElement::url() return [[url absoluteString] createJSStringRef]; } -static void _accessibilityNotificationCallback(id element, NSString* notification, void* context) -{ - if (!context) - return; - - JSObjectRef functionCallback = static_cast(context); - - JSRetainPtr jsNotification(Adopt, [notification createJSStringRef]); - JSValueRef argument = JSValueMakeString([mainFrame globalContext], jsNotification.get()); - JSObjectCallAsFunction([mainFrame globalContext], functionCallback, NULL, 1, &argument, NULL); -} - bool AccessibilityUIElement::addNotificationListener(JSObjectRef functionCallback) { if (!functionCallback) return false; - m_notificationFunctionCallback = functionCallback; - [platformUIElement() accessibilitySetPostedNotificationCallback:_accessibilityNotificationCallback withContext:reinterpret_cast(m_notificationFunctionCallback)]; + // Mac programmers should not be adding more than one notification listener per element. + // Other platforms may be different. + if (m_notificationHandler) + return false; + m_notificationHandler = [[AccessibilityNotificationHandler alloc] initWithPlatformElement:platformUIElement()]; + [m_notificationHandler setCallback:functionCallback]; + return true; } +void AccessibilityUIElement::removeNotificationListener() +{ + // Mac programmers should not be trying to remove a listener that's already removed. + ASSERT(m_notificationHandler); + + [m_notificationHandler release]; + m_notificationHandler = nil; +} + bool AccessibilityUIElement::isSelectable() const { // FIXME: implement @@ -812,7 +896,9 @@ bool AccessibilityUIElement::isCollapsed() const bool AccessibilityUIElement::hasPopup() const { - // FIXME: implement + id value = [m_element accessibilityAttributeValue:@"AXHasPopup"]; + if ([value isKindOfClass:[NSNumber class]]) + return [value boolValue]; return false; } diff --git a/WebKitTools/DumpRenderTree/mac/Configurations/Base.xcconfig b/WebKitTools/DumpRenderTree/mac/Configurations/Base.xcconfig index a72dd7d..5f989e0 100644 --- a/WebKitTools/DumpRenderTree/mac/Configurations/Base.xcconfig +++ b/WebKitTools/DumpRenderTree/mac/Configurations/Base.xcconfig @@ -34,3 +34,35 @@ GCC_WARN_UNUSED_VARIABLE = YES GCC_WARN_ABOUT_DEPRECATED_FUNCTIONS = NO WARNING_CFLAGS = -Wall -W -Wno-unused-parameter LINKER_DISPLAYS_MANGLED_NAMES = YES; + + +TARGET_MAC_OS_X_VERSION_MAJOR = $(MAC_OS_X_VERSION_MAJOR); + + +// Use GCC 4.2 with Xcode 3.1, which includes GCC 4.2 but defaults to GCC 4.0. +// Note that Xcode versions as new as 3.1.2 use XCODE_VERSION_ACTUAL for the minor version +// number. Newer versions of Xcode use XCODE_VERSION_MINOR for the minor version, and +// XCODE_VERSION_ACTUAL for the full version number. +TARGET_GCC_VERSION = $(TARGET_GCC_VERSION_$(TARGET_MAC_OS_X_VERSION_MAJOR)); +TARGET_GCC_VERSION_ = $(TARGET_GCC_VERSION_1040); +TARGET_GCC_VERSION_1040 = GCC_40; +TARGET_GCC_VERSION_1050 = $(TARGET_GCC_VERSION_1050_$(XCODE_VERSION_MINOR)); +TARGET_GCC_VERSION_1050_ = $(TARGET_GCC_VERSION_1050_$(XCODE_VERSION_ACTUAL)); +TARGET_GCC_VERSION_1050_0310 = GCC_42; +TARGET_GCC_VERSION_1050_0320 = GCC_42; +TARGET_GCC_VERSION_1060 = GCC_42; +TARGET_GCC_VERSION_1070 = LLVM_GCC_42; + +GCC_VERSION = $(GCC_VERSION_$(TARGET_GCC_VERSION)); +GCC_VERSION_GCC_40 = 4.0; +GCC_VERSION_GCC_42 = 4.2; +GCC_VERSION_LLVM_GCC_42 = com.apple.compilers.llvmgcc42; + +// If the target Mac OS X version does not match the current Mac OS X version then we'll want to build using the target version's SDK. +SDKROOT = $(SDKROOT_$(MAC_OS_X_VERSION_MAJOR)_$(TARGET_MAC_OS_X_VERSION_MAJOR)); +SDKROOT_1050_1040 = macosx10.4; +SDKROOT_1060_1040 = macosx10.4; +SDKROOT_1060_1050 = macosx10.5; +SDKROOT_1070_1040 = macosx10.4; +SDKROOT_1070_1050 = macosx10.5; +SDKROOT_1070_1060 = macosx10.6; diff --git a/WebKitTools/DumpRenderTree/mac/Configurations/DebugRelease.xcconfig b/WebKitTools/DumpRenderTree/mac/Configurations/DebugRelease.xcconfig index 96a39a9..ab3278e 100644 --- a/WebKitTools/DumpRenderTree/mac/Configurations/DebugRelease.xcconfig +++ b/WebKitTools/DumpRenderTree/mac/Configurations/DebugRelease.xcconfig @@ -23,7 +23,7 @@ #include "Base.xcconfig" -ARCHS = $(ARCHS_$(MAC_OS_X_VERSION_MAJOR)); +ARCHS = $(ARCHS_$(TARGET_MAC_OS_X_VERSION_MAJOR)); ARCHS_ = $(ARCHS_1040); ARCHS_1040 = $(NATIVE_ARCH); ARCHS_1050 = $(NATIVE_ARCH); @@ -32,7 +32,7 @@ ARCHS_1070 = $(ARCHS_STANDARD_32_64_BIT); ONLY_ACTIVE_ARCH = YES; -MACOSX_DEPLOYMENT_TARGET = $(MACOSX_DEPLOYMENT_TARGET_$(MAC_OS_X_VERSION_MAJOR)) +MACOSX_DEPLOYMENT_TARGET = $(MACOSX_DEPLOYMENT_TARGET_$(TARGET_MAC_OS_X_VERSION_MAJOR)) MACOSX_DEPLOYMENT_TARGET_ = 10.4; MACOSX_DEPLOYMENT_TARGET_1040 = 10.4; MACOSX_DEPLOYMENT_TARGET_1050 = 10.5; diff --git a/WebKitTools/DumpRenderTree/mac/DumpRenderTree.mm b/WebKitTools/DumpRenderTree/mac/DumpRenderTree.mm index c7ddf21..0210cf0 100644 --- a/WebKitTools/DumpRenderTree/mac/DumpRenderTree.mm +++ b/WebKitTools/DumpRenderTree/mac/DumpRenderTree.mm @@ -76,15 +76,21 @@ #import #import #import -#import #import #import #import #import #import +extern "C" { +#import +} + using namespace std; +@interface DumpRenderTreeApplication : NSApplication +@end + @interface DumpRenderTreeEvent : NSEvent @end @@ -246,6 +252,7 @@ static void activateFonts() static const char* fontFileNames[] = { "AHEM____.TTF", + "ColorBits.ttf", "WebKitWeightWatcher100.ttf", "WebKitWeightWatcher200.ttf", "WebKitWeightWatcher300.ttf", @@ -355,7 +362,14 @@ void testStringByEvaluatingJavaScriptFromString() static NSString *libraryPathForDumpRenderTree() { - return [@"~/Library/Application Support/DumpRenderTree" stringByExpandingTildeInPath]; + //FIXME: This may not be sufficient to prevent interactions/crashes + //when running more than one copy of DumpRenderTree. + //See https://bugs.webkit.org/show_bug.cgi?id=10906 + char* dumpRenderTreeTemp = getenv("DUMPRENDERTREE_TEMP"); + if (dumpRenderTreeTemp) + return [[NSFileManager defaultManager] stringWithFileSystemRepresentation:dumpRenderTreeTemp length:strlen(dumpRenderTreeTemp)]; + else + return [@"~/Library/Application Support/DumpRenderTree" stringByExpandingTildeInPath]; } // Called before each test. @@ -420,7 +434,7 @@ static void resetDefaultsToConsistentValues() [preferences setOfflineWebApplicationCacheEnabled:YES]; [preferences setDeveloperExtrasEnabled:NO]; [preferences setLoadsImagesAutomatically:YES]; - [preferences setFrameSetFlatteningEnabled:NO]; + [preferences setFrameFlatteningEnabled:NO]; if (persistentUserStyleSheetLocation) { [preferences setUserStyleSheetLocation:[NSURL URLWithString:(NSString *)(persistentUserStyleSheetLocation.get())]]; [preferences setUserStyleSheetEnabled:YES]; @@ -430,20 +444,8 @@ static void resetDefaultsToConsistentValues() // The back/forward cache is causing problems due to layouts during transition from one page to another. // So, turn it off for now, but we might want to turn it back on some day. [preferences setUsesPageCache:NO]; - -#if defined(BUILDING_ON_LEOPARD) - // Disable hardware composititing to avoid timeouts and crashes from buggy CoreVideo teardown code. - // https://bugs.webkit.org/show_bug.cgi?id=28845 and rdar://problem/7228836 - SInt32 qtVersion; - OSErr err = Gestalt(gestaltQuickTimeVersion, &qtVersion); - assert(err == noErr); - // Bug 7228836 exists in at least 7.6.3 through 7.6.4, hopefully it will be fixed in 7.6.5. - // FIXME: Once we know the exact versions of QuickTime affected, we can update this check. - if (qtVersion <= 0x07648000) // 7.6.4, final release (0x8). See http://developer.apple.com/mac/library/techn - [preferences setAcceleratedCompositingEnabled:NO]; - else -#endif - [preferences setAcceleratedCompositingEnabled:YES]; + [preferences setAcceleratedCompositingEnabled:YES]; + [preferences setWebGLEnabled:NO]; [[NSHTTPCookieStorage sharedHTTPCookieStorage] setCookieAcceptPolicy:NSHTTPCookieAcceptPolicyOnlyFromMainDocumentDomain]; @@ -686,7 +688,7 @@ void dumpRenderTree(int argc, const char *argv[]) int main(int argc, const char *argv[]) { NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; - [NSApplication sharedApplication]; // Force AppKit to init itself + [DumpRenderTreeApplication sharedApplication]; // Force AppKit to init itself dumpRenderTree(argc, argv); [WebCoreStatistics garbageCollectJavaScriptObjects]; [WebCoreStatistics emptyCache]; // Otherwise SVGImages trigger false positives for Frame/Node counts @@ -1139,9 +1141,15 @@ static bool shouldOpenWebInspector(const char* pathOrURL) return strstr(pathOrURL, "inspector/"); } +static bool shouldEnableDeveloperExtras(const char* pathOrURL) +{ + return shouldOpenWebInspector(pathOrURL) || strstr(pathOrURL, "inspector-enabled/"); +} + static void resetWebViewToConsistentStateBeforeTesting() { WebView *webView = [mainFrame webView]; + [webView setEditable:NO]; [(EditingDelegate *)[webView editingDelegate] setAcceptsEditing:YES]; [webView makeTextStandardSize:nil]; [webView resetPageZoom:nil]; @@ -1162,7 +1170,7 @@ static void resetWebViewToConsistentStateBeforeTesting() [[[mainFrame webView] inspector] setJavaScriptProfilingEnabled:NO]; [WebView _setUsesTestModeFocusRingColor:YES]; - [WebView _resetOriginAccessWhiteLists]; + [WebView _resetOriginAccessWhitelists]; } static void runTest(const string& testPathOrURL) @@ -1217,8 +1225,11 @@ static void runTest(const string& testPathOrURL) else [[mainFrame webView] setHistoryDelegate:nil]; - if (shouldOpenWebInspector(pathOrURL.c_str())) - gLayoutTestController->showWebInspector(); + if (shouldEnableDeveloperExtras(pathOrURL.c_str())) { + gLayoutTestController->setDeveloperExtrasEnabled(true); + if (shouldOpenWebInspector(pathOrURL.c_str())) + gLayoutTestController->showWebInspector(); + } if ([WebHistory optionalSharedHistory]) [WebHistory setOptionalSharedHistory:nil]; @@ -1238,9 +1249,10 @@ static void runTest(const string& testPathOrURL) NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; [mainFrame loadRequest:[NSURLRequest requestWithURL:url]]; [pool release]; + while (!done) { pool = [[NSAutoreleasePool alloc] init]; - [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantPast]]; + [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantPast]]; [pool release]; } @@ -1268,7 +1280,8 @@ static void runTest(const string& testPathOrURL) } } - if (shouldOpenWebInspector(pathOrURL.c_str())) + // If developer extras enabled Web Inspector may have been open by the test. + if (shouldEnableDeveloperExtras(pathOrURL.c_str())) gLayoutTestController->closeWebInspector(); resetWebViewToConsistentStateBeforeTesting(); @@ -1307,3 +1320,13 @@ void displayWebView() } @end + +@implementation DumpRenderTreeApplication + +- (BOOL)isRunning +{ + // Java plug-in freezes unless NSApplication is running + return YES; +} + +@end diff --git a/WebKitTools/DumpRenderTree/mac/EventSendingController.mm b/WebKitTools/DumpRenderTree/mac/EventSendingController.mm index feaeddc..8c5cebf 100644 --- a/WebKitTools/DumpRenderTree/mac/EventSendingController.mm +++ b/WebKitTools/DumpRenderTree/mac/EventSendingController.mm @@ -134,7 +134,9 @@ BOOL replayingSavedEvents; || aSelector == @selector(textZoomIn) || aSelector == @selector(textZoomOut) || aSelector == @selector(zoomPageIn) - || aSelector == @selector(zoomPageOut)) + || aSelector == @selector(zoomPageOut) + || aSelector == @selector(mouseScrollByX:andY:) + || aSelector == @selector(continuousMouseScrollByX:andY:)) return NO; return YES; } @@ -166,6 +168,10 @@ BOOL replayingSavedEvents; return @"mouseMoveTo"; if (aSelector == @selector(setDragMode:)) return @"setDragMode"; + if (aSelector == @selector(mouseScrollByX:andY:)) + return @"mouseScrollBy"; + if (aSelector == @selector(continuousMouseScrollByX:andY:)) + return @"continuousMouseScrollBy"; return nil; } @@ -453,6 +459,39 @@ static int buildModifierFlags(const WebScriptObject* modifiers) } } +- (void)mouseScrollByX:(int)x andY:(int)y continuously:(BOOL)c +{ + // CGEventCreateScrollWheelEvent() was introduced in 10.5 +#if !defined(BUILDING_ON_TIGER) + CGScrollEventUnit unit = c?kCGScrollEventUnitPixel:kCGScrollEventUnitLine; + CGEventRef cgScrollEvent = CGEventCreateScrollWheelEvent(NULL, unit, 2, y, x); + + // CGEvent locations are in global display coordinates. + CGPoint lastGlobalMousePosition = { + lastMousePosition.x, + [[NSScreen mainScreen] frame].size.height - lastMousePosition.y + }; + CGEventSetLocation(cgScrollEvent, lastGlobalMousePosition); + + NSEvent *scrollEvent = [NSEvent eventWithCGEvent:cgScrollEvent]; + CFRelease(cgScrollEvent); + + NSView *subView = [[mainFrame webView] hitTest:[scrollEvent locationInWindow]]; + if (subView) + [subView scrollWheel:scrollEvent]; +#endif +} + +- (void)continuousMouseScrollByX:(int)x andY:(int)y +{ + [self mouseScrollByX:x andY:y continuously:YES]; +} + +- (void)mouseScrollByX:(int)x andY:(int)y +{ + [self mouseScrollByX:x andY:y continuously:NO]; +} + - (void)contextClick { [[[mainFrame frameView] documentView] layout]; @@ -507,33 +546,43 @@ static int buildModifierFlags(const WebScriptObject* modifiers) - (void)keyDown:(NSString *)character withModifiers:(WebScriptObject *)modifiers withLocation:(unsigned long)keyLocation { NSString *eventCharacter = character; + unsigned short keyCode = 0; if ([character isEqualToString:@"leftArrow"]) { const unichar ch = NSLeftArrowFunctionKey; eventCharacter = [NSString stringWithCharacters:&ch length:1]; + keyCode = 0x7B; } else if ([character isEqualToString:@"rightArrow"]) { const unichar ch = NSRightArrowFunctionKey; eventCharacter = [NSString stringWithCharacters:&ch length:1]; + keyCode = 0x7C; } else if ([character isEqualToString:@"upArrow"]) { const unichar ch = NSUpArrowFunctionKey; eventCharacter = [NSString stringWithCharacters:&ch length:1]; + keyCode = 0x7E; } else if ([character isEqualToString:@"downArrow"]) { const unichar ch = NSDownArrowFunctionKey; eventCharacter = [NSString stringWithCharacters:&ch length:1]; + keyCode = 0x7D; } else if ([character isEqualToString:@"pageUp"]) { const unichar ch = NSPageUpFunctionKey; eventCharacter = [NSString stringWithCharacters:&ch length:1]; + keyCode = 0x74; } else if ([character isEqualToString:@"pageDown"]) { const unichar ch = NSPageDownFunctionKey; eventCharacter = [NSString stringWithCharacters:&ch length:1]; + keyCode = 0x79; } else if ([character isEqualToString:@"home"]) { const unichar ch = NSHomeFunctionKey; eventCharacter = [NSString stringWithCharacters:&ch length:1]; + keyCode = 0x73; } else if ([character isEqualToString:@"end"]) { const unichar ch = NSEndFunctionKey; eventCharacter = [NSString stringWithCharacters:&ch length:1]; + keyCode = 0x77; } else if ([character isEqualToString:@"delete"]) { const unichar ch = 0x7f; eventCharacter = [NSString stringWithCharacters:&ch length:1]; + keyCode = 0x75; } // Compare the input string with the function-key names defined by the DOM spec (i.e. "F1",...,"F24"). @@ -542,9 +591,59 @@ static int buildModifierFlags(const WebScriptObject* modifiers) if ([character isEqualToString:[NSString stringWithFormat:@"F%u", i]]) { const unichar ch = NSF1FunctionKey + (i - 1); eventCharacter = [NSString stringWithCharacters:&ch length:1]; + switch (i) { + case 1: keyCode = 0x7A; break; + case 2: keyCode = 0x78; break; + case 3: keyCode = 0x63; break; + case 4: keyCode = 0x76; break; + case 5: keyCode = 0x60; break; + case 6: keyCode = 0x61; break; + case 7: keyCode = 0x62; break; + case 8: keyCode = 0x64; break; + case 9: keyCode = 0x65; break; + case 10: keyCode = 0x6D; break; + case 11: keyCode = 0x67; break; + case 12: keyCode = 0x6F; break; + case 13: keyCode = 0x69; break; + case 14: keyCode = 0x6B; break; + case 15: keyCode = 0x71; break; + case 16: keyCode = 0x6A; break; + case 17: keyCode = 0x40; break; + case 18: keyCode = 0x4F; break; + case 19: keyCode = 0x50; break; + case 20: keyCode = 0x5A; break; + } } } + // FIXME: No keyCode is set for most keys. + if ([character isEqualToString:@"\t"]) + keyCode = 0x30; + else if ([character isEqualToString:@" "]) + keyCode = 0x31; + else if ([character isEqualToString:@"\r"]) + keyCode = 0x24; + else if ([character isEqualToString:@"\n"]) + keyCode = 0x4C; + else if ([character isEqualToString:@"\x8"]) + keyCode = 0x33; + else if ([character isEqualToString:@"7"]) + keyCode = 0x1A; + else if ([character isEqualToString:@"5"]) + keyCode = 0x17; + else if ([character isEqualToString:@"9"]) + keyCode = 0x19; + else if ([character isEqualToString:@"0"]) + keyCode = 0x1D; + else if ([character isEqualToString:@"a"]) + keyCode = 0x00; + else if ([character isEqualToString:@"b"]) + keyCode = 0x0B; + else if ([character isEqualToString:@"d"]) + keyCode = 0x02; + else if ([character isEqualToString:@"e"]) + keyCode = 0x0E; + NSString *charactersIgnoringModifiers = eventCharacter; int modifierFlags = 0; @@ -570,7 +669,7 @@ static int buildModifierFlags(const WebScriptObject* modifiers) characters:eventCharacter charactersIgnoringModifiers:charactersIgnoringModifiers isARepeat:NO - keyCode:0]; + keyCode:keyCode]; [[[[mainFrame webView] window] firstResponder] keyDown:event]; @@ -583,7 +682,7 @@ static int buildModifierFlags(const WebScriptObject* modifiers) characters:eventCharacter charactersIgnoringModifiers:charactersIgnoringModifiers isARepeat:NO - keyCode:0]; + keyCode:keyCode]; [[[[mainFrame webView] window] firstResponder] keyUp:event]; } diff --git a/WebKitTools/DumpRenderTree/mac/LayoutTestControllerMac.mm b/WebKitTools/DumpRenderTree/mac/LayoutTestControllerMac.mm index 66ba5f0..e62e411 100644 --- a/WebKitTools/DumpRenderTree/mac/LayoutTestControllerMac.mm +++ b/WebKitTools/DumpRenderTree/mac/LayoutTestControllerMac.mm @@ -115,6 +115,11 @@ void LayoutTestController::addDisallowedURL(JSStringRef url) CFSetAddValue(disallowedURLs, [request URL]); } +bool LayoutTestController::callShouldCloseOnWebView() +{ + return [[mainFrame webView] shouldClose]; +} + void LayoutTestController::clearAllDatabases() { [[WebDatabaseManager sharedWebDatabaseManager] deleteAllDatabases]; @@ -176,6 +181,23 @@ void LayoutTestController::keepWebHistory() } } +JSValueRef LayoutTestController::computedStyleIncludingVisitedInfo(JSContextRef context, JSValueRef value) +{ + return [[mainFrame webView] _computedStyleIncludingVisitedInfo:context forElement:value]; +} + +JSRetainPtr LayoutTestController::layerTreeAsText() const +{ + JSRetainPtr string(Adopt, JSStringCreateWithCFString((CFStringRef)[mainFrame _layerTreeAsText])); + return string; +} + +JSRetainPtr LayoutTestController::markerTextForListItem(JSContextRef context, JSValueRef nodeObject) const +{ + // FIXME: Implement me. + return JSRetainPtr(); +} + int LayoutTestController::pageNumberForElementById(JSStringRef id, float pageWidthInPixels, float pageHeightInPixels) { RetainPtr idCF(AdoptCF, JSStringCopyCFString(kCFAllocatorDefault, id)); @@ -302,7 +324,7 @@ void LayoutTestController::setIconDatabaseEnabled(bool iconDatabaseEnabled) void LayoutTestController::setJavaScriptProfilingEnabled(bool profilingEnabled) { - [[[mainFrame webView] preferences] setDeveloperExtrasEnabled:profilingEnabled]; + setDeveloperExtrasEnabled(profilingEnabled); [[[mainFrame webView] inspector] setJavaScriptProfilingEnabled:profilingEnabled]; } @@ -324,9 +346,14 @@ void LayoutTestController::setXSSAuditorEnabled(bool enabled) [[[mainFrame webView] preferences] setXSSAuditorEnabled:enabled]; } -void LayoutTestController::setFrameSetFlatteningEnabled(bool enabled) +void LayoutTestController::setFrameFlatteningEnabled(bool enabled) { - [[[mainFrame webView] preferences] setFrameSetFlatteningEnabled:enabled]; + [[[mainFrame webView] preferences] setFrameFlatteningEnabled:enabled]; +} + +void LayoutTestController::setSpatialNavigationEnabled(bool enabled) +{ + // FIXME: Implement for SpatialNavigation layout tests. } void LayoutTestController::setAllowUniversalAccessFromFileURLs(bool enabled) @@ -424,7 +451,7 @@ void LayoutTestController::setSelectTrailingWhitespaceEnabled(bool flag) [[mainFrame webView] setSelectTrailingWhitespaceEnabled:flag]; } -static const CFTimeInterval waitToDumpWatchdogInterval = 15.0; +static const CFTimeInterval waitToDumpWatchdogInterval = 30.0; static void waitUntilDoneWatchdogFired(CFRunLoopTimerRef timer, void* info) { @@ -541,7 +568,7 @@ void LayoutTestController::waitForPolicyDelegate() [[mainFrame webView] setPolicyDelegate:policyDelegate]; } -void LayoutTestController::whiteListAccessFromOrigin(JSStringRef sourceOrigin, JSStringRef destinationProtocol, JSStringRef destinationHost, bool allowDestinationSubdomains) +void LayoutTestController::addOriginAccessWhitelistEntry(JSStringRef sourceOrigin, JSStringRef destinationProtocol, JSStringRef destinationHost, bool allowDestinationSubdomains) { RetainPtr sourceOriginCF(AdoptCF, JSStringCopyCFString(kCFAllocatorDefault, sourceOrigin)); NSString *sourceOriginNS = (NSString *)sourceOriginCF.get(); @@ -549,7 +576,23 @@ void LayoutTestController::whiteListAccessFromOrigin(JSStringRef sourceOrigin, J NSString *destinationProtocolNS = (NSString *)protocolCF.get(); RetainPtr hostCF(AdoptCF, JSStringCopyCFString(kCFAllocatorDefault, destinationHost)); NSString *destinationHostNS = (NSString *)hostCF.get(); - [WebView _whiteListAccessFromOrigin:sourceOriginNS destinationProtocol:destinationProtocolNS destinationHost:destinationHostNS allowDestinationSubdomains:allowDestinationSubdomains]; + [WebView _addOriginAccessWhitelistEntryWithSourceOrigin:sourceOriginNS destinationProtocol:destinationProtocolNS destinationHost:destinationHostNS allowDestinationSubdomains:allowDestinationSubdomains]; +} + +void LayoutTestController::removeOriginAccessWhitelistEntry(JSStringRef sourceOrigin, JSStringRef destinationProtocol, JSStringRef destinationHost, bool allowDestinationSubdomains) +{ + RetainPtr sourceOriginCF(AdoptCF, JSStringCopyCFString(kCFAllocatorDefault, sourceOrigin)); + NSString *sourceOriginNS = (NSString *)sourceOriginCF.get(); + RetainPtr protocolCF(AdoptCF, JSStringCopyCFString(kCFAllocatorDefault, destinationProtocol)); + NSString *destinationProtocolNS = (NSString *)protocolCF.get(); + RetainPtr hostCF(AdoptCF, JSStringCopyCFString(kCFAllocatorDefault, destinationHost)); + NSString *destinationHostNS = (NSString *)hostCF.get(); + [WebView _removeOriginAccessWhitelistEntryWithSourceOrigin:sourceOriginNS destinationProtocol:destinationProtocolNS destinationHost:destinationHostNS allowDestinationSubdomains:allowDestinationSubdomains]; +} + +void LayoutTestController::setScrollbarPolicy(JSStringRef orientation, JSStringRef policy) +{ + // FIXME: implement } void LayoutTestController::addUserScript(JSStringRef source, bool runAtStart) @@ -566,16 +609,19 @@ void LayoutTestController::addUserStyleSheet(JSStringRef source) [WebView _addUserStyleSheetToGroup:@"org.webkit.DumpRenderTree" world:[WebScriptWorld world] source:sourceNS url:nil whitelist:nil blacklist:nil]; } +void LayoutTestController::setDeveloperExtrasEnabled(bool enabled) +{ + [[[mainFrame webView] preferences] setDeveloperExtrasEnabled:enabled]; +} + void LayoutTestController::showWebInspector() { - [[[mainFrame webView] preferences] setDeveloperExtrasEnabled:true]; [[[mainFrame webView] inspector] show:nil]; } void LayoutTestController::closeWebInspector() { [[[mainFrame webView] inspector] close:nil]; - [[[mainFrame webView] preferences] setDeveloperExtrasEnabled:false]; } void LayoutTestController::evaluateInWebInspector(long callId, JSStringRef script) @@ -686,3 +732,101 @@ void LayoutTestController::apiTestNewWindowDataLoadBaseURL(JSStringRef utf8Data, [delegate release]; [pool release]; } + +void LayoutTestController::apiTestGoToCurrentBackForwardItem() +{ + WebView *view = [mainFrame webView]; + [view goToBackForwardItem:[[view backForwardList] currentItem]]; +} + +void LayoutTestController::setWebViewEditable(bool editable) +{ + WebView *view = [mainFrame webView]; + [view setEditable:editable]; +} + +#ifndef BUILDING_ON_TIGER +static NSString *SynchronousLoaderRunLoopMode = @"DumpRenderTreeSynchronousLoaderRunLoopMode"; + +@interface SynchronousLoader : NSObject +{ + NSString *m_username; + NSString *m_password; + BOOL m_isDone; +} ++ (void)makeRequest:(NSURLRequest *)request withUsername:(NSString *)username password:(NSString *)password; +@end + +@implementation SynchronousLoader : NSObject +- (void)dealloc +{ + [m_username release]; + [m_password release]; + + [super dealloc]; +} + +- (BOOL)connectionShouldUseCredentialStorage:(NSURLConnection *)connection +{ + return YES; +} + +- (void)connection:(NSURLConnection *)connection didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge +{ + if ([challenge previousFailureCount] == 0) { + NSURLCredential *credential = [[NSURLCredential alloc] initWithUser:m_username password:m_password persistence:NSURLCredentialPersistenceForSession]; + [[challenge sender] useCredential:credential forAuthenticationChallenge:challenge]; + return; + } + [[challenge sender] cancelAuthenticationChallenge:challenge]; +} + +- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error +{ + printf("SynchronousLoader failed: %s\n", [[error description] UTF8String]); + m_isDone = YES; +} + +- (void)connectionDidFinishLoading:(NSURLConnection *)connection +{ + m_isDone = YES; +} + ++ (void)makeRequest:(NSURLRequest *)request withUsername:(NSString *)username password:(NSString *)password +{ + ASSERT(![[request URL] user]); + ASSERT(![[request URL] password]); + + SynchronousLoader *delegate = [[SynchronousLoader alloc] init]; + delegate->m_username = [username copy]; + delegate->m_password = [password copy]; + + NSURLConnection *connection = [[NSURLConnection alloc] initWithRequest:request delegate:delegate startImmediately:NO]; + [connection scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:SynchronousLoaderRunLoopMode]; + [connection start]; + + while (!delegate->m_isDone) + [[NSRunLoop currentRunLoop] runMode:SynchronousLoaderRunLoopMode beforeDate:[NSDate distantFuture]]; + + [connection cancel]; + + [connection release]; + [delegate release]; +} + +@end +#endif + +void LayoutTestController::authenticateSession(JSStringRef url, JSStringRef username, JSStringRef password) +{ + // See . +#if !defined(BUILDING_ON_TIGER) && !defined(BUILDING_ON_LEOPARD) + RetainPtr urlStringCF(AdoptCF, JSStringCopyCFString(kCFAllocatorDefault, url)); + RetainPtr usernameCF(AdoptCF, JSStringCopyCFString(kCFAllocatorDefault, username)); + RetainPtr passwordCF(AdoptCF, JSStringCopyCFString(kCFAllocatorDefault, password)); + + NSURLRequest *request = [[NSURLRequest alloc] initWithURL:[NSURL URLWithString:(NSString *)urlStringCF.get()]]; + + [SynchronousLoader makeRequest:request withUsername:(NSString *)usernameCF.get() password:(NSString *)passwordCF.get()]; +#endif +} diff --git a/WebKitTools/DumpRenderTree/mac/ResourceLoadDelegate.mm b/WebKitTools/DumpRenderTree/mac/ResourceLoadDelegate.mm index 6f82e01..9244110 100644 --- a/WebKitTools/DumpRenderTree/mac/ResourceLoadDelegate.mm +++ b/WebKitTools/DumpRenderTree/mac/ResourceLoadDelegate.mm @@ -35,6 +35,8 @@ #import #import +using namespace std; + @interface NSURL (DRTExtras) - (NSString *)_drt_descriptionSuitableForTestResult; @end @@ -121,10 +123,10 @@ return @""; } --(NSURLRequest *)webView: (WebView *)wv resource:identifier willSendRequest: (NSURLRequest *)newRequest redirectResponse:(NSURLResponse *)redirectResponse fromDataSource:(WebDataSource *)dataSource +-(NSURLRequest *)webView: (WebView *)wv resource:identifier willSendRequest: (NSURLRequest *)request redirectResponse:(NSURLResponse *)redirectResponse fromDataSource:(WebDataSource *)dataSource { if (!done && gLayoutTestController->dumpResourceLoadCallbacks()) { - NSString *string = [NSString stringWithFormat:@"%@ - willSendRequest %@ redirectResponse %@", identifier, [newRequest _drt_descriptionSuitableForTestResult], + NSString *string = [NSString stringWithFormat:@"%@ - willSendRequest %@ redirectResponse %@", identifier, [request _drt_descriptionSuitableForTestResult], [redirectResponse _drt_descriptionSuitableForTestResult]]; printf("%s\n", [string UTF8String]); } @@ -137,7 +139,7 @@ return nil; } - NSURL *url = [newRequest URL]; + NSURL *url = [request URL]; NSString *host = [url host]; if (host && (NSOrderedSame == [[url scheme] caseInsensitiveCompare:@"http"] || NSOrderedSame == [[url scheme] caseInsensitiveCompare:@"https"]) @@ -151,7 +153,15 @@ if (disallowedURLs && CFSetContainsValue(disallowedURLs, url)) return nil; - return newRequest; + NSMutableURLRequest *newRequest = [request mutableCopy]; + const set& clearHeaders = gLayoutTestController->willSendRequestClearHeaders(); + for (set::const_iterator header = clearHeaders.begin(); header != clearHeaders.end(); ++header) { + NSString *nsHeader = [[NSString alloc] initWithUTF8String:header->c_str()]; + [newRequest setValue:nil forHTTPHeaderField:nsHeader]; + [nsHeader release]; + } + + return [newRequest autorelease]; } - (void)webView:(WebView *)wv resource:(id)identifier didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge fromDataSource:(WebDataSource *)dataSource diff --git a/WebKitTools/DumpRenderTree/qt/DumpRenderTree.pro b/WebKitTools/DumpRenderTree/qt/DumpRenderTree.pro index ad42bdd..b66eb5d 100644 --- a/WebKitTools/DumpRenderTree/qt/DumpRenderTree.pro +++ b/WebKitTools/DumpRenderTree/qt/DumpRenderTree.pro @@ -2,12 +2,8 @@ TARGET = DumpRenderTree CONFIG -= app_bundle CONFIG += uitools -mac:!static:contains(QT_CONFIG, qt_framework):!CONFIG(webkit_no_framework) { - CONFIG -= debug - CONFIG += release -} - BASEDIR = $$PWD/../ +isEmpty(OUTPUT_DIR): OUTPUT_DIR = ../../.. include(../../../WebKit.pri) INCLUDEPATH += /usr/include/freetype2 @@ -17,7 +13,7 @@ INCLUDEPATH += ../../../JavaScriptCore/ForwardingHeaders INCLUDEPATH += $$BASEDIR DESTDIR = ../../../bin -!win32 { +!win32:!symbian { CONFIG += link_pkgconfig PKGCONFIG += fontconfig } diff --git a/WebKitTools/DumpRenderTree/qt/DumpRenderTreeQt.cpp b/WebKitTools/DumpRenderTree/qt/DumpRenderTreeQt.cpp index 43f1318..2c2db92 100644 --- a/WebKitTools/DumpRenderTree/qt/DumpRenderTreeQt.cpp +++ b/WebKitTools/DumpRenderTree/qt/DumpRenderTreeQt.cpp @@ -32,6 +32,7 @@ #include "config.h" #include "DumpRenderTreeQt.h" +#include "../../../WebKit/qt/WebCoreSupport/DumpRenderTreeSupportQt.h" #include "EventSenderQt.h" #include "GCControllerQt.h" #include "LayoutTestControllerQt.h" @@ -39,12 +40,11 @@ #include "testplugin.h" #include "WorkQueue.h" +#include #include #include #include #include -#include -#include #include #include #include @@ -52,7 +52,13 @@ #include #include #include +#include +#include +#ifndef QT_NO_PRINTER +#include +#endif #include +#include #include #include @@ -66,6 +72,7 @@ #endif #include +#include #ifndef Q_OS_WIN #include @@ -73,20 +80,12 @@ #include -extern void qt_drt_run(bool b); extern void qt_dump_set_accepts_editing(bool b); extern void qt_dump_frame_loader(bool b); -extern void qt_drt_clearFrameName(QWebFrame* qFrame); -extern void qt_drt_overwritePluginDirectories(); -extern void qt_drt_resetOriginAccessWhiteLists(); -extern bool qt_drt_hasDocumentElement(QWebFrame* qFrame); +extern void qt_dump_resource_load_callbacks(bool b); namespace WebCore { -// Choose some default values. -const unsigned int maxViewWidth = 800; -const unsigned int maxViewHeight = 600; - NetworkAccessManager::NetworkAccessManager(QObject* parent) : QNetworkAccessManager(parent) { @@ -116,6 +115,26 @@ void NetworkAccessManager::sslErrorsEncountered(QNetworkReply* reply, const QLis } #endif + +#ifndef QT_NO_PRINTER +class NullPrinter : public QPrinter { +public: + class NullPaintEngine : public QPaintEngine { + public: + virtual bool begin(QPaintDevice*) { return true; } + virtual bool end() { return true; } + virtual QPaintEngine::Type type() const { return QPaintEngine::User; } + virtual void drawPixmap(const QRectF& r, const QPixmap& pm, const QRectF& sr) { } + virtual void updateState(const QPaintEngineState& state) { } + }; + + virtual QPaintEngine* paintEngine() const { return const_cast(&m_engine); } + + NullPaintEngine m_engine; +}; +#endif + + WebPage::WebPage(QObject* parent, DumpRenderTree* drt) : QWebPage(parent) , m_webInspector(0) @@ -135,6 +154,7 @@ WebPage::WebPage(QObject* parent, DumpRenderTree* drt) globalSettings->setAttribute(QWebSettings::LocalContentCanAccessRemoteUrls, true); globalSettings->setAttribute(QWebSettings::JavascriptEnabled, true); globalSettings->setAttribute(QWebSettings::PrivateBrowsingEnabled, false); + globalSettings->setAttribute(QWebSettings::SpatialNavigationEnabled, false); connect(this, SIGNAL(geometryChangeRequested(const QRect &)), this, SLOT(setViewGeometry(const QRect & ))); @@ -165,9 +185,16 @@ void WebPage::resetSettings() settings()->resetAttribute(QWebSettings::JavascriptCanOpenWindows); settings()->resetAttribute(QWebSettings::JavascriptEnabled); settings()->resetAttribute(QWebSettings::PrivateBrowsingEnabled); + settings()->resetAttribute(QWebSettings::SpatialNavigationEnabled); settings()->resetAttribute(QWebSettings::LinksIncludedInFocusChain); settings()->resetAttribute(QWebSettings::OfflineWebApplicationCacheEnabled); settings()->resetAttribute(QWebSettings::LocalContentCanAccessRemoteUrls); + settings()->resetAttribute(QWebSettings::PluginsEnabled); + + m_drt->layoutTestController()->setCaretBrowsingEnabled(false); + m_drt->layoutTestController()->setFrameFlatteningEnabled(false); + m_drt->layoutTestController()->setSmartInsertDeleteEnabled(true); + m_drt->layoutTestController()->setSelectTrailingWhitespaceEnabled(false); // globalSettings must be reset explicitly. m_drt->layoutTestController()->setXSSAuditorEnabled(false); @@ -315,16 +342,25 @@ DumpRenderTree::DumpRenderTree() , m_enableTextOutput(false) , m_singleFileMode(false) { - qt_drt_overwritePluginDirectories(); - QWebSettings::enablePersistentStorage(); + DumpRenderTreeSupportQt::overwritePluginDirectories(); + + char* dumpRenderTreeTemp = getenv("DUMPRENDERTREE_TEMP"); + if (dumpRenderTreeTemp) + QWebSettings::enablePersistentStorage(QString(dumpRenderTreeTemp)); + else + QWebSettings::enablePersistentStorage(); // create our primary testing page/view. m_mainView = new QWebView(0); - m_mainView->resize(QSize(maxViewWidth, maxViewHeight)); + m_mainView->resize(QSize(LayoutTestController::maxViewWidth, LayoutTestController::maxViewHeight)); m_page = new WebPage(m_mainView, this); m_mainView->setPage(m_page); m_mainView->setContextMenuPolicy(Qt::NoContextMenu); + // clean up cache by resetting quota. + qint64 quota = webPage()->settings()->offlineWebApplicationCacheQuota(); + webPage()->settings()->setOfflineWebApplicationCacheQuota(quota); + // create our controllers. This has to be done before connectFrame, // as it exports there to the JavaScript DOM window. m_controller = new LayoutTestController(this); @@ -348,6 +384,7 @@ DumpRenderTree::DumpRenderTree() connect(m_page, SIGNAL(loadStarted()), m_controller, SLOT(resetLoadFinished())); connect(m_page, SIGNAL(windowCloseRequested()), this, SLOT(windowCloseRequested())); + connect(m_page, SIGNAL(printRequested(QWebFrame*)), this, SLOT(dryRunPrint(QWebFrame*))); connect(m_page->mainFrame(), SIGNAL(titleChanged(const QString&)), SLOT(titleChanged(const QString&))); @@ -357,7 +394,8 @@ DumpRenderTree::DumpRenderTree() this, SLOT(statusBarMessage(const QString&))); QObject::connect(this, SIGNAL(quit()), qApp, SLOT(quit()), Qt::QueuedConnection); - qt_drt_run(true); + + DumpRenderTreeSupportQt::setDumpRenderTreeModeEnabled(true); QFocusEvent event(QEvent::FocusIn, Qt::ActiveWindowFocusReason); QApplication::sendEvent(m_mainView, &event); } @@ -381,6 +419,14 @@ static void clearHistory(QWebPage* page) history->setMaximumItemCount(itemCount); } +void DumpRenderTree::dryRunPrint(QWebFrame* frame) +{ +#ifndef QT_NO_PRINTER + NullPrinter printer; + frame->print(&printer); +#endif +} + void DumpRenderTree::resetToConsistentStateBeforeTesting() { // reset so that any current loads are stopped @@ -395,18 +441,24 @@ void DumpRenderTree::resetToConsistentStateBeforeTesting() // of the DRT. m_controller->reset(); + // reset mouse clicks counter + m_eventSender->resetClickCount(); + closeRemainingWindows(); m_page->resetSettings(); m_page->undoStack()->clear(); m_page->mainFrame()->setZoomFactor(1.0); clearHistory(m_page); - qt_drt_clearFrameName(m_page->mainFrame()); + DumpRenderTreeSupportQt::clearFrameName(m_page->mainFrame()); + + m_page->mainFrame()->setScrollBarPolicy(Qt::Vertical, Qt::ScrollBarAsNeeded); + m_page->mainFrame()->setScrollBarPolicy(Qt::Horizontal, Qt::ScrollBarAsNeeded); WorkQueue::shared()->clear(); WorkQueue::shared()->setFrozen(false); - qt_drt_resetOriginAccessWhiteLists(); + DumpRenderTreeSupportQt::resetOriginAccessWhiteLists(); QLocale::setDefault(QLocale::c()); setlocale(LC_ALL, ""); @@ -419,20 +471,30 @@ static bool isWebInspectorTest(const QUrl& url) return false; } +static bool shouldEnableDeveloperExtras(const QUrl& url) +{ + return isWebInspectorTest(url) || url.path().contains("inspector-enabled/"); +} + void DumpRenderTree::open(const QUrl& url) { resetToConsistentStateBeforeTesting(); - if (isWebInspectorTest(m_page->mainFrame()->url())) + if (shouldEnableDeveloperExtras(m_page->mainFrame()->url())) { layoutTestController()->closeWebInspector(); + layoutTestController()->setDeveloperExtrasEnabled(false); + } - if (isWebInspectorTest(url)) - layoutTestController()->showWebInspector(); + if (shouldEnableDeveloperExtras(url)) { + layoutTestController()->setDeveloperExtrasEnabled(true); + if (isWebInspectorTest(url)) + layoutTestController()->showWebInspector(); + } // W3C SVG tests expect to be 480x360 bool isW3CTest = url.toString().contains("svg/W3C-SVG-1.1"); - int width = isW3CTest ? 480 : maxViewWidth; - int height = isW3CTest ? 360 : maxViewHeight; + int width = isW3CTest ? 480 : LayoutTestController::maxViewWidth; + int height = isW3CTest ? 360 : LayoutTestController::maxViewHeight; m_mainView->resize(QSize(width, height)); m_page->setPreferredContentsSize(QSize()); m_page->setViewportSize(QSize(width, height)); @@ -441,7 +503,9 @@ void DumpRenderTree::open(const QUrl& url) m_page->event(&ev); QWebSettings::clearMemoryCaches(); +#if !(defined(Q_WS_S60) && QT_VERSION <= QT_VERSION_CHECK(4, 6, 2)) QFontDatabase::removeAllApplicationFonts(); +#endif #if defined(Q_WS_X11) initializeFonts(); #endif @@ -557,7 +621,7 @@ void DumpRenderTree::hidePage() QString DumpRenderTree::dumpFramesAsText(QWebFrame* frame) { - if (!frame || !qt_drt_hasDocumentElement(frame)) + if (!frame || !DumpRenderTreeSupportQt::hasDocumentElement(frame)) return QString(); QString result; @@ -666,8 +730,9 @@ static const char *methodNameStringForFailedTest(LayoutTestController *controlle void DumpRenderTree::dump() { - // Prevent any further frame load callbacks from appearing after we dump the result. + // Prevent any further frame load or resource load callbacks from appearing after we dump the result. qt_dump_frame_loader(false); + qt_dump_resource_load_callbacks(false); QWebFrame *mainFrame = m_page->mainFrame(); diff --git a/WebKitTools/DumpRenderTree/qt/DumpRenderTreeQt.h b/WebKitTools/DumpRenderTree/qt/DumpRenderTreeQt.h index 8d80f87..8309492 100644 --- a/WebKitTools/DumpRenderTree/qt/DumpRenderTreeQt.h +++ b/WebKitTools/DumpRenderTree/qt/DumpRenderTreeQt.h @@ -53,6 +53,7 @@ QT_END_NAMESPACE class QWebFrame; class LayoutTestController; +class DumpRenderTreeSupportQt; class EventSender; class TextInputController; class GCController; @@ -118,6 +119,7 @@ Q_SIGNALS: private Q_SLOTS: void showPage(); void hidePage(); + void dryRunPrint(QWebFrame*); private: QString dumpFramesAsText(QWebFrame* frame); diff --git a/WebKitTools/DumpRenderTree/qt/EventSenderQt.cpp b/WebKitTools/DumpRenderTree/qt/EventSenderQt.cpp index 1ef2d3f..7432052 100644 --- a/WebKitTools/DumpRenderTree/qt/EventSenderQt.cpp +++ b/WebKitTools/DumpRenderTree/qt/EventSenderQt.cpp @@ -67,6 +67,8 @@ EventSender::EventSender(QWebPage* parent) endOfQueue = 0; startOfQueue = 0; m_eventLoop = 0; + m_currentButton = 0; + resetClickCount(); m_page->view()->installEventFilter(this); } @@ -92,11 +94,28 @@ void EventSender::mouseDown(int button) break; } + // only consider a click to count, an event originated by the + // same previous button and at the same position. + if (m_currentButton == button + && m_mousePos == m_clickPos + && m_clickTimer.isActive()) + m_clickCount++; + else + m_clickCount = 1; + + m_currentButton = button; + m_clickPos = m_mousePos; m_mouseButtons |= mouseButton; // qDebug() << "EventSender::mouseDown" << frame; - QMouseEvent* event = new QMouseEvent(QEvent::MouseButtonPress, m_mousePos, m_mousePos, mouseButton, m_mouseButtons, Qt::NoModifier); + QMouseEvent* event; + event = new QMouseEvent((m_clickCount == 2) ? QEvent::MouseButtonDblClick : + QEvent::MouseButtonPress, m_mousePos, m_mousePos, + mouseButton, m_mouseButtons, Qt::NoModifier); + sendOrQueueEvent(event); + + m_clickTimer.start(QApplication::doubleClickInterval(), this); } void EventSender::mouseUp(int button) @@ -289,11 +308,15 @@ void EventSender::scheduleAsynchronousClick() void EventSender::addTouchPoint(int x, int y) { #if QT_VERSION >= QT_VERSION_CHECK(4, 6, 0) - int id = m_touchPoints.count(); + // Use index to refer to the position in the vector that this touch + // is stored. We then create a unique id for the touch that will be + // passed into WebCore. + int index = m_touchPoints.count(); + int id = m_touchPoints.isEmpty() ? 0 : m_touchPoints.last().id() + 1; QTouchEvent::TouchPoint point(id); m_touchPoints.append(point); - updateTouchPoint(id, x, y); - m_touchPoints[id].setState(Qt::TouchPointPressed); + updateTouchPoint(index, x, y); + m_touchPoints[index].setState(Qt::TouchPointPressed); #endif } @@ -510,3 +533,8 @@ bool EventSender::eventFilter(QObject* watched, QEvent* event) } return false; } + +void EventSender::timerEvent(QTimerEvent* ev) +{ + m_clickTimer.stop(); +} diff --git a/WebKitTools/DumpRenderTree/qt/EventSenderQt.h b/WebKitTools/DumpRenderTree/qt/EventSenderQt.h index 38bca89..e824e0f 100644 --- a/WebKitTools/DumpRenderTree/qt/EventSenderQt.h +++ b/WebKitTools/DumpRenderTree/qt/EventSenderQt.h @@ -30,6 +30,7 @@ #define EventSenderQt_h #include +#include #include #include #include @@ -50,6 +51,7 @@ class EventSender : public QObject { public: EventSender(QWebPage* parent); virtual bool eventFilter(QObject* watched, QEvent* event); + void resetClickCount() { m_clickCount = 0; } public slots: void mouseDown(int button = 0); @@ -73,18 +75,24 @@ public slots: void clearTouchPoints(); void releaseTouchPoint(int index); +protected: + void timerEvent(QTimerEvent*); + private: void sendTouchEvent(QEvent::Type); void sendOrQueueEvent(QEvent*); void replaySavedEvents(bool flush); QPoint m_mousePos; + QPoint m_clickPos; Qt::MouseButtons m_mouseButtons; QWebPage* m_page; - int m_timeLeap; + int m_clickCount; + int m_currentButton; bool m_mouseButtonPressed; bool m_drag; QEventLoop* m_eventLoop; QWebFrame* frameUnderMouse() const; + QBasicTimer m_clickTimer; #if QT_VERSION >= QT_VERSION_CHECK(4, 6, 0) QList m_touchPoints; Qt::KeyboardModifiers m_touchModifiers; diff --git a/WebKitTools/DumpRenderTree/qt/GCControllerQt.cpp b/WebKitTools/DumpRenderTree/qt/GCControllerQt.cpp index 9cc3aa7..ba7e2c3 100644 --- a/WebKitTools/DumpRenderTree/qt/GCControllerQt.cpp +++ b/WebKitTools/DumpRenderTree/qt/GCControllerQt.cpp @@ -29,14 +29,10 @@ #include "config.h" #include "GCControllerQt.h" +#include "../../../WebKit/qt/WebCoreSupport/DumpRenderTreeSupportQt.h" #include -extern int qt_drt_javaScriptObjectsCount(); -extern void qt_drt_garbageCollector_collect(); - -extern void qt_drt_garbageCollector_collectOnAlternateThread(bool waitUntilDone); - GCController::GCController(QWebPage* parent) : QObject(parent) { @@ -44,15 +40,15 @@ GCController::GCController(QWebPage* parent) void GCController::collect() const { - qt_drt_garbageCollector_collect(); + DumpRenderTreeSupportQt::garbageCollectorCollect(); } void GCController::collectOnAlternateThread(bool waitUntilDone) const { - qt_drt_garbageCollector_collectOnAlternateThread(waitUntilDone); + DumpRenderTreeSupportQt::garbageCollectorCollectOnAlternateThread(waitUntilDone); } size_t GCController::getJSObjectCount() const { - return qt_drt_javaScriptObjectsCount(); + return DumpRenderTreeSupportQt::javaScriptObjectsCount(); } diff --git a/WebKitTools/DumpRenderTree/qt/GCControllerQt.h b/WebKitTools/DumpRenderTree/qt/GCControllerQt.h index 8f5b432..ed2a858 100644 --- a/WebKitTools/DumpRenderTree/qt/GCControllerQt.h +++ b/WebKitTools/DumpRenderTree/qt/GCControllerQt.h @@ -32,6 +32,7 @@ #include class QWebPage; +class DumpRenderTreeSupportQt; class GCController : public QObject { @@ -43,6 +44,7 @@ public slots: void collect() const; void collectOnAlternateThread(bool waitUntilDone) const; size_t getJSObjectCount() const; + }; #endif diff --git a/WebKitTools/DumpRenderTree/qt/ImageDiff.pro b/WebKitTools/DumpRenderTree/qt/ImageDiff.pro index 636835a..74fabf8 100644 --- a/WebKitTools/DumpRenderTree/qt/ImageDiff.pro +++ b/WebKitTools/DumpRenderTree/qt/ImageDiff.pro @@ -1,9 +1,10 @@ TARGET = ImageDiff CONFIG -= app_bundle +isEmpty(OUTPUT_DIR): OUTPUT_DIR = ../../.. include(../../../WebKit.pri) INCLUDEPATH += ../../../JavaScriptCore -DESTDIR = ../../../bin +DESTDIR = $$OUTPUT_DIR/bin QT = core gui diff --git a/WebKitTools/DumpRenderTree/qt/LayoutTestControllerQt.cpp b/WebKitTools/DumpRenderTree/qt/LayoutTestControllerQt.cpp index a26bc3d..b95fe23 100644 --- a/WebKitTools/DumpRenderTree/qt/LayoutTestControllerQt.cpp +++ b/WebKitTools/DumpRenderTree/qt/LayoutTestControllerQt.cpp @@ -28,6 +28,7 @@ */ #include "config.h" #include "LayoutTestControllerQt.h" +#include "../../../WebKit/qt/WebCoreSupport/DumpRenderTreeSupportQt.h" #include "DumpRenderTreeQt.h" #include "WorkQueue.h" @@ -36,32 +37,23 @@ #include #include +extern void qt_wrt_setViewMode(QWebPage* page, const QString& mode); extern void qt_dump_editing_callbacks(bool b); extern void qt_dump_frame_loader(bool b); extern void qt_dump_resource_load_callbacks(bool b); -extern void qt_drt_setFrameSetFlatteningEnabled(QWebPage*, bool); -extern void qt_drt_setJavaScriptProfilingEnabled(QWebFrame*, bool enabled); -extern void qt_drt_setTimelineProfilingEnabled(QWebPage*, bool enabled); -extern bool qt_drt_pauseAnimation(QWebFrame*, const QString& name, double time, const QString& elementId); -extern bool qt_drt_pauseTransitionOfProperty(QWebFrame*, const QString& name, double time, const QString& elementId); -extern bool qt_drt_pauseSVGAnimation(QWebFrame*, const QString& animationId, double time, const QString& elementId); -extern int qt_drt_numberOfActiveAnimations(QWebFrame*); -extern void qt_drt_setDomainRelaxationForbiddenForURLScheme(bool forbidden, const QString& scheme); - -extern void qt_drt_whiteListAccessFromOrigin(const QString& sourceOrigin, const QString& destinationProtocol, const QString& destinationHost, bool allowDestinationSubdomains); -extern QString qt_drt_counterValueForElementById(QWebFrame* qFrame, const QString& id); -extern int qt_drt_workerThreadCount(); -extern int qt_drt_pageNumberForElementById(QWebFrame* qFrame, const QString& id, float width, float height); -extern int qt_drt_numberOfPages(QWebFrame* qFrame, float width, float height); -extern void qt_drt_webinspector_executeScript(QWebPage* page, long callId, const QString& script); -extern void qt_drt_webinspector_show(QWebPage *page); -extern void qt_drt_webinspector_close(QWebPage *page); +extern void qt_set_will_send_request_returns_null_on_redirect(bool b); +extern void qt_set_will_send_request_returns_null(bool b); +extern void qt_set_will_send_request_clear_headers(const QStringList& headers); + +extern void qt_dump_notification(bool b); LayoutTestController::LayoutTestController(WebCore::DumpRenderTree* drt) : QObject() , m_drt(drt) { + qRegisterMetaType("QWebElement"); reset(); + qt_dump_notification(true); } void LayoutTestController::reset() @@ -85,6 +77,9 @@ void LayoutTestController::reset() qt_dump_editing_callbacks(false); qt_dump_frame_loader(false); qt_dump_resource_load_callbacks(false); + qt_set_will_send_request_returns_null_on_redirect(false); + qt_set_will_send_request_returns_null(false); + qt_set_will_send_request_clear_headers(QStringList()); emit hidePage(); } @@ -136,12 +131,17 @@ void LayoutTestController::waitUntilDone() { //qDebug() << ">>>>waitForDone"; m_waitForDone = true; - m_timeoutTimer.start(15000, this); + m_timeoutTimer.start(30000, this); } QString LayoutTestController::counterValueForElementById(const QString& id) { - return qt_drt_counterValueForElementById(m_drt->webPage()->mainFrame(), id); + return DumpRenderTreeSupportQt::counterValueForElementById(m_drt->webPage()->mainFrame(), id); +} + +void LayoutTestController::setViewModeMediaFeature(const QString& mode) +{ + qt_wrt_setViewMode(m_drt->webPage(), mode); } int LayoutTestController::webHistoryItemCount() @@ -188,6 +188,17 @@ int LayoutTestController::windowCount() return m_drt->windowCount(); } +void LayoutTestController::grantDesktopNotificationPermission(const QString& origin) +{ + // FIXME: Implement for notification security +} + +bool LayoutTestController::checkDesktopNotificationPermission(const QString& origin) +{ + // FIXME: Implement for notification security + return true; +} + void LayoutTestController::display() { emit showPage(); @@ -220,6 +231,21 @@ void LayoutTestController::dumpResourceLoadCallbacks() qt_dump_resource_load_callbacks(true); } +void LayoutTestController::setWillSendRequestReturnsNullOnRedirect(bool enabled) +{ + qt_set_will_send_request_returns_null_on_redirect(enabled); +} + +void LayoutTestController::setWillSendRequestReturnsNull(bool enabled) +{ + qt_set_will_send_request_returns_null(enabled); +} + +void LayoutTestController::setWillSendRequestClearHeader(const QStringList& headers) +{ + qt_set_will_send_request_clear_headers(headers); +} + void LayoutTestController::queueBackNavigation(int howFarBackward) { //qDebug() << ">>>queueBackNavigation" << howFarBackward; @@ -290,27 +316,36 @@ QString LayoutTestController::decodeHostName(const QString& host) return decoded; } +void LayoutTestController::setMediaType(const QString& type) +{ + DumpRenderTreeSupportQt::setMediaType(m_drt->webPage()->mainFrame(), type); +} void LayoutTestController::closeWebInspector() { - qt_drt_webinspector_close(m_drt->webPage()); + DumpRenderTreeSupportQt::webInspectorClose(m_drt->webPage()); m_drt->webPage()->settings()->setAttribute(QWebSettings::DeveloperExtrasEnabled, false); } +void LayoutTestController::setDeveloperExtrasEnabled(bool enabled) +{ + m_drt->webPage()->settings()->setAttribute(QWebSettings::DeveloperExtrasEnabled, enabled); +} + void LayoutTestController::showWebInspector() { m_drt->webPage()->settings()->setAttribute(QWebSettings::DeveloperExtrasEnabled, true); - qt_drt_webinspector_show(m_drt->webPage()); + DumpRenderTreeSupportQt::webInspectorShow(m_drt->webPage()); } void LayoutTestController::evaluateInWebInspector(long callId, const QString& script) { - qt_drt_webinspector_executeScript(m_drt->webPage(), callId, script); + DumpRenderTreeSupportQt::webInspectorExecuteScript(m_drt->webPage(), callId, script); } -void LayoutTestController::setFrameSetFlatteningEnabled(bool enabled) +void LayoutTestController::setFrameFlatteningEnabled(bool enabled) { - qt_drt_setFrameSetFlatteningEnabled(m_drt->webPage(), enabled); + DumpRenderTreeSupportQt::setFrameFlatteningEnabled(m_drt->webPage(), enabled); } void LayoutTestController::setAllowUniversalAccessFromFileURLs(bool enabled) @@ -323,15 +358,20 @@ void LayoutTestController::setAllowFileAccessFromFileURLs(bool enabled) m_drt->webPage()->settings()->setAttribute(QWebSettings::LocalContentCanAccessFileUrls, enabled); } +void LayoutTestController::setAppCacheMaximumSize(unsigned long long quota) +{ + m_drt->webPage()->settings()->setOfflineWebApplicationCacheQuota(quota); +} + void LayoutTestController::setJavaScriptProfilingEnabled(bool enable) { - m_topLoadingFrame->page()->settings()->setAttribute(QWebSettings::DeveloperExtrasEnabled, true); - qt_drt_setJavaScriptProfilingEnabled(m_topLoadingFrame, enable); + setDeveloperExtrasEnabled(enable); + DumpRenderTreeSupportQt::setJavaScriptProfilingEnabled(m_topLoadingFrame, enable); } void LayoutTestController::setTimelineProfilingEnabled(bool enable) { - qt_drt_setTimelineProfilingEnabled(m_drt->webPage(), enable); + DumpRenderTreeSupportQt::setTimelineProfilingEnabled(m_drt->webPage(), enable); } void LayoutTestController::setFixedContentsSize(int width, int height) @@ -344,6 +384,11 @@ void LayoutTestController::setPrivateBrowsingEnabled(bool enable) m_drt->webPage()->settings()->setAttribute(QWebSettings::PrivateBrowsingEnabled, enable); } +void LayoutTestController::setSpatialNavigationEnabled(bool enable) +{ + m_drt->webPage()->settings()->setAttribute(QWebSettings::SpatialNavigationEnabled, enable); +} + void LayoutTestController::setPopupBlockingEnabled(bool enable) { m_drt->webPage()->settings()->setAttribute(QWebSettings::JavascriptCanOpenWindows, !enable); @@ -367,12 +412,12 @@ void LayoutTestController::setMainFrameIsFirstResponder(bool isFirst) void LayoutTestController::setXSSAuditorEnabled(bool enable) { - // Set XSSAuditorEnabled globally so that windows created by the test inherit it too. + // Set XSSAuditingEnabled globally so that windows created by the test inherit it too. // resetSettings() will call this to reset the page and global setting to false again. // Needed by http/tests/security/xssAuditor/link-opens-new-window.html QWebSettings* globalSettings = QWebSettings::globalSettings(); - globalSettings->setAttribute(QWebSettings::XSSAuditorEnabled, enable); - m_drt->webPage()->settings()->setAttribute(QWebSettings::XSSAuditorEnabled, enable); + globalSettings->setAttribute(QWebSettings::XSSAuditingEnabled, enable); + m_drt->webPage()->settings()->setAttribute(QWebSettings::XSSAuditingEnabled, enable); } bool LayoutTestController::pauseAnimationAtTimeOnElementWithId(const QString& animationName, @@ -381,7 +426,7 @@ bool LayoutTestController::pauseAnimationAtTimeOnElementWithId(const QString& an { QWebFrame* frame = m_drt->webPage()->mainFrame(); Q_ASSERT(frame); - return qt_drt_pauseAnimation(frame, animationName, time, elementId); + return DumpRenderTreeSupportQt::pauseAnimation(frame, animationName, time, elementId); } bool LayoutTestController::pauseTransitionAtTimeOnElementWithId(const QString& propertyName, @@ -390,7 +435,7 @@ bool LayoutTestController::pauseTransitionAtTimeOnElementWithId(const QString& p { QWebFrame* frame = m_drt->webPage()->mainFrame(); Q_ASSERT(frame); - return qt_drt_pauseTransitionOfProperty(frame, propertyName, time, elementId); + return DumpRenderTreeSupportQt::pauseTransitionOfProperty(frame, propertyName, time, elementId); } bool LayoutTestController::sampleSVGAnimationForElementAtTime(const QString& animationId, @@ -399,14 +444,14 @@ bool LayoutTestController::sampleSVGAnimationForElementAtTime(const QString& ani { QWebFrame* frame = m_drt->webPage()->mainFrame(); Q_ASSERT(frame); - return qt_drt_pauseSVGAnimation(frame, animationId, time, elementId); + return DumpRenderTreeSupportQt::pauseSVGAnimation(frame, animationId, time, elementId); } unsigned LayoutTestController::numberOfActiveAnimations() const { QWebFrame* frame = m_drt->webPage()->mainFrame(); Q_ASSERT(frame); - return qt_drt_numberOfActiveAnimations(frame); + return DumpRenderTreeSupportQt::numberOfActiveAnimations(frame); } void LayoutTestController::disableImageLoading() @@ -432,9 +477,14 @@ void LayoutTestController::clearAllDatabases() QWebDatabase::removeAllDatabases(); } -void LayoutTestController::whiteListAccessFromOrigin(const QString& sourceOrigin, const QString& destinationProtocol, const QString& destinationHost, bool allowDestinationSubdomains) +void LayoutTestController::addOriginAccessWhitelistEntry(const QString& sourceOrigin, const QString& destinationProtocol, const QString& destinationHost, bool allowDestinationSubdomains) +{ + DumpRenderTreeSupportQt::whiteListAccessFromOrigin(sourceOrigin, destinationProtocol, destinationHost, allowDestinationSubdomains); +} + +void LayoutTestController::removeOriginAccessWhitelistEntry(const QString& sourceOrigin, const QString& destinationProtocol, const QString& destinationHost, bool allowDestinationSubdomains) { - qt_drt_whiteListAccessFromOrigin(sourceOrigin, destinationProtocol, destinationHost, allowDestinationSubdomains); + // FIXME: Implement. } void LayoutTestController::waitForPolicyDelegate() @@ -457,6 +507,10 @@ void LayoutTestController::overridePreference(const QString& name, const QVarian settings->setFontSize(QWebSettings::DefaultFontSize, value.toInt()); else if (name == "WebKitUsesPageCachePreferenceKey") QWebSettings::setMaximumPagesInCache(value.toInt()); + else if (name == "WebKitEnableCaretBrowsing") + setCaretBrowsingEnabled(value.toBool()); + else if (name == "WebKitPluginsEnabled") + settings->setAttribute(QWebSettings::PluginsEnabled, value.toBool()); else printf("ERROR: LayoutTestController::overridePreference() does not support the '%s' preference\n", name.toLatin1().data()); @@ -467,6 +521,11 @@ void LayoutTestController::setUserStyleSheetLocation(const QString& url) m_userStyleSheetLocation = QUrl(url); } +void LayoutTestController::setCaretBrowsingEnabled(bool value) +{ + DumpRenderTreeSupportQt::setCaretBrowsingEnabled(m_drt->webPage(), value); +} + void LayoutTestController::setUserStyleSheetEnabled(bool enabled) { if (enabled) @@ -477,12 +536,12 @@ void LayoutTestController::setUserStyleSheetEnabled(bool enabled) void LayoutTestController::setDomainRelaxationForbiddenForURLScheme(bool forbidden, const QString& scheme) { - qt_drt_setDomainRelaxationForbiddenForURLScheme(forbidden, scheme); + DumpRenderTreeSupportQt::setDomainRelaxationForbiddenForURLScheme(forbidden, scheme); } int LayoutTestController::workerThreadCount() { - return qt_drt_workerThreadCount(); + return DumpRenderTreeSupportQt::workerThreadCount(); } int LayoutTestController::pageNumberForElementById(const QString& id, float width, float height) @@ -493,10 +552,74 @@ int LayoutTestController::pageNumberForElementById(const QString& id, float widt height = m_drt->webPage()->viewportSize().height(); } - return qt_drt_pageNumberForElementById(m_drt->webPage()->mainFrame(), id, width, height); + return DumpRenderTreeSupportQt::pageNumberForElementById(m_drt->webPage()->mainFrame(), id, width, height); } int LayoutTestController::numberOfPages(float width, float height) { - return qt_drt_numberOfPages(m_drt->webPage()->mainFrame(), width, height); + return DumpRenderTreeSupportQt::numberOfPages(m_drt->webPage()->mainFrame(), width, height); +} + +bool LayoutTestController::callShouldCloseOnWebView() +{ + // FIXME: Implement for testing fix for https://bugs.webkit.org/show_bug.cgi?id=27481 + return false; +} + +void LayoutTestController::setScrollbarPolicy(const QString& orientation, const QString& policy) +{ + Qt::Orientation o; + Qt::ScrollBarPolicy p; + + if (orientation == "vertical") + o = Qt::Vertical; + else if (orientation == "horizontal") + o = Qt::Horizontal; + else + return; + + if (policy == "on") + p = Qt::ScrollBarAlwaysOn; + else if (policy == "auto") + p = Qt::ScrollBarAsNeeded; + else if (policy == "off") + p = Qt::ScrollBarAlwaysOff; + else + return; + + m_drt->webPage()->mainFrame()->setScrollBarPolicy(o, p); +} + +void LayoutTestController::setSmartInsertDeleteEnabled(bool enable) +{ + DumpRenderTreeSupportQt::setSmartInsertDeleteEnabled(m_drt->webPage(), enable); } + +void LayoutTestController::setSelectTrailingWhitespaceEnabled(bool enable) +{ + DumpRenderTreeSupportQt::setSelectTrailingWhitespaceEnabled(m_drt->webPage(), enable); +} + +void LayoutTestController::execCommand(const QString& name, const QString& value) +{ + DumpRenderTreeSupportQt::executeCoreCommandByName(m_drt->webPage(), name, value); +} + +bool LayoutTestController::isCommandEnabled(const QString& name) const +{ + return DumpRenderTreeSupportQt::isCommandEnabled(m_drt->webPage(), name); +} + +QString LayoutTestController::markerTextForListItem(const QWebElement& listItem) +{ + return DumpRenderTreeSupportQt::markerTextForListItem(listItem); +} + +void LayoutTestController::authenticateSession(const QString&, const QString&, const QString&) +{ + // FIXME: If there is a concept per-session (per-process) credential storage, the credentials should be added to it for later use. +} + + +const unsigned LayoutTestController::maxViewWidth = 800; +const unsigned LayoutTestController::maxViewHeight = 600; diff --git a/WebKitTools/DumpRenderTree/qt/LayoutTestControllerQt.h b/WebKitTools/DumpRenderTree/qt/LayoutTestControllerQt.h index d73794e..df645e1 100644 --- a/WebKitTools/DumpRenderTree/qt/LayoutTestControllerQt.h +++ b/WebKitTools/DumpRenderTree/qt/LayoutTestControllerQt.h @@ -40,12 +40,14 @@ #include #include +#include #include #include #include #include class QWebFrame; +class DumpRenderTreeSupportQt; namespace WebCore { class DumpRenderTree; } @@ -70,6 +72,9 @@ public: void reset(); + static const unsigned int maxViewWidth; + static const unsigned int maxViewHeight; + protected: void timerEvent(QTimerEvent*); @@ -98,6 +103,9 @@ public slots: void dumpEditingCallbacks(); void dumpFrameLoadCallbacks(); void dumpResourceLoadCallbacks(); + void setWillSendRequestReturnsNullOnRedirect(bool enabled); + void setWillSendRequestReturnsNull(bool enabled); + void setWillSendRequestClearHeader(const QStringList& headers); void queueBackNavigation(int howFarBackward); void queueForwardNavigation(int howFarForward); void queueLoad(const QString& url, const QString& target = QString()); @@ -107,6 +115,8 @@ public slots: void provisionalLoad(); void setCloseRemainingWindowsWhenComplete(bool = false) {} int windowCount(); + void grantDesktopNotificationPermission(const QString& origin); + bool checkDesktopNotificationPermission(const QString& origin); void display(); void clearBackForwardList(); QString pathToLocalResource(const QString& url); @@ -114,23 +124,33 @@ public slots: QString encodeHostName(const QString& host); QString decodeHostName(const QString& host); void dumpSelectionRect() const {} + void setDeveloperExtrasEnabled(bool); void showWebInspector(); void closeWebInspector(); void evaluateInWebInspector(long callId, const QString& script); - void setFrameSetFlatteningEnabled(bool enable); + void setMediaType(const QString& type); + void setFrameFlatteningEnabled(bool enable); void setAllowUniversalAccessFromFileURLs(bool enable); void setAllowFileAccessFromFileURLs(bool enable); + void setAppCacheMaximumSize(unsigned long long quota); void setJavaScriptProfilingEnabled(bool enable); void setTimelineProfilingEnabled(bool enable); void setFixedContentsSize(int width, int height); void setPrivateBrowsingEnabled(bool enable); + void setSpatialNavigationEnabled(bool enabled); void setPopupBlockingEnabled(bool enable); void setPOSIXLocale(const QString& locale); void resetLoadFinished() { m_loadFinished = false; } void setWindowIsKey(bool isKey); void setMainFrameIsFirstResponder(bool isFirst); void setXSSAuditorEnabled(bool enable); + void setCaretBrowsingEnabled(bool enable); + void setViewModeMediaFeature(const QString& mode); + void setSmartInsertDeleteEnabled(bool enable); + void setSelectTrailingWhitespaceEnabled(bool enable); + void execCommand(const QString& name, const QString& value = QString()); + bool isCommandEnabled(const QString& name) const; bool pauseAnimationAtTimeOnElementWithId(const QString& animationName, double time, const QString& elementId); bool pauseTransitionAtTimeOnElementWithId(const QString& propertyName, double time, const QString& elementId); @@ -138,7 +158,8 @@ public slots: unsigned numberOfActiveAnimations() const; - void whiteListAccessFromOrigin(const QString& sourceOrigin, const QString& destinationProtocol, const QString& destinationHost, bool allowDestinationSubdomains); + void addOriginAccessWhitelistEntry(const QString& sourceOrigin, const QString& destinationProtocol, const QString& destinationHost, bool allowDestinationSubdomains); + void removeOriginAccessWhitelistEntry(const QString& sourceOrigin, const QString& destinationProtocol, const QString& destinationHost, bool allowDestinationSubdomains); void dispatchPendingLoadRequests(); void disableImageLoading(); @@ -153,7 +174,19 @@ public slots: void setDomainRelaxationForbiddenForURLScheme(bool forbidden, const QString& scheme); int workerThreadCount(); int pageNumberForElementById(const QString& id, float width = 0, float height = 0); - int numberOfPages(float width, float height); + int numberOfPages(float width = maxViewWidth, float height = maxViewHeight); + bool callShouldCloseOnWebView(); + + /* + Policy values: 'on', 'auto' or 'off'. + Orientation values: 'vertical' or 'horizontal'. + */ + void setScrollbarPolicy(const QString& orientation, const QString& policy); + + QString markerTextForListItem(const QWebElement& listItem); + + // Simulate a request an embedding application could make, populating per-session credential storage. + void authenticateSession(const QString& url, const QString& username, const QString& password); private slots: void processWork(); diff --git a/WebKitTools/DumpRenderTree/qt/TestNetscapePlugin/TestNetscapePlugin.pro b/WebKitTools/DumpRenderTree/qt/TestNetscapePlugin/TestNetscapePlugin.pro index 7b8162b..9b19231 100644 --- a/WebKitTools/DumpRenderTree/qt/TestNetscapePlugin/TestNetscapePlugin.pro +++ b/WebKitTools/DumpRenderTree/qt/TestNetscapePlugin/TestNetscapePlugin.pro @@ -2,6 +2,7 @@ TEMPLATE = lib TARGET = TestNetscapePlugIn VPATH = ../../unix/TestNetscapePlugin ../../TestNetscapePlugIn.subproj +isEmpty(OUTPUT_DIR): OUTPUT_DIR = ../../../.. include(../../../../WebKit.pri) DESTDIR = $$OUTPUT_DIR/lib/plugins diff --git a/WebKitTools/DumpRenderTree/qt/main.cpp b/WebKitTools/DumpRenderTree/qt/main.cpp index 7d72982..7d1c08c 100644 --- a/WebKitTools/DumpRenderTree/qt/main.cpp +++ b/WebKitTools/DumpRenderTree/qt/main.cpp @@ -93,7 +93,7 @@ QString get_backtrace() { return s; } -#ifndef Q_OS_WIN +#if HAVE(SIGNAL_H) static NO_RETURN void crashHandler(int sig) { fprintf(stderr, "%s\n", strsignal(sig)); @@ -114,10 +114,7 @@ int main(int argc, char* argv[]) WebCore::DumpRenderTree::initializeFonts(); #endif -#if QT_VERSION >= 0x040500 QApplication::setGraphicsSystem("raster"); -#endif - QApplication::setStyle(new QWindowsStyle); QFont f("Sans Serif"); @@ -132,7 +129,7 @@ int main(int argc, char* argv[]) QX11Info::setAppDpiX(0, 96); #endif -#ifndef Q_OS_WIN +#if HAVE(SIGNAL_H) signal(SIGILL, crashHandler); /* 4: illegal instruction (not reset when caught) */ signal(SIGTRAP, crashHandler); /* 5: trace trap (not reset when caught) */ signal(SIGFPE, crashHandler); /* 8: floating point exception */ diff --git a/WebKitTools/DumpRenderTree/unix/TestNetscapePlugin/TestNetscapePlugin.cpp b/WebKitTools/DumpRenderTree/unix/TestNetscapePlugin/TestNetscapePlugin.cpp index cb01267..a3c6773 100644 --- a/WebKitTools/DumpRenderTree/unix/TestNetscapePlugin/TestNetscapePlugin.cpp +++ b/WebKitTools/DumpRenderTree/unix/TestNetscapePlugin/TestNetscapePlugin.cpp @@ -58,6 +58,7 @@ webkit_test_plugin_new_instance(NPMIMEType /*mimetype*/, { if (browser->version >= 14) { PluginObject* obj = (PluginObject*)browser->createobject(instance, getPluginClass()); + instance->pdata = obj; for (int i = 0; i < argc; i++) { if (strcasecmp(argn[i], "onstreamload") == 0 && !obj->onStreamLoad) @@ -85,8 +86,11 @@ webkit_test_plugin_new_instance(NPMIMEType /*mimetype*/, obj->testDocumentOpenInDestroyStream = TRUE; else if (strcasecmp(argn[i], "testwindowopen") == 0) obj->testWindowOpen = TRUE; + else if (strcasecmp(argn[i], "onSetWindow") == 0 && !obj->onSetWindow) + obj->onSetWindow = strdup(argv[i]); } - instance->pdata = obj; + + browser->getvalue(instance, NPNVprivateModeBool, (void *)&obj->cachedPrivateBrowsingMode); } return NPERR_NO_ERROR; @@ -114,6 +118,9 @@ webkit_test_plugin_destroy_instance(NPP instance, NPSavedData** /*save*/) if (obj->logDestroy) pluginLog(instance, "NPP_Destroy"); + if (obj->onSetWindow) + free(obj->onSetWindow); + browser->releaseobject(&obj->header); } @@ -126,10 +133,14 @@ webkit_test_plugin_set_window(NPP instance, NPWindow *window) PluginObject* obj = static_cast(instance->pdata); if (obj) { + obj->lastWindow = *window; + if (obj->logSetWindow) { pluginLog(instance, "NPP_SetWindow: %d %d", (int)window->width, (int)window->height); obj->logSetWindow = false; } + if (obj->onSetWindow) + executeScript(obj, obj->onSetWindow); if (obj->testWindowOpen) { testWindowOpen(instance); @@ -282,9 +293,17 @@ webkit_test_plugin_get_value(NPP instance, NPPVariable variable, void *value) } static NPError -webkit_test_plugin_set_value(NPP /*instance*/, NPNVariable /*variable*/, void* /*value*/) +webkit_test_plugin_set_value(NPP instance, NPNVariable variable, void* value) { - return NPERR_NO_ERROR; + PluginObject* obj = static_cast(instance->pdata); + + switch (variable) { + case NPNVprivateModeBool: + obj->cachedPrivateBrowsingMode = *(NPBool*)value; + return NPERR_NO_ERROR; + default: + return NPERR_GENERIC_ERROR; + } } char * diff --git a/WebKitTools/DumpRenderTree/win/AccessibilityControllerWin.cpp b/WebKitTools/DumpRenderTree/win/AccessibilityControllerWin.cpp index 6b35948..255bfc3 100644 --- a/WebKitTools/DumpRenderTree/win/AccessibilityControllerWin.cpp +++ b/WebKitTools/DumpRenderTree/win/AccessibilityControllerWin.cpp @@ -59,6 +59,12 @@ AccessibilityController::~AccessibilityController() JSValueUnprotect(frame->globalContext(), it->second); } +AccessibilityUIElement AccessibilityController::elementAtPoint(int x, int y) +{ + // FIXME: implement + return 0; +} + AccessibilityUIElement AccessibilityController::focusedElement() { COMPtr rootAccessible = rootElement().platformUIElement(); diff --git a/WebKitTools/DumpRenderTree/win/AccessibilityUIElementWin.cpp b/WebKitTools/DumpRenderTree/win/AccessibilityUIElementWin.cpp index 301112f..9f00ae4 100644 --- a/WebKitTools/DumpRenderTree/win/AccessibilityUIElementWin.cpp +++ b/WebKitTools/DumpRenderTree/win/AccessibilityUIElementWin.cpp @@ -83,6 +83,18 @@ int AccessibilityUIElement::childrenCount() return childCount; } +int AccessibilityUIElement::rowCount() +{ + // FIXME: implement + return 0; +} + +int AccessibilityUIElement::columnCount() +{ + // FIXME: implement + return 0; +} + AccessibilityUIElement AccessibilityUIElement::elementAtPoint(int x, int y) { return 0; @@ -224,6 +236,11 @@ JSStringRef AccessibilityUIElement::language() return JSStringCreateWithCharacters(0, 0); } +JSStringRef AccessibilityUIElement::helpText() const +{ + return 0; +} + double AccessibilityUIElement::x() { long x, y, width, height; @@ -477,6 +494,11 @@ void AccessibilityUIElement::showMenu() m_element->accDoDefaultAction(self()); } +void AccessibilityUIElement::press() +{ + // FIXME: implement +} + AccessibilityUIElement AccessibilityUIElement::disclosedRowAtIndex(unsigned index) { return 0; @@ -540,6 +562,11 @@ bool AccessibilityUIElement::addNotificationListener(JSObjectRef functionCallbac return true; } +void AccessibilityUIElement::removeNotificationListener() +{ + // FIXME: implement +} + bool AccessibilityUIElement::isSelectable() const { DWORD state = accessibilityState(m_element); diff --git a/WebKitTools/DumpRenderTree/win/DumpRenderTree.cpp b/WebKitTools/DumpRenderTree/win/DumpRenderTree.cpp index ddfca95..f9b40d1 100644 --- a/WebKitTools/DumpRenderTree/win/DumpRenderTree.cpp +++ b/WebKitTools/DumpRenderTree/win/DumpRenderTree.cpp @@ -84,6 +84,7 @@ static bool printSeparators; static bool leakChecking = false; static bool threaded = false; static bool forceComplexText = false; +static bool printSupportedFeatures = false; static RetainPtr persistentUserStyleSheetLocation; volatile bool done; @@ -731,6 +732,11 @@ static bool shouldOpenWebInspector(const char* pathOrURL) return strstr(pathOrURL, "/inspector/") || strstr(pathOrURL, "\\inspector\\"); } +static bool shouldEnableDeveloperExtras(const char* pathOrURL) +{ + return shouldOpenWebInspector(pathOrURL) || strstr(pathOrURL, "/inspector-enabled/") || strstr(pathOrURL, "\\inspector-enabled\\"); +} + static void resetDefaultsToConsistentValues(IWebPreferences* preferences) { #ifdef USE_MAC_FONTS @@ -790,7 +796,7 @@ static void resetDefaultsToConsistentValues(IWebPreferences* preferences) prefsPrivate->setExperimentalNotificationsEnabled(TRUE); prefsPrivate->setShouldPaintNativeControls(FALSE); // FIXME - need to make DRT pass with Windows native controls prefsPrivate->setXSSAuditorEnabled(FALSE); - prefsPrivate->setFrameSetFlatteningEnabled(FALSE); + prefsPrivate->setFrameFlatteningEnabled(FALSE); prefsPrivate->setOfflineWebApplicationCacheEnabled(TRUE); } setAlwaysAcceptCookies(false); @@ -836,7 +842,7 @@ static void resetWebViewToConsistentStateBeforeTesting() SetFocus(viewWindow); webViewPrivate->clearMainFrameName(); - webViewPrivate->resetOriginAccessWhiteLists(); + webViewPrivate->resetOriginAccessWhitelists(); BSTR groupName; if (SUCCEEDED(webView->groupName(&groupName))) { @@ -910,8 +916,11 @@ static void runTest(const string& testPathOrURL) resetWebViewToConsistentStateBeforeTesting(); - if (shouldOpenWebInspector(pathOrURL.c_str())) - gLayoutTestController->showWebInspector(); + if (shouldEnableDeveloperExtras(pathOrURL.c_str())) { + gLayoutTestController->setDeveloperExtrasEnabled(true); + if (shouldOpenWebInspector(pathOrURL.c_str())) + gLayoutTestController->showWebInspector(); + } prevTestBFItem = 0; if (webView) { @@ -947,7 +956,7 @@ static void runTest(const string& testPathOrURL) DispatchMessage(&msg); } - if (shouldOpenWebInspector(pathOrURL.c_str())) + if (shouldEnableDeveloperExtras(pathOrURL.c_str())) gLayoutTestController->closeWebInspector(); resetWebViewToConsistentStateBeforeTesting(); @@ -1222,6 +1231,11 @@ int main(int argc, char* argv[]) continue; } + if (!stricmp(argv[i], "--print-supported-features")) { + printSupportedFeatures = true; + continue; + } + tests.append(argv[i]); } @@ -1246,6 +1260,20 @@ int main(int argc, char* argv[]) standardPreferences->setJavaScriptEnabled(TRUE); standardPreferences->setDefaultFontSize(16); + if (printSupportedFeatures) { + BOOL acceleratedCompositingAvailable; + standardPreferences->acceleratedCompositingEnabled(&acceleratedCompositingAvailable); + BOOL threeDRenderingAvailable = +#if ENABLE(3D_RENDERING) + true; +#else + false; +#endif + + printf("SupportedFeatures:%s %s\n", acceleratedCompositingAvailable ? "AcceleratedCompositing" : "", threeDRenderingAvailable ? "3DRendering" : ""); + return 0; + } + COMPtr webView(AdoptCOM, createWebViewAndOffscreenWindow(&webViewWindow)); if (!webView) return -1; diff --git a/WebKitTools/DumpRenderTree/win/EventSender.cpp b/WebKitTools/DumpRenderTree/win/EventSender.cpp index 5a42b00..2a36d8d 100644 --- a/WebKitTools/DumpRenderTree/win/EventSender.cpp +++ b/WebKitTools/DumpRenderTree/win/EventSender.cpp @@ -667,20 +667,21 @@ static JSClassRef getClass(JSContextRef context) return eventSenderClass; } -JSObjectRef makeEventSender(JSContextRef context) +JSObjectRef makeEventSender(JSContextRef context, bool isTopFrame) { - down = false; - dragMode = true; - replayingSavedEvents = false; - timeOffset = 0; - lastMousePosition.x = 0; - lastMousePosition.y = 0; - - endOfQueue = 0; - startOfQueue = 0; + if (isTopFrame) { + down = false; + dragMode = true; + replayingSavedEvents = false; + timeOffset = 0; + lastMousePosition.x = 0; + lastMousePosition.y = 0; - didDragEnter = false; - draggingInfo = 0; + endOfQueue = 0; + startOfQueue = 0; + didDragEnter = false; + draggingInfo = 0; + } return JSObjectMake(context, getClass(context), 0); } diff --git a/WebKitTools/DumpRenderTree/win/EventSender.h b/WebKitTools/DumpRenderTree/win/EventSender.h index 79d7dab..a0add85 100644 --- a/WebKitTools/DumpRenderTree/win/EventSender.h +++ b/WebKitTools/DumpRenderTree/win/EventSender.h @@ -35,7 +35,7 @@ typedef long HRESULT; typedef const struct OpaqueJSContext* JSContextRef; typedef struct OpaqueJSValue* JSObjectRef; -JSObjectRef makeEventSender(JSContextRef context); +JSObjectRef makeEventSender(JSContextRef context, bool isTopFrame); void replaySavedEvents(HRESULT* oleDragAndDropReturnValue = 0); extern DraggingInfo* draggingInfo; diff --git a/WebKitTools/DumpRenderTree/win/FrameLoadDelegate.cpp b/WebKitTools/DumpRenderTree/win/FrameLoadDelegate.cpp index 37d5e1c..29f99ab 100644 --- a/WebKitTools/DumpRenderTree/win/FrameLoadDelegate.cpp +++ b/WebKitTools/DumpRenderTree/win/FrameLoadDelegate.cpp @@ -336,6 +336,9 @@ void FrameLoadDelegate::didClearWindowObjectForFrameInStandardWorld(IWebFrame* f JSGlobalContextRef context = frame->globalContext(); JSObjectRef windowObject = JSContextGetGlobalObject(context); + IWebFrame* parentFrame = 0; + frame->parentFrame(&parentFrame); + JSValueRef exception = 0; ::gLayoutTestController->makeWindowObject(context, windowObject, &exception); @@ -348,7 +351,7 @@ void FrameLoadDelegate::didClearWindowObjectForFrameInStandardWorld(IWebFrame* f ASSERT(!exception); JSStringRef eventSenderStr = JSStringCreateWithUTF8CString("eventSender"); - JSValueRef eventSender = makeEventSender(context); + JSValueRef eventSender = makeEventSender(context, !parentFrame); JSObjectSetProperty(context, windowObject, eventSenderStr, eventSender, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete, 0); JSStringRelease(eventSenderStr); } diff --git a/WebKitTools/DumpRenderTree/win/LayoutTestControllerWin.cpp b/WebKitTools/DumpRenderTree/win/LayoutTestControllerWin.cpp index 9f84488..24ddc3b 100644 --- a/WebKitTools/DumpRenderTree/win/LayoutTestControllerWin.cpp +++ b/WebKitTools/DumpRenderTree/win/LayoutTestControllerWin.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2006, 2007, 2008, 2009 Apple Inc. All rights reserved. + * Copyright (C) 2006, 2007, 2008, 2009, 2010 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -110,6 +110,21 @@ void LayoutTestController::clearBackForwardList() backForwardList->goToItem(item.get()); } +bool LayoutTestController::callShouldCloseOnWebView() +{ + COMPtr webView; + if (FAILED(frame->webView(&webView))) + return false; + + COMPtr viewPrivate; + if (FAILED(webView->QueryInterface(&viewPrivate))) + return false; + + BOOL result; + viewPrivate->shouldClose(&result); + return result; +} + JSStringRef LayoutTestController::copyDecodedHostName(JSStringRef name) { // FIXME: Implement! @@ -158,6 +173,33 @@ void LayoutTestController::keepWebHistory() history->setOptionalSharedHistory(sharedHistory.get()); } +JSValueRef LayoutTestController::computedStyleIncludingVisitedInfo(JSContextRef context, JSValueRef value) +{ + // FIXME: Implement this. + return JSValueMakeUndefined(context); +} + +JSRetainPtr LayoutTestController::layerTreeAsText() const +{ + COMPtr framePrivate(Query, frame); + if (!framePrivate) + return false; + + BSTR textBSTR = 0; + HRESULT hr = framePrivate->layerTreeAsText(&textBSTR); + + wstring text(textBSTR, SysStringLen(textBSTR)); + SysFreeString(textBSTR); + JSRetainPtr textValueJS(Adopt, JSStringCreateWithCharacters(text.data(), text.length())); + return textValueJS; +} + +JSRetainPtr LayoutTestController::markerTextForListItem(JSContextRef context, JSValueRef nodeObject) const +{ + // FIXME: Implement me. + return JSRetainPtr(); +} + void LayoutTestController::waitForPolicyDelegate() { // FIXME: Implement this. @@ -372,7 +414,7 @@ void LayoutTestController::setXSSAuditorEnabled(bool enabled) prefsPrivate->setXSSAuditorEnabled(enabled); } -void LayoutTestController::setFrameSetFlatteningEnabled(bool enabled) +void LayoutTestController::setFrameFlatteningEnabled(bool enabled) { COMPtr webView; if (FAILED(frame->webView(&webView))) @@ -386,7 +428,12 @@ void LayoutTestController::setFrameSetFlatteningEnabled(bool enabled) if (!prefsPrivate) return; - prefsPrivate->setFrameSetFlatteningEnabled(enabled); + prefsPrivate->setFrameFlatteningEnabled(enabled); +} + +void LayoutTestController::setSpatialNavigationEnabled(bool enabled) +{ + // FIXME: Implement for SpatialNavigation layout tests. } void LayoutTestController::setAllowUniversalAccessFromFileURLs(bool enabled) @@ -688,19 +735,11 @@ void LayoutTestController::setJavaScriptProfilingEnabled(bool flag) if (FAILED(webView->QueryInterface(&viewPrivate))) return; - COMPtr preferences; - if (FAILED(webView->preferences(&preferences))) - return; - - COMPtr prefsPrivate(Query, preferences); - if (!prefsPrivate) - return; - COMPtr inspector; if (FAILED(viewPrivate->inspector(&inspector))) return; - prefsPrivate->setDeveloperExtrasEnabled(flag); + setDeveloperExtrasEnabled(flag); inspector->setJavaScriptProfilingEnabled(flag); } @@ -717,7 +756,7 @@ void LayoutTestController::setSelectTrailingWhitespaceEnabled(bool flag) viewEditing->setSelectTrailingWhitespaceEnabled(flag ? TRUE : FALSE); } -static const CFTimeInterval waitToDumpWatchdogInterval = 15.0; +static const CFTimeInterval waitToDumpWatchdogInterval = 30.0; static void CALLBACK waitUntilDoneWatchdogFired(HWND, UINT, UINT_PTR, DWORD) { @@ -950,13 +989,27 @@ static _bstr_t bstrT(JSStringRef jsString) return _bstr_t(JSStringCopyBSTR(jsString), false); } -void LayoutTestController::whiteListAccessFromOrigin(JSStringRef sourceOrigin, JSStringRef destinationProtocol, JSStringRef destinationHost, bool allowDestinationSubdomains) +void LayoutTestController::addOriginAccessWhitelistEntry(JSStringRef sourceOrigin, JSStringRef destinationProtocol, JSStringRef destinationHost, bool allowDestinationSubdomains) { COMPtr webView; if (FAILED(WebKitCreateInstance(__uuidof(WebView), 0, __uuidof(webView), reinterpret_cast(&webView)))) return; - webView->whiteListAccessFromOrigin(bstrT(sourceOrigin).GetBSTR(), bstrT(destinationProtocol).GetBSTR(), bstrT(destinationHost).GetBSTR(), allowDestinationSubdomains); + webView->addOriginAccessWhitelistEntry(bstrT(sourceOrigin).GetBSTR(), bstrT(destinationProtocol).GetBSTR(), bstrT(destinationHost).GetBSTR(), allowDestinationSubdomains); +} + +void LayoutTestController::removeOriginAccessWhitelistEntry(JSStringRef sourceOrigin, JSStringRef destinationProtocol, JSStringRef destinationHost, bool allowDestinationSubdomains) +{ + COMPtr webView; + if (FAILED(WebKitCreateInstance(__uuidof(WebView), 0, __uuidof(webView), reinterpret_cast(&webView)))) + return; + + webView->removeOriginAccessWhitelistEntry(bstrT(sourceOrigin).GetBSTR(), bstrT(destinationProtocol).GetBSTR(), bstrT(destinationHost).GetBSTR(), allowDestinationSubdomains); +} + +void LayoutTestController::setScrollbarPolicy(JSStringRef orientation, JSStringRef policy) +{ + // FIXME: implement } void LayoutTestController::addUserScript(JSStringRef source, bool runAtStart) @@ -986,7 +1039,7 @@ void LayoutTestController::addUserStyleSheet(JSStringRef source) webView->addUserStyleSheetToGroup(_bstr_t(L"org.webkit.DumpRenderTree").GetBSTR(), world.get(), bstrT(source).GetBSTR(), 0, 0, 0, 0, 0); } -void LayoutTestController::showWebInspector() +void LayoutTestController::setDeveloperExtrasEnabled(bool enabled) { COMPtr webView; if (FAILED(frame->webView(&webView))) @@ -1000,7 +1053,14 @@ void LayoutTestController::showWebInspector() if (!prefsPrivate) return; - prefsPrivate->setDeveloperExtrasEnabled(true); + prefsPrivate->setDeveloperExtrasEnabled(enabled); +} + +void LayoutTestController::showWebInspector() +{ + COMPtr webView; + if (FAILED(frame->webView(&webView))) + return; COMPtr viewPrivate(Query, webView); if (!viewPrivate) @@ -1026,16 +1086,6 @@ void LayoutTestController::closeWebInspector() return; inspector->close(); - - COMPtr preferences; - if (FAILED(webView->preferences(&preferences))) - return; - - COMPtr prefsPrivate(Query, preferences); - if (!prefsPrivate) - return; - - prefsPrivate->setDeveloperExtrasEnabled(false); } void LayoutTestController::evaluateInWebInspector(long callId, JSStringRef script) @@ -1169,3 +1219,29 @@ void LayoutTestController::apiTestNewWindowDataLoadBaseURL(JSStringRef utf8Data, { } + +void LayoutTestController::apiTestGoToCurrentBackForwardItem() +{ + COMPtr webView; + if (FAILED(frame->webView(&webView))) + return; + + COMPtr backForwardList; + if (FAILED(webView->backForwardList(&backForwardList))) + return; + + COMPtr item; + if (FAILED(backForwardList->currentItem(&item))) + return; + + BOOL success; + webView->goToBackForwardItem(item.get(), &success); +} + +void LayoutTestController::setWebViewEditable(bool) +{ +} + +void LayoutTestController::authenticateSession(JSStringRef, JSStringRef, JSStringRef) +{ +} diff --git a/WebKitTools/DumpRenderTree/win/ResourceLoadDelegate.cpp b/WebKitTools/DumpRenderTree/win/ResourceLoadDelegate.cpp index 19bf84a..2e031da 100644 --- a/WebKitTools/DumpRenderTree/win/ResourceLoadDelegate.cpp +++ b/WebKitTools/DumpRenderTree/win/ResourceLoadDelegate.cpp @@ -31,28 +31,35 @@ #include "DumpRenderTree.h" #include "LayoutTestController.h" -#include #include +#include +#include +#include #include #include -#include - -using std::wstring; -using std::wiostream; +using namespace std; static inline wstring wstringFromBSTR(BSTR str) { return wstring(str, ::SysStringLen(str)); } -wstring wstringFromInt(int i) +static inline wstring wstringFromInt(int i) { - std::wostringstream ss; + wostringstream ss; ss << i; return ss.str(); } +static inline BSTR BSTRFromString(const string& str) +{ + int length = ::MultiByteToWideChar(CP_UTF8, 0, str.c_str(), str.length(), 0, 0); + BSTR result = ::SysAllocStringLen(0, length); + ::MultiByteToWideChar(CP_UTF8, 0, str.c_str(), str.length(), result, length); + return result; +} + typedef HashMap IdentifierMap; IdentifierMap& urlMap() @@ -254,8 +261,16 @@ HRESULT STDMETHODCALLTYPE ResourceLoadDelegate::willSendRequest( return S_OK; } - request->AddRef(); - *newRequest = request; + IWebMutableURLRequest* requestCopy = 0; + request->mutableCopy(&requestCopy); + const set& clearHeaders = gLayoutTestController->willSendRequestClearHeaders(); + for (set::const_iterator header = clearHeaders.begin(); header != clearHeaders.end(); ++header) { + BSTR bstrHeader = BSTRFromString(*header); + requestCopy->setValue(0, bstrHeader); + SysFreeString(bstrHeader); + } + + *newRequest = requestCopy; return S_OK; } diff --git a/WebKitTools/DumpRenderTree/win/TestNetscapePlugin/main.cpp b/WebKitTools/DumpRenderTree/win/TestNetscapePlugin/main.cpp index 08a2f6a..eeacb7e 100644 --- a/WebKitTools/DumpRenderTree/win/TestNetscapePlugin/main.cpp +++ b/WebKitTools/DumpRenderTree/win/TestNetscapePlugin/main.cpp @@ -6,7 +6,7 @@ redistribute this Apple software. In consideration of your agreement to abide by the following terms, and subject to these - terms, Apple grants you a personal, non-exclusive license, under AppleÕs copyrights in + terms, Apple grants you a personal, non-exclusive license, under Apple's copyrights in this original Apple software (the "Apple Software"), to use, reproduce, modify and redistribute the Apple Software, with or without modifications, in source and/or binary forms; provided that if you redistribute the Apple Software in its entirety and without @@ -89,7 +89,8 @@ NPError NPP_New(NPMIMEType pluginType, NPP instance, uint16 mode, int16 argc, ch { if (browser->version >= 14) { PluginObject* obj = (PluginObject*)browser->createobject(instance, getPluginClass()); - + instance->pdata = obj; + for (int16 i = 0; i < argc; i++) { if (_stricmp(argn[i], "onstreamload") == 0 && !obj->onStreamLoad) obj->onStreamLoad = _strdup(argv[i]); @@ -107,9 +108,11 @@ NPError NPP_New(NPMIMEType pluginType, NPP instance, uint16 mode, int16 argc, ch obj->testDocumentOpenInDestroyStream = TRUE; else if (_stricmp(argn[i], "testwindowopen") == 0) obj->testWindowOpen = TRUE; + else if (_stricmp(argn[i], "onSetWindow") == 0 && !obj->onSetWindow) + obj->onSetWindow = strdup(argv[i]); } - - instance->pdata = obj; + + browser->getvalue(instance, NPNVprivateModeBool, (void *)&obj->cachedPrivateBrowsingMode); } return NPERR_NO_ERROR; @@ -136,6 +139,9 @@ NPError NPP_Destroy(NPP instance, NPSavedData **save) if (obj->logDestroy) printf("PLUGIN: NPP_Destroy\n"); + if (obj->onSetWindow) + free(obj->onSetWindow); + browser->releaseobject(&obj->header); } return NPERR_NO_ERROR; @@ -146,6 +152,11 @@ NPError NPP_SetWindow(NPP instance, NPWindow *window) PluginObject* obj = static_cast(instance->pdata); if (obj) { + obj->lastWindow = *window; + + if (obj->onSetWindow) + executeScript(obj, obj->onSetWindow); + if (obj->testWindowOpen) { testWindowOpen(instance); obj->testWindowOpen = FALSE; @@ -238,5 +249,13 @@ NPError NPP_GetValue(NPP instance, NPPVariable variable, void *value) NPError NPP_SetValue(NPP instance, NPNVariable variable, void *value) { - return NPERR_GENERIC_ERROR; + PluginObject* obj = static_cast(instance->pdata); + + switch (variable) { + case NPNVprivateModeBool: + obj->cachedPrivateBrowsingMode = *(NPBool*)value; + return NPERR_NO_ERROR; + default: + return NPERR_GENERIC_ERROR; + } } diff --git a/WebKitTools/DumpRenderTree/wx/LayoutTestControllerWx.cpp b/WebKitTools/DumpRenderTree/wx/LayoutTestControllerWx.cpp index ce1bda5..873e8d8 100644 --- a/WebKitTools/DumpRenderTree/wx/LayoutTestControllerWx.cpp +++ b/WebKitTools/DumpRenderTree/wx/LayoutTestControllerWx.cpp @@ -35,6 +35,8 @@ #include #include +#include + LayoutTestController::~LayoutTestController() @@ -170,7 +172,7 @@ void LayoutTestController::setXSSAuditorEnabled(bool enabled) // FIXME: implement } -void LayoutTestController::setFrameSetFlatteningEnabled(bool enabled) +void LayoutTestController::setFrameFlatteningEnabled(bool enabled) { // FIXME: implement } @@ -360,9 +362,19 @@ void LayoutTestController::disableImageLoading() } -void LayoutTestController::whiteListAccessFromOrigin(JSStringRef sourceOrigin, JSStringRef destinationProtocol, JSStringRef destinationHost, bool allowDestinationSubdomains) +void LayoutTestController::addOriginAccessWhitelistEntry(JSStringRef sourceOrigin, JSStringRef destinationProtocol, JSStringRef destinationHost, bool allowDestinationSubdomains) { + // FIXME: implement +} +void LayoutTestController::removeOriginAccessWhitelistEntry(JSStringRef sourceOrigin, JSStringRef destinationProtocol, JSStringRef destinationHost, bool allowDestinationSubdomains) +{ + // FIXME: implement +} + +void LayoutTestController::setScrollbarPolicy(JSStringRef orientation, JSStringRef policy) +{ + // FIXME: implement } JSRetainPtr LayoutTestController::counterValueForElementById(JSStringRef id) @@ -386,3 +398,36 @@ void LayoutTestController::apiTestNewWindowDataLoadBaseURL(JSStringRef utf8Data, { } + +void LayoutTestController::apiTestGoToCurrentBackForwardItem() +{ + +} + +void LayoutTestController::setSpatialNavigationEnabled(bool) +{ + +} + +void LayoutTestController::setWebViewEditable(bool) +{ +} + +bool LayoutTestController::callShouldCloseOnWebView() +{ + return false; +} + +JSRetainPtr LayoutTestController::layerTreeAsText() const +{ + return 0; +} + +JSValueRef LayoutTestController::computedStyleIncludingVisitedInfo(JSContextRef, JSValueRef) +{ + return 0; +} + +void LayoutTestController::authenticateSession(JSStringRef, JSStringRef, JSStringRef) +{ +} -- cgit v1.1