diff options
Diffstat (limited to 'WebKitTools/DumpRenderTree/chromium')
32 files changed, 9149 insertions, 0 deletions
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 <wtf/Assertions.h> + +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 <wtf/Vector.h> + +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<AccessibilityUIElement*> 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 <wtf/Assertions.h> +#include <wtf/OwnPtr.h> + +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<CppBoundClass::GetterCallback> 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<CppNPObject*>(npObj); + delete obj; +} + +bool CppNPObject::hasMethod(NPObject* npObj, NPIdentifier ident) +{ + CppNPObject* obj = reinterpret_cast<CppNPObject*>(npObj); + return obj->boundClass->hasMethod(ident); +} + +bool CppNPObject::hasProperty(NPObject* npObj, NPIdentifier ident) +{ + CppNPObject* obj = reinterpret_cast<CppNPObject*>(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<CppNPObject*>(npObj); + return obj->boundClass->invoke(ident, arguments, argumentCount, result); +} + +bool CppNPObject::getProperty(NPObject* npObj, NPIdentifier ident, NPVariant* result) +{ + CppNPObject* obj = reinterpret_cast<CppNPObject*>(npObj); + return obj->boundClass->getProperty(ident, result); +} + +bool CppNPObject::setProperty(NPObject* npObj, NPIdentifier ident, const NPVariant* value) +{ + CppNPObject* obj = reinterpret_cast<CppNPObject*>(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<CppNPObject*>(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 <wtf/HashMap.h> +#include <wtf/Noncopyable.h> +#include <wtf/OwnPtr.h> +#include <wtf/Vector.h> + +namespace WebKit { +class WebFrame; +class WebString; +} + +typedef Vector<CppVariant> 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.<classname>. 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 T> 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 T> 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<class T> + void bindMethod(const std::string& name, void (T::*method)(const CppArgumentList&, CppVariant*)) + { + Callback* callback = new MemberCallback<T>(static_cast<T*>(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<class T> + void bindProperty(const std::string& name, void (T::*method)(CppVariant*)) + { + GetterCallback* callback = new MemberGetterCallback<T>(static_cast<T*>(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<class T> + void bindFallbackMethod(void (T::*method)(const CppArgumentList&, CppVariant*)) + { + if (method) { + Callback* callback = new MemberCallback<T>(static_cast<T*>(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<NPIdentifier, PropertyCallback*> PropertyList; + typedef HashMap<NPIdentifier, Callback*> 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<Callback> 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 <limits> +#include <wtf/Assertions.h> +#include <wtf/StringExtras.h> + +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<uint32_t>(strlen(newValue))}; + WebBindings::initializeVariantWithStringCopy(this, &newString); +} + +void CppVariant::set(const string& newValue) +{ + freeData(); + type = NPVariantType_String; + NPString newString = {newValue.data(), + static_cast<uint32_t>(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<int32_t>(value.doubleValue); + ASSERT_NOT_REACHED(); + return 0; +} + +double CppVariant::toDouble() const +{ + if (isInt32()) + return static_cast<double>(value.intValue); + if (isDouble()) + return value.doubleValue; + ASSERT_NOT_REACHED(); + return 0; +} + +bool CppVariant::toBoolean() const +{ + ASSERT(isBool()); + return value.boolValue; +} + +Vector<string> CppVariant::toStringVector() const +{ + + ASSERT(isObject()); + Vector<string> 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<int>(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 <string> +#include <wtf/Vector.h> + +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<std::string> 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 <wtf/Vector.h> +#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<string> 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 <wtf/Deque.h> +#include <wtf/StringExtras.h> + +#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<SavedEvent> 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<string> modifiers = argument->toStringVector(); + for (Vector<string>::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<float>(horizontal); + event.wheelTicksY = static_cast<float>(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<unsigned long>(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<CppVariant*>(0))); + webkit_support::PostTaskFromHere(m_methodFactory.NewRunnableMethod( + &EventSender::mouseUp, arguments, static_cast<CppVariant*>(0))); +} + +void EventSender::beginDragWithFiles(const CppArgumentList& arguments, CppVariant* result) +{ + currentDragData.initialize(); + Vector<string> 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<EventSender> 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 <algorithm> +#include <stdio.h> +#include <string.h> +#include <string> +#include <vector> +#include <wtf/OwnArrayPtr.h> +#include <wtf/Vector.h> + +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<unsigned char> 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<unsigned char> 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<const uint32_t*>(&(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<unsigned char*>(&m_data.front())[(y * m_width + x) * 4]; + *reinterpret_cast<uint32_t*>(addr) = color; + } + +private: + // pixel dimensions of the image + int m_width, m_height; + + vector<unsigned char> 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<float>(actual.width()) * static_cast<float>(actual.height()); + if (!totalPixels) + return 100.0f; // When the bitmap is empty, they are 100% different. + return static_cast<float>(pixelsDifferent) / totalPixels * 100; +} + +void printHelp() +{ + fprintf(stderr, + "Usage:\n" + " ImageDiff <compare file> <reference file>\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 <compare file> <reference file> <output file>\n" + " Compares two files on disk, outputs an image that visualizes the" + " difference to <output file>\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: <byte length> <data>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<unsigned char> 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<const char*> 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 <wtf/OwnArrayPtr.h> +#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<int>(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<WCHAR> 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<WebInputElement>(); + 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<float>(arguments[argOffset].toInt32()); + *pageHeightInPixels = static_cast<float>(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 <wtf/Deque.h> + +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<WorkQueue> m_timer; + Deque<WorkItem*> 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<LayoutTestController> 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 <AppKit/AppKit.h> +#include <signal.h> +#include <stdio.h> +#include <stdlib.h> + +// 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 <wtf/ASCIICType.h> +#include <wtf/Assertions.h> + +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 <wtf/HashMap.h> +#include <wtf/text/StringHash.h> +#include <wtf/text/WTFString.h> + +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<WebCore::String, bool> 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 <wtf/Assertions.h> + +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<int>(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<int>(m_entries.size()); + if (currentSize > 0) { + while (m_lastCommittedEntryIndex < (currentSize - 1)) { + m_entries.removeLast(); + currentSize--; + } + } + + m_entries.append(linked_ptr<TestNavigationEntry>(entry)); + m_lastCommittedEntryIndex = static_cast<int>(m_entries.size()) - 1; + updateMaxPageID(); +} + +int TestNavigationController::entryIndexWithPageID(int32 pageID) const +{ + for (int i = static_cast<int>(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 <string> +#include <wtf/Vector.h> + +// 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<int>(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<linked_ptr<TestNavigationEntry> > 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 <algorithm> +#include <cctype> +#include <vector> + +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<WebHistoryItem>& 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<WebHistoryItem> 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<skia::BitmapPlatformDevice&>(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<unsigned char> png; + gfx::PNGCodec::ColorFormat colorFormat = gfx::PNGCodec::FORMAT_BGRA; + gfx::PNGCodec::Encode( + reinterpret_cast<const unsigned char*>(sourceBitmap.getPixels()), + colorFormat, sourceBitmap.width(), sourceBitmap.height(), + static_cast<int>(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<WebViewHost*> 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 <string> +#include <wtf/OwnPtr.h> +#include <wtf/Vector.h> + +// 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<AccessibilityController*> m_accessibilityController; + OwnPtr<EventSender*> m_eventSender; + OwnPtr<LayoutTestController*> m_layoutTestController; + OwnPtr<PlainTextController*> m_plainTextController; + OwnPtr<TextInputController*> m_textInputController; + TestParams m_params; + + // List of all windows in this process. + // The main window should be put into windowList[0]. + typedef Vector<WebViewHost*> 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 <signal.h> + +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 <AppKit/AppKit.h> + +// 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 <process.h> + +// 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<TestShell*>(arg); + // FIXME: Do we need user-specified time settings as with the original + // Chromium implementation? + DWORD timeout = static_cast<DWORD>(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<HANDLE>(_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 <wtf/RefCounted.h> + +namespace WebKit { +class WebNotificationPresenter; +class WebString; +class WebURL; +} + +class TestWebWorker : public WebKit::WebWorker, + public WebKit::WebWorkerClient, + public WTF::RefCounted<TestWebWorker> { +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<TestWebWorker>; +}; + +#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 <wtf/StringExtras.h> +#include <string> + +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<v8::Value> toV8(Navigator* impl) +{ + if (!impl) + return v8::Null(); + v8::Handle<v8::Object> 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<ApplicationInstalledCallback> createApplicationInstalledCallback( + v8::Local<v8::Value> 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<v8::Value> 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<ApplicationInstalledCallback> 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 <wtf/Assertions.h> + +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("<NSURLResponse %s, http status code %d>", + 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("<NSError domain %s, code %d, failing URL \"%s\">", + 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 <NSURLRequest URL %s, main document URL %s," + " http method %s> 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<WebView*>(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<TestShellExtraData*>(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<TestNavigationEntry> 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<TestNavigationEntry*>(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() : "<unknown>"); +} + +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 <wtf/HashMap.h> +#include <wtf/Vector.h> + +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<TestShellExtraData> m_pendingExtraData; + + // Maps resource identifiers to a descriptive string. + typedef HashMap<unsigned, std::string> 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<skia::PlatformCanvas> m_canvas; + WebKit::WebRect m_paintRect; + bool m_isPainting; + + OwnPtr<TestNavigationController*> 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 <iostream> +#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 <wtf/Platform.h> + +// 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 |