diff options
Diffstat (limited to 'Tools/DumpRenderTree/chromium')
56 files changed, 13989 insertions, 0 deletions
diff --git a/Tools/DumpRenderTree/chromium/AccessibilityController.cpp b/Tools/DumpRenderTree/chromium/AccessibilityController.cpp new file mode 100644 index 0000000..5601d9d --- /dev/null +++ b/Tools/DumpRenderTree/chromium/AccessibilityController.cpp @@ -0,0 +1,124 @@ +/* + * 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 "WebAccessibilityCache.h" +#include "WebAccessibilityObject.h" +#include "WebFrame.h" +#include "WebString.h" +#include "WebView.h" + +using namespace WebKit; + +AccessibilityController::AccessibilityController(TestShell* shell) + : m_shell(shell) +{ + + bindMethod("dumpAccessibilityNotifications", &AccessibilityController::dumpAccessibilityNotifications); + 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(); + + m_dumpAccessibilityNotifications = false; +} + +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::dumpAccessibilityNotifications(const CppArgumentList&, CppVariant* result) +{ + m_dumpAccessibilityNotifications = true; + result->setNull(); +} + +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/Tools/DumpRenderTree/chromium/AccessibilityController.h b/Tools/DumpRenderTree/chromium/AccessibilityController.h new file mode 100644 index 0000000..0817ec3 --- /dev/null +++ b/Tools/DumpRenderTree/chromium/AccessibilityController.h @@ -0,0 +1,84 @@ +/* + * 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(); + + // This function sets a flag that tells the test_shell to dump all + // accessibility notifications. + void dumpAccessibilityNotifications(const CppArgumentList&, CppVariant*); + +public: + // The following methods are not exposed to JavaScript. + bool shouldDumpAccessibilityNotifications() { return m_dumpAccessibilityNotifications; } + +private: + // If true, the test_shell will dump all accessibility notifications. + bool m_dumpAccessibilityNotifications; + + // 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/Tools/DumpRenderTree/chromium/AccessibilityUIElement.cpp b/Tools/DumpRenderTree/chromium/AccessibilityUIElement.cpp new file mode 100644 index 0000000..dbd025a --- /dev/null +++ b/Tools/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 "WebAccessibilityObject.h" +#include "WebCString.h" +#include "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/Tools/DumpRenderTree/chromium/AccessibilityUIElement.h b/Tools/DumpRenderTree/chromium/AccessibilityUIElement.h new file mode 100644 index 0000000..366ed42 --- /dev/null +++ b/Tools/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 "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/Tools/DumpRenderTree/chromium/CppBoundClass.cpp b/Tools/DumpRenderTree/chromium/CppBoundClass.cpp new file mode 100644 index 0000000..1348bbf --- /dev/null +++ b/Tools/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 "WebBindings.h" +#include "WebFrame.h" +#include "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/Tools/DumpRenderTree/chromium/CppBoundClass.h b/Tools/DumpRenderTree/chromium/CppBoundClass.h new file mode 100644 index 0000000..6cb638e --- /dev/null +++ b/Tools/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/Tools/DumpRenderTree/chromium/CppVariant.cpp b/Tools/DumpRenderTree/chromium/CppVariant.cpp new file mode 100644 index 0000000..22e0013 --- /dev/null +++ b/Tools/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 "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/Tools/DumpRenderTree/chromium/CppVariant.h b/Tools/DumpRenderTree/chromium/CppVariant.h new file mode 100644 index 0000000..3032310 --- /dev/null +++ b/Tools/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 "WebBindings.h" +#include "webkit/support/webkit_support.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/Tools/DumpRenderTree/chromium/DRTDevToolsAgent.cpp b/Tools/DumpRenderTree/chromium/DRTDevToolsAgent.cpp new file mode 100644 index 0000000..78c86e7 --- /dev/null +++ b/Tools/DumpRenderTree/chromium/DRTDevToolsAgent.cpp @@ -0,0 +1,161 @@ +/* + * 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 "DRTDevToolsAgent.h" + +#include "DRTDevToolsCallArgs.h" +#include "DRTDevToolsClient.h" + +#include "WebCString.h" +#include "WebDevToolsAgent.h" +#include "WebString.h" +#include "WebView.h" +#include "webkit/support/webkit_support.h" + +using namespace WebKit; + +DRTDevToolsAgent::DRTDevToolsAgent() + : m_drtDevToolsClient(0) + , m_webView(0) +{ + static int devToolsAgentCounter = 0; + + m_routingID = ++devToolsAgentCounter; + if (m_routingID == 1) + WebDevToolsAgent::setMessageLoopDispatchHandler(&DRTDevToolsAgent::dispatchMessageLoop); +} + +void DRTDevToolsAgent::reset() +{ + m_taskList.revokeAll(); +} + +void DRTDevToolsAgent::setWebView(WebView* webView) +{ + m_webView = webView; +} + +void DRTDevToolsAgent::sendMessageToInspectorFrontend(const WebKit::WebString& data) +{ + if (m_drtDevToolsClient) + m_drtDevToolsClient->asyncCall(DRTDevToolsCallArgs(data)); +} + +void DRTDevToolsAgent::runtimePropertyChanged(const WebKit::WebString& name, const WebKit::WebString& value) +{ + // FIXME: Implement. +} + +WebCString DRTDevToolsAgent::debuggerScriptSource() +{ + return webkit_support::GetDevToolsDebuggerScriptSource(); +} + +WebDevToolsAgentClient::WebKitClientMessageLoop* DRTDevToolsAgent::createClientMessageLoop() +{ + return webkit_support::CreateDevToolsMessageLoop(); +} + +void DRTDevToolsAgent::asyncCall(const DRTDevToolsCallArgs& args) +{ + postTask(new AsyncCallTask(this, args)); +} + +void DRTDevToolsAgent::call(const DRTDevToolsCallArgs &args) +{ + WebDevToolsAgent* agent = webDevToolsAgent(); + if (agent) + agent->dispatchOnInspectorBackend(args.m_data); + if (DRTDevToolsCallArgs::callsCount() == 1 && m_drtDevToolsClient) + m_drtDevToolsClient->allMessagesProcessed(); +} + +void DRTDevToolsAgent::delayedFrontendLoaded() +{ + WebDevToolsAgent* agent = webDevToolsAgent(); + if (agent) + agent->frontendLoaded(); +} + + +WebDevToolsAgent* DRTDevToolsAgent::webDevToolsAgent() +{ + if (!m_webView) + return 0; + return m_webView->devToolsAgent(); +} + +void DRTDevToolsAgent::attach(DRTDevToolsClient* client) +{ + ASSERT(!m_drtDevToolsClient); + m_drtDevToolsClient = client; + WebDevToolsAgent* agent = webDevToolsAgent(); + if (agent) + agent->attach(); +} + +void DRTDevToolsAgent::detach() +{ + ASSERT(m_drtDevToolsClient); + WebDevToolsAgent* agent = webDevToolsAgent(); + if (agent) + agent->detach(); + m_drtDevToolsClient = 0; +} + +void DRTDevToolsAgent::frontendLoaded() +{ + postTask(new DelayedFrontendLoadedTask(this)); +} + +bool DRTDevToolsAgent::setTimelineProfilingEnabled(bool enabled) +{ + WebDevToolsAgent* agent = webDevToolsAgent(); + if (!agent) + return false; + agent->setTimelineProfilingEnabled(enabled); + return true; +} + +bool DRTDevToolsAgent::evaluateInWebInspector(long callID, const std::string& script) +{ + WebDevToolsAgent* agent = webDevToolsAgent(); + if (!agent) + return false; + agent->evaluateInWebInspector(callID, WebString::fromUTF8(script)); + return true; +} + +// static method +void DRTDevToolsAgent::dispatchMessageLoop() +{ + webkit_support::DispatchMessageLoop(); +} diff --git a/Tools/DumpRenderTree/chromium/DRTDevToolsAgent.h b/Tools/DumpRenderTree/chromium/DRTDevToolsAgent.h new file mode 100644 index 0000000..e1478d0 --- /dev/null +++ b/Tools/DumpRenderTree/chromium/DRTDevToolsAgent.h @@ -0,0 +1,105 @@ +/* + * 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 DRTDevToolsAgent_h +#define DRTDevToolsAgent_h + +#include "DRTDevToolsCallArgs.h" +#include "Task.h" +#include "WebDevToolsAgentClient.h" +#include <wtf/HashMap.h> +#include <wtf/Noncopyable.h> + +namespace WebKit { + +class WebCString; +class WebDevToolsAgent; +class WebString; +class WebView; +struct WebDevToolsMessageData; + +} // namespace WebKit + +class DRTDevToolsClient; + +class DRTDevToolsAgent : public WebKit::WebDevToolsAgentClient + , public Noncopyable { +public: + DRTDevToolsAgent(); + virtual ~DRTDevToolsAgent() {} + void reset(); + + void setWebView(WebKit::WebView*); + + // WebDevToolsAgentClient implementation. + virtual void sendMessageToInspectorFrontend(const WebKit::WebString&); + virtual int hostIdentifier() { return m_routingID; } + virtual void runtimePropertyChanged(const WebKit::WebString& name, const WebKit::WebString& value); + virtual WebKit::WebCString debuggerScriptSource(); + virtual WebKitClientMessageLoop* createClientMessageLoop(); + + void asyncCall(const DRTDevToolsCallArgs&); + + void attach(DRTDevToolsClient*); + void detach(); + void frontendLoaded(); + + bool evaluateInWebInspector(long callID, const std::string& script); + bool setTimelineProfilingEnabled(bool enable); + TaskList* taskList() { return &m_taskList; } + +private: + void call(const DRTDevToolsCallArgs&); + void delayedFrontendLoaded(); + static void dispatchMessageLoop(); + WebKit::WebDevToolsAgent* webDevToolsAgent(); + + class AsyncCallTask: public MethodTask<DRTDevToolsAgent> { + public: + AsyncCallTask(DRTDevToolsAgent* object, const DRTDevToolsCallArgs& args) + : MethodTask<DRTDevToolsAgent>(object), m_args(args) {} + virtual void runIfValid() { m_object->call(m_args); } + private: + DRTDevToolsCallArgs m_args; + }; + + struct DelayedFrontendLoadedTask: public MethodTask<DRTDevToolsAgent> { + DelayedFrontendLoadedTask(DRTDevToolsAgent* object) : MethodTask<DRTDevToolsAgent>(object) {} + virtual void runIfValid() { m_object->delayedFrontendLoaded(); } + }; + + TaskList m_taskList; + DRTDevToolsClient* m_drtDevToolsClient; + int m_routingID; + WebKit::WebDevToolsAgent* m_webDevToolsAgent; + WebKit::WebView* m_webView; +}; + +#endif // DRTDevToolsAgent_h diff --git a/Tools/DumpRenderTree/chromium/DRTDevToolsCallArgs.cpp b/Tools/DumpRenderTree/chromium/DRTDevToolsCallArgs.cpp new file mode 100644 index 0000000..dacd6f7 --- /dev/null +++ b/Tools/DumpRenderTree/chromium/DRTDevToolsCallArgs.cpp @@ -0,0 +1,36 @@ +/* + * 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 "DRTDevToolsCallArgs.h" + +// static +int DRTDevToolsCallArgs::m_callsCount = 0; + diff --git a/Tools/DumpRenderTree/chromium/DRTDevToolsCallArgs.h b/Tools/DumpRenderTree/chromium/DRTDevToolsCallArgs.h new file mode 100644 index 0000000..a548159 --- /dev/null +++ b/Tools/DumpRenderTree/chromium/DRTDevToolsCallArgs.h @@ -0,0 +1,65 @@ +/* + * 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 DRTDevToolsCallArgs_h +#define DRTDevToolsCallArgs_h + +#include "WebString.h" +#include <wtf/Assertions.h> + +class DRTDevToolsCallArgs { +public: + DRTDevToolsCallArgs(const WebKit::WebString& data) + : m_data(data) + { + ++m_callsCount; + } + + DRTDevToolsCallArgs(const DRTDevToolsCallArgs& args) + : m_data(args.m_data) + { + ++m_callsCount; + } + + ~DRTDevToolsCallArgs() + { + --m_callsCount; + ASSERT(m_callsCount >= 0); + } + + static int callsCount() { return m_callsCount; } + + WebKit::WebString m_data; + +private: + static int m_callsCount; +}; + +#endif // DRTDevToolsCallArgs_h diff --git a/Tools/DumpRenderTree/chromium/DRTDevToolsClient.cpp b/Tools/DumpRenderTree/chromium/DRTDevToolsClient.cpp new file mode 100644 index 0000000..acccf18 --- /dev/null +++ b/Tools/DumpRenderTree/chromium/DRTDevToolsClient.cpp @@ -0,0 +1,124 @@ +/* + * 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 "DRTDevToolsClient.h" + +#include "DRTDevToolsAgent.h" +#include "DRTDevToolsCallArgs.h" + +#include "WebDevToolsAgent.h" +#include "WebDevToolsFrontend.h" +#include "WebFrame.h" +#include "WebScriptSource.h" +#include "WebString.h" +#include "WebView.h" +#include "webkit/support/webkit_support.h" + +using namespace WebKit; + +DRTDevToolsClient::DRTDevToolsClient(DRTDevToolsAgent* agent, WebView* webView) + : m_webView(webView) + , m_drtDevToolsAgent(agent) +{ + m_webDevToolsFrontend.set(WebDevToolsFrontend::create(m_webView, + this, + WebString::fromUTF8("en-US"))); + m_drtDevToolsAgent->attach(this); +} + +DRTDevToolsClient::~DRTDevToolsClient() +{ + // There is a chance that the page will be destroyed at detach step of + // m_drtDevToolsAgent and we should clean pending requests a bit earlier. + m_taskList.revokeAll(); + if (m_drtDevToolsAgent) + m_drtDevToolsAgent->detach(); +} + +void DRTDevToolsClient::reset() +{ + m_taskList.revokeAll(); +} + +void DRTDevToolsClient::sendFrontendLoaded() { + if (m_drtDevToolsAgent) + m_drtDevToolsAgent->frontendLoaded(); +} + +void DRTDevToolsClient::sendMessageToBackend(const WebString& data) +{ + if (m_drtDevToolsAgent) + m_drtDevToolsAgent->asyncCall(DRTDevToolsCallArgs(data)); +} + +void DRTDevToolsClient::sendDebuggerCommandToAgent(const WebString& command) +{ + WebDevToolsAgent::executeDebuggerCommand(command, 1); +} + +void DRTDevToolsClient::activateWindow() +{ + // Not implemented. +} + +void DRTDevToolsClient::closeWindow() +{ + // Not implemented. +} + +void DRTDevToolsClient::dockWindow() +{ + // Not implemented. +} + +void DRTDevToolsClient::undockWindow() +{ + // Not implemented. +} + +void DRTDevToolsClient::asyncCall(const DRTDevToolsCallArgs& args) +{ + postTask(new AsyncCallTask(this, args)); +} + +void DRTDevToolsClient::call(const DRTDevToolsCallArgs& args) +{ + m_webDevToolsFrontend->dispatchOnInspectorFrontend(args.m_data); + if (DRTDevToolsCallArgs::callsCount() == 1) + allMessagesProcessed(); +} + +void DRTDevToolsClient::allMessagesProcessed() +{ + m_webView->mainFrame()->executeScript( + WebKit::WebScriptSource(WebString::fromUTF8( + "if (window.WebInspector && WebInspector.queuesAreEmpty) WebInspector.queuesAreEmpty();"))); +} diff --git a/Tools/DumpRenderTree/chromium/DRTDevToolsClient.h b/Tools/DumpRenderTree/chromium/DRTDevToolsClient.h new file mode 100644 index 0000000..9ca1402 --- /dev/null +++ b/Tools/DumpRenderTree/chromium/DRTDevToolsClient.h @@ -0,0 +1,90 @@ +/* + * 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 DRTDevToolsClient_h +#define DRTDevToolsClient_h + +#include "DRTDevToolsCallArgs.h" +#include "Task.h" +#include "WebDevToolsFrontendClient.h" +#include <wtf/Noncopyable.h> +#include <wtf/OwnPtr.h> + +namespace WebKit { + +class WebDevToolsFrontend; +struct WebDevToolsMessageData; +class WebString; +class WebView; + +} // namespace WebKit + +class DRTDevToolsAgent; + +class DRTDevToolsClient : public WebKit::WebDevToolsFrontendClient + , public Noncopyable { +public: + DRTDevToolsClient(DRTDevToolsAgent*, WebKit::WebView*); + virtual ~DRTDevToolsClient(); + void reset(); + + // WebDevToolsFrontendClient implementation + virtual void sendFrontendLoaded(); + virtual void sendMessageToBackend(const WebKit::WebString&); + virtual void sendDebuggerCommandToAgent(const WebKit::WebString& command); + + virtual void activateWindow(); + virtual void closeWindow(); + virtual void dockWindow(); + virtual void undockWindow(); + + void asyncCall(const DRTDevToolsCallArgs&); + + void allMessagesProcessed(); + TaskList* taskList() { return &m_taskList; } + + private: + void call(const DRTDevToolsCallArgs&); + class AsyncCallTask: public MethodTask<DRTDevToolsClient> { + public: + AsyncCallTask(DRTDevToolsClient* object, const DRTDevToolsCallArgs& args) + : MethodTask<DRTDevToolsClient>(object), m_args(args) {} + virtual void runIfValid() { m_object->call(m_args); } + private: + DRTDevToolsCallArgs m_args; + }; + + TaskList m_taskList; + WebKit::WebView* m_webView; + DRTDevToolsAgent* m_drtDevToolsAgent; + WTF::OwnPtr<WebKit::WebDevToolsFrontend> m_webDevToolsFrontend; +}; + +#endif // DRTDevToolsClient_h diff --git a/Tools/DumpRenderTree/chromium/DumpRenderTree.cpp b/Tools/DumpRenderTree/chromium/DumpRenderTree.cpp new file mode 100644 index 0000000..3bbba98 --- /dev/null +++ b/Tools/DumpRenderTree/chromium/DumpRenderTree.cpp @@ -0,0 +1,214 @@ +/* + * 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 <v8/include/v8.h> +#include <wtf/Vector.h> + +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 const char optionPixelTestsWithName[] = "--pixel-tests="; +static const char optionTestShell[] = "--test-shell"; +static const char optionAllowExternalPages[] = "--allow-external-pages"; +static const char optionStartupDialog[] = "--testshell-startup-dialog"; +static const char optionCheckLayoutTestSystemDeps[] = "--check-layout-test-sys-deps"; +static const char optionEnableAcceleratedCompositing[] = "--enable-accelerated-compositing"; +static const char optionEnableAccelerated2DCanvas[] = "--enable-accelerated-2d-canvas"; + +static const char optionMultipleLoads[] = "--multiple-loads="; +static const char optionJavaScriptFlags[] = "--js-flags="; + +static void runTest(TestShell& shell, TestParams& params, const string& testName, bool testShellMode) +{ + int oldTimeoutMsec = shell.layoutTestTimeout(); + params.pixelHash = ""; + string pathOrURL = testName; + if (testShellMode) { + string timeOut; + string::size_type separatorPosition = pathOrURL.find(' '); + if (separatorPosition != string::npos) { + timeOut = pathOrURL.substr(separatorPosition + 1); + pathOrURL.erase(separatorPosition); + separatorPosition = timeOut.find_first_of(' '); + if (separatorPosition != string::npos) { + params.pixelHash = timeOut.substr(separatorPosition + 1); + timeOut.erase(separatorPosition); + } + shell.setLayoutTestTimeout(atoi(timeOut.c_str())); + } + } else { + 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); + webkit_support::SetCurrentDirectoryForFileURL(params.testUrl); + for (int i = 0; i < shell.loadCount(); i++) { + string javaScriptFlags = shell.javaScriptFlagsForLoad(i); + v8::V8::SetFlagsFromString(javaScriptFlags.data(), static_cast<int>(javaScriptFlags.size())); + bool isLastLoad = (i == (shell.loadCount() - 1)); + shell.setDumpWhenFinished(isLastLoad); + shell.resetTestController(); + shell.runFileTest(params); + } + shell.setLayoutTestTimeout(oldTimeoutMsec); +} + +int main(int argc, char* argv[]) +{ + webkit_support::SetUpTestEnvironment(); + platformInit(&argc, &argv); + + TestParams params; + Vector<string> tests; + bool serverMode = false; + bool testShellMode = false; + bool allowExternalPages = false; + bool startupDialog = false; + bool acceleratedCompositingEnabled = false; + bool accelerated2DCanvasEnabled = false; + int loadCount = 1; + string javaScriptFlags; + 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.find(optionPixelTestsWithName)) { + params.dumpPixels = true; + params.pixelFileName = argument.substr(strlen(optionPixelTestsWithName)); + } else if (argument == optionTestShell) { + testShellMode = true; + serverMode = true; + } else if (argument == optionAllowExternalPages) + allowExternalPages = true; + else if (argument == optionStartupDialog) + startupDialog = true; + else if (argument == optionCheckLayoutTestSystemDeps) + exit(checkLayoutTestSystemDependencies() ? EXIT_SUCCESS : EXIT_FAILURE); + else if (argument == optionEnableAcceleratedCompositing) + acceleratedCompositingEnabled = true; + else if (argument == optionEnableAccelerated2DCanvas) + accelerated2DCanvasEnabled = true; + else if (!argument.find(optionMultipleLoads)) { + string multipleLoadsStr = argument.substr(strlen(optionMultipleLoads)); + loadCount = atoi(multipleLoadsStr.c_str()); + } else if (!argument.find(optionJavaScriptFlags)) { + javaScriptFlags = argument.substr(strlen(optionJavaScriptFlags)); + } else if (argument.size() && argument[0] == '-') + fprintf(stderr, "Unknown option: %s\n", argv[i]); + else + tests.append(argument); + } + if (testShellMode && params.dumpPixels && params.pixelFileName.empty()) { + fprintf(stderr, "--pixel-tests with --test-shell requires a file name.\n"); + return EXIT_FAILURE; + } + if (loadCount < 1) { + fprintf(stderr, "--multiple-loads requires a positive numeric argument.\n"); + return EXIT_FAILURE; + } + + // The test runner might send a quoted string which needs to be unquoted before further processing. + if (javaScriptFlags.length() > 1 && javaScriptFlags[0] == '"' && javaScriptFlags[javaScriptFlags.length() - 1] == '"') + javaScriptFlags = javaScriptFlags.substr(1, javaScriptFlags.length() - 2); + // Split the JavaScript flags into a list. + Vector<string> flagsList; + size_t start = 0; + while (true) { + size_t commaPos = javaScriptFlags.find_first_of(',', start); + string flags; + if (commaPos == string::npos) + flags = javaScriptFlags.substr(start, javaScriptFlags.length() - start); + else { + flags = javaScriptFlags.substr(start, commaPos - start); + start = commaPos + 1; + } + flagsList.append(flags); + if (commaPos == string::npos) + break; + } + + if (startupDialog) + openStartupDialog(); + + { // Explicit scope for the TestShell instance. + TestShell shell(testShellMode); + shell.setAllowExternalPages(allowExternalPages); + shell.setAcceleratedCompositingEnabled(acceleratedCompositingEnabled); + shell.setAccelerated2dCanvasEnabled(accelerated2DCanvasEnabled); + shell.setLoadCount(loadCount); + shell.setJavaScriptFlags(flagsList); + 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, testShellMode); + } + } 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], testShellMode); + } + + 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/Tools/DumpRenderTree/chromium/EventSender.cpp b/Tools/DumpRenderTree/chromium/EventSender.cpp new file mode 100644 index 0000000..6104a90 --- /dev/null +++ b/Tools/DumpRenderTree/chromium/EventSender.cpp @@ -0,0 +1,1008 @@ +/* + * 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 "WebContextMenuData.h" +#include "WebDragData.h" +#include "WebDragOperation.h" +#include "WebPoint.h" +#include "WebString.h" +#include "WebTouchPoint.h" +#include "WebView.h" +#include "webkit/support/webkit_support.h" +#include <wtf/Deque.h> +#include <wtf/StringExtras.h> + +#if OS(WINDOWS) +#include "win/WebInputEventFactory.h" +#endif + +// FIXME: layout before each event? + +using namespace std; +using namespace WebKit; + +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; +static int touchModifiers; +static Vector<WebTouchPoint> touchPoints; + +// 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 (webkit_support::GetCurrentTimeInMillisecond() + 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 and everywhere non-Mac, 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 webkit_support::VKEY_LEFT: + *name = "MoveToBeginningOfLine"; + break; + case webkit_support::VKEY_RIGHT: + *name = "MoveToEndOfLine"; + break; + case webkit_support::VKEY_UP: + *name = "MoveToBeginningOfDocument"; + break; + case webkit_support::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_shell(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("addTouchPoint", &EventSender::addTouchPoint); + bindMethod("beginDragWithFiles", &EventSender::beginDragWithFiles); + bindMethod("cancelTouchPoint", &EventSender::cancelTouchPoint); + bindMethod("clearKillRing", &EventSender::clearKillRing); + bindMethod("clearTouchPoints", &EventSender::clearTouchPoints); + bindMethod("contextClick", &EventSender::contextClick); + bindMethod("continuousMouseScrollBy", &EventSender::continuousMouseScrollBy); + bindMethod("dispatchMessage", &EventSender::dispatchMessage); + bindMethod("enableDOMUIEventLogging", &EventSender::enableDOMUIEventLogging); + bindMethod("fireKeyboardEventsToElement", &EventSender::fireKeyboardEventsToElement); + bindMethod("keyDown", &EventSender::keyDown); + bindMethod("leapForward", &EventSender::leapForward); + bindMethod("mouseDown", &EventSender::mouseDown); + bindMethod("mouseMoveTo", &EventSender::mouseMoveTo); + bindMethod("mouseScrollBy", &EventSender::mouseScrollBy); + bindMethod("mouseUp", &EventSender::mouseUp); + bindMethod("releaseTouchPoint", &EventSender::releaseTouchPoint); + bindMethod("scheduleAsynchronousClick", &EventSender::scheduleAsynchronousClick); + bindMethod("setTouchModifier", &EventSender::setTouchModifier); + bindMethod("textZoomIn", &EventSender::textZoomIn); + bindMethod("textZoomOut", &EventSender::textZoomOut); + bindMethod("touchCancel", &EventSender::touchCancel); + bindMethod("touchEnd", &EventSender::touchEnd); + bindMethod("touchMove", &EventSender::touchMove); + bindMethod("touchStart", &EventSender::touchStart); + bindMethod("updateTouchPoint", &EventSender::updateTouchPoint); + bindMethod("zoomPageIn", &EventSender::zoomPageIn); + bindMethod("zoomPageOut", &EventSender::zoomPageOut); + + // 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; + touchModifiers = 0; + touchPoints.clear(); + m_taskList.revokeAll(); +} + +WebView* EventSender::webview() +{ + return m_shell->webView(); +} + +void EventSender::doDragDrop(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::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 = webkit_support::VKEY_RETURN; + } else if ("rightArrow" == codeStr) + code = webkit_support::VKEY_RIGHT; + else if ("downArrow" == codeStr) + code = webkit_support::VKEY_DOWN; + else if ("leftArrow" == codeStr) + code = webkit_support::VKEY_LEFT; + else if ("upArrow" == codeStr) + code = webkit_support::VKEY_UP; + else if ("insert" == codeStr) + code = webkit_support::VKEY_INSERT; + else if ("delete" == codeStr) + code = webkit_support::VKEY_DELETE; + else if ("pageUp" == codeStr) + code = webkit_support::VKEY_PRIOR; + else if ("pageDown" == codeStr) + code = webkit_support::VKEY_NEXT; + else if ("home" == codeStr) + code = webkit_support::VKEY_HOME; + else if ("end" == codeStr) + code = webkit_support::VKEY_END; + else if ("printScreen" == codeStr) + code = webkit_support::VKEY_SNAPSHOT; + 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 = webkit_support::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)) + m_shell->webViewHost()->setEditCommand(editCommand, ""); + + webview()->handleInputEvent(eventDown); + + m_shell->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::mouseScrollBy(const CppArgumentList& arguments, CppVariant* result) +{ + handleMouseWheel(arguments, result, false); +} + +void EventSender::continuousMouseScrollBy(const CppArgumentList& arguments, CppVariant* result) +{ + handleMouseWheel(arguments, result, true); +} + +void EventSender::replaySavedEvents() +{ + replayingSavedEvents = true; + while (!mouseEventQueue.isEmpty()) { + SavedEvent e = mouseEventQueue.takeFirst(); + + 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; +} + +// Because actual context menu is implemented by the browser side, +// this function does only what LayoutTests are expecting: +// - Many test checks the count of items. So returning non-zero value makes sense. +// - Some test compares the count before and after some action. So changing the count based on flags +// also makes sense. This function is doing such for some flags. +// - Some test even checks actual string content. So providing it would be also helpful. +// +static Vector<WebString> makeMenuItemStringsFor(WebContextMenuData* contextMenu, MockSpellCheck* spellcheck) +{ + // These constants are based on Safari's context menu because tests are made for it. + static const char* nonEditableMenuStrings[] = { "Back", "Reload Page", "Open in Dashbaord", "<separator>", "View Source", "Save Page As", "Print Page", "Inspect Element", 0 }; + static const char* editableMenuStrings[] = { "Cut", "Copy", "<separator>", "Paste", "Spelling and Grammar", "Substitutions, Transformations", "Font", "Speech", "Paragraph Direction", "<separator>", 0 }; + + // This is possible because mouse events are cancelleable. + if (!contextMenu) + return Vector<WebString>(); + + Vector<WebString> strings; + + if (contextMenu->isEditable) { + for (const char** item = editableMenuStrings; *item; ++item) + strings.append(WebString::fromUTF8(*item)); + Vector<WebString> suggestions; + spellcheck->fillSuggestionList(contextMenu->misspelledWord, &suggestions); + for (size_t i = 0; i < suggestions.size(); ++i) + strings.append(suggestions[i]); + } else { + for (const char** item = nonEditableMenuStrings; *item; ++item) + strings.append(WebString::fromUTF8(*item)); + } + + return strings; +} + + +void EventSender::contextClick(const CppArgumentList& arguments, CppVariant* result) +{ + webview()->layout(); + + updateClickCountForButton(WebMouseEvent::ButtonRight); + + // Clears last context menu data because we need to know if the context menu be requested + // after following mouse events. + m_shell->webViewHost()->clearContextMenuData(); + + // 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; + + WebContextMenuData* lastContextMenu = m_shell->webViewHost()->lastContextMenuData(); + result->set(WebBindings::makeStringArray(makeMenuItemStringsFor(lastContextMenu, m_shell->webViewHost()->mockSpellCheck()))); +} + +class MouseDownTask: public MethodTask<EventSender> { +public: + MouseDownTask(EventSender* obj, const CppArgumentList& arg) + : MethodTask<EventSender>(obj), m_arguments(arg) {} + virtual void runIfValid() { m_object->mouseDown(m_arguments, 0); } +private: + CppArgumentList m_arguments; +}; + +class MouseUpTask: public MethodTask<EventSender> { +public: + MouseUpTask(EventSender* obj, const CppArgumentList& arg) + : MethodTask<EventSender>(obj), m_arguments(arg) {} + virtual void runIfValid() { m_object->mouseUp(m_arguments, 0); } +private: + CppArgumentList m_arguments; +}; + +void EventSender::scheduleAsynchronousClick(const CppArgumentList& arguments, CppVariant* result) +{ + result->setNull(); + postTask(new MouseDownTask(this, arguments)); + postTask(new MouseUpTask(this, arguments)); +} + +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(); +} + +void EventSender::addTouchPoint(const CppArgumentList& arguments, CppVariant* result) +{ + result->setNull(); + + WebTouchPoint touchPoint; + touchPoint.state = WebTouchPoint::StatePressed; + touchPoint.position = WebPoint(arguments[0].toInt32(), arguments[1].toInt32()); + touchPoint.screenPosition = touchPoint.position; + touchPoint.id = touchPoints.size(); + touchPoints.append(touchPoint); +} + +void EventSender::clearTouchPoints(const CppArgumentList&, CppVariant* result) +{ + result->setNull(); + touchPoints.clear(); +} + +void EventSender::releaseTouchPoint(const CppArgumentList& arguments, CppVariant* result) +{ + result->setNull(); + + const unsigned index = arguments[0].toInt32(); + ASSERT(index < touchPoints.size()); + + WebTouchPoint* touchPoint = &touchPoints[index]; + touchPoint->state = WebTouchPoint::StateReleased; +} + +void EventSender::setTouchModifier(const CppArgumentList& arguments, CppVariant* result) +{ + result->setNull(); + + int mask = 0; + const string keyName = arguments[0].toString(); + if (keyName == "shift") + mask = WebInputEvent::ShiftKey; + else if (keyName == "alt") + mask = WebInputEvent::AltKey; + else if (keyName == "ctrl") + mask = WebInputEvent::ControlKey; + else if (keyName == "meta") + mask = WebInputEvent::MetaKey; + + if (arguments[1].toBoolean()) + touchModifiers |= mask; + else + touchModifiers &= ~mask; +} + +void EventSender::updateTouchPoint(const CppArgumentList& arguments, CppVariant* result) +{ + result->setNull(); + + const unsigned index = arguments[0].toInt32(); + ASSERT(index < touchPoints.size()); + + WebPoint position(arguments[1].toInt32(), arguments[2].toInt32()); + WebTouchPoint* touchPoint = &touchPoints[index]; + touchPoint->state = WebTouchPoint::StateMoved; + touchPoint->position = position; + touchPoint->screenPosition = position; +} + +void EventSender::cancelTouchPoint(const CppArgumentList& arguments, CppVariant* result) +{ + result->setNull(); + + const unsigned index = arguments[0].toInt32(); + ASSERT(index < touchPoints.size()); + + WebTouchPoint* touchPoint = &touchPoints[index]; + touchPoint->state = WebTouchPoint::StateCancelled; +} + +void EventSender::sendCurrentTouchEvent(const WebInputEvent::Type type) +{ + ASSERT(static_cast<unsigned>(WebTouchEvent::touchPointsLengthCap) > touchPoints.size()); + webview()->layout(); + + WebTouchEvent touchEvent; + touchEvent.type = type; + touchEvent.modifiers = touchModifiers; + touchEvent.timeStampSeconds = getCurrentEventTimeSec(); + touchEvent.touchPointsLength = touchPoints.size(); + for (unsigned i = 0; i < touchPoints.size(); ++i) + touchEvent.touchPoints[i] = touchPoints[i]; + webview()->handleInputEvent(touchEvent); + + for (unsigned i = 0; i < touchPoints.size(); ++i) { + WebTouchPoint* touchPoint = &touchPoints[i]; + if (touchPoint->state == WebTouchPoint::StateReleased) { + touchPoints.remove(i); + --i; + } else + touchPoint->state = WebTouchPoint::StateStationary; + } +} + +void EventSender::handleMouseWheel(const CppArgumentList& arguments, CppVariant* result, bool continuous) +{ + 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). + 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 = event.wheelTicksX; + event.deltaY = event.wheelTicksY; + if (continuous) { + event.wheelTicksX /= scrollbarPixelsPerTick; + event.wheelTicksY /= scrollbarPixelsPerTick; + } else { + event.deltaX *= scrollbarPixelsPerTick; + event.deltaY *= scrollbarPixelsPerTick; + } + webview()->handleInputEvent(event); +} + +void EventSender::touchEnd(const CppArgumentList&, CppVariant* result) +{ + result->setNull(); + sendCurrentTouchEvent(WebInputEvent::TouchEnd); +} + +void EventSender::touchMove(const CppArgumentList&, CppVariant* result) +{ + result->setNull(); + sendCurrentTouchEvent(WebInputEvent::TouchMove); +} + +void EventSender::touchStart(const CppArgumentList&, CppVariant* result) +{ + result->setNull(); + sendCurrentTouchEvent(WebInputEvent::TouchStart); +} + +void EventSender::touchCancel(const CppArgumentList&, CppVariant* result) +{ + result->setNull(); + sendCurrentTouchEvent(WebInputEvent::TouchCancel); +} + +// +// 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/Tools/DumpRenderTree/chromium/EventSender.h b/Tools/DumpRenderTree/chromium/EventSender.h new file mode 100644 index 0000000..118509b --- /dev/null +++ b/Tools/DumpRenderTree/chromium/EventSender.h @@ -0,0 +1,163 @@ +/* + * 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 "Task.h" +#include "WebDragOperation.h" +#include "WebInputEvent.h" +#include "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. + void doDragDrop(const WebKit::WebDragData&, WebKit::WebDragOperationsMask); + + // JS callback methods. + void mouseDown(const CppArgumentList&, CppVariant*); + void mouseUp(const CppArgumentList&, CppVariant*); + void mouseMoveTo(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 mouseScrollBy(const CppArgumentList&, CppVariant*); + void continuousMouseScrollBy(const CppArgumentList&, CppVariant*); + void scheduleAsynchronousClick(const CppArgumentList&, CppVariant*); + void beginDragWithFiles(const CppArgumentList&, CppVariant*); + CppVariant dragMode; + + void addTouchPoint(const CppArgumentList&, CppVariant*); + void cancelTouchPoint(const CppArgumentList&, CppVariant*); + void clearTouchPoints(const CppArgumentList&, CppVariant*); + void releaseTouchPoint(const CppArgumentList&, CppVariant*); + void setTouchModifier(const CppArgumentList&, CppVariant*); + void touchCancel(const CppArgumentList&, CppVariant*); + void touchEnd(const CppArgumentList&, CppVariant*); + void touchMove(const CppArgumentList&, CppVariant*); + void touchStart(const CppArgumentList&, CppVariant*); + void updateTouchPoint(const CppArgumentList&, CppVariant*); + + // 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 + + TaskList* taskList() { return &m_taskList; } + +private: + // Returns the test shell's webview. + 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. + void doMouseMove(const WebKit::WebMouseEvent&); + void doMouseUp(const WebKit::WebMouseEvent&); + static void doLeapForward(int milliseconds); + 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); + + // Compose a touch event from the current touch points and send it. + void sendCurrentTouchEvent(const WebKit::WebInputEvent::Type); + + // Handle a request to send a wheel event. + void handleMouseWheel(const CppArgumentList&, CppVariant*, bool continuous); + + TaskList m_taskList; + + // Non-owning pointer. The EventSender is owned by the TestShell. + TestShell* m_shell; + + // 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/Tools/DumpRenderTree/chromium/ImageDiff.cpp b/Tools/DumpRenderTree/chromium/ImageDiff.cpp new file mode 100644 index 0000000..f2875dd --- /dev/null +++ b/Tools/DumpRenderTree/chromium/ImageDiff.cpp @@ -0,0 +1,408 @@ +/* + * 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 "webkit/support/webkit_support_gfx.h" +#include <algorithm> +#include <stdio.h> +#include <string.h> +#include <vector> +#include <wtf/OwnArrayPtr.h> +#include <wtf/Vector.h> + +#if OS(WINDOWS) +#include <windows.h> +#define PATH_MAX MAX_PATH +#endif + +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 (!webkit_support::DecodePNG(source.get(), byteLength, &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 (!webkit_support::DecodePNG(&compressed[0], compressed.size(), &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", outFile); + return false; + } + if (dataSize != fwrite(data, 1, dataSize, file)) { + fclose(file); + fprintf(stderr, "ImageDiff: Unable to write data to file \"%s\"\n", outFile); + 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; + webkit_support::EncodeRGBAPNG(diffImage.data(), diffImage.width(), diffImage.height(), + diffImage.width() * 4, &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/Tools/DumpRenderTree/chromium/LayoutTestController.cpp b/Tools/DumpRenderTree/chromium/LayoutTestController.cpp new file mode 100644 index 0000000..e3af738 --- /dev/null +++ b/Tools/DumpRenderTree/chromium/LayoutTestController.cpp @@ -0,0 +1,1590 @@ +/* + * 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 "DRTDevToolsAgent.h" +#include "TestShell.h" +#include "WebAnimationController.h" +#include "WebBindings.h" +#include "WebConsoleMessage.h" +#include "WebData.h" +#include "WebDeviceOrientation.h" +#include "WebDeviceOrientationClientMock.h" +#include "WebDocument.h" +#include "WebElement.h" +#include "WebFrame.h" +#if ENABLE(CLIENT_BASED_GEOLOCATION) +#include "WebGeolocationClientMock.h" +#else +#include "WebGeolocationServiceMock.h" +#endif +#include "WebInputElement.h" +#include "WebKit.h" +#include "WebNotificationPresenter.h" +#include "WebScriptSource.h" +#include "WebSecurityPolicy.h" +#include "WebSettings.h" +#include "WebSize.h" +#include "WebSpeechInputControllerMock.h" +#include "WebURL.h" +#include "WebView.h" +#include "WebViewHost.h" +#include "webkit/support/webkit_support.h" +#include <algorithm> +#include <cstdlib> +#include <limits> +#include <wtf/text/WTFString.h> + +#if OS(WINDOWS) +#include <wtf/OwnArrayPtr.h> +#endif + +using namespace WebCore; +using namespace WebKit; +using namespace std; + +LayoutTestController::LayoutTestController(TestShell* shell) + : m_shell(shell) + , m_closeRemainingWindows(false) + , m_deferMainResourceDataLoad(false) + , 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("addFileToPasteboardOnDrag", &LayoutTestController::addFileToPasteboardOnDrag); + bindMethod("addOriginAccessWhitelistEntry", &LayoutTestController::addOriginAccessWhitelistEntry); + bindMethod("addUserScript", &LayoutTestController::addUserScript); + bindMethod("addUserStyleSheet", &LayoutTestController::addUserStyleSheet); + bindMethod("clearAllDatabases", &LayoutTestController::clearAllDatabases); + bindMethod("closeWebInspector", &LayoutTestController::closeWebInspector); + bindMethod("counterValueForElementById", &LayoutTestController::counterValueForElementById); + bindMethod("disableImageLoading", &LayoutTestController::disableImageLoading); + bindMethod("display", &LayoutTestController::display); + bindMethod("dumpAsText", &LayoutTestController::dumpAsText); + bindMethod("dumpBackForwardList", &LayoutTestController::dumpBackForwardList); + bindMethod("dumpChildFramesAsText", &LayoutTestController::dumpChildFramesAsText); + bindMethod("dumpChildFrameScrollPositions", &LayoutTestController::dumpChildFrameScrollPositions); + bindMethod("dumpDatabaseCallbacks", &LayoutTestController::dumpDatabaseCallbacks); + bindMethod("dumpEditingCallbacks", &LayoutTestController::dumpEditingCallbacks); + bindMethod("dumpFrameLoadCallbacks", &LayoutTestController::dumpFrameLoadCallbacks); + bindMethod("dumpUserGestureInFrameLoadCallbacks", &LayoutTestController::dumpUserGestureInFrameLoadCallbacks); + bindMethod("dumpResourceLoadCallbacks", &LayoutTestController::dumpResourceLoadCallbacks); + bindMethod("dumpResourceResponseMIMETypes", &LayoutTestController::dumpResourceResponseMIMETypes); + bindMethod("dumpSelectionRect", &LayoutTestController::dumpSelectionRect); + bindMethod("dumpStatusCallbacks", &LayoutTestController::dumpWindowStatusChanges); + bindMethod("dumpTitleChanges", &LayoutTestController::dumpTitleChanges); + bindMethod("elementDoesAutoCompleteForElementWithId", &LayoutTestController::elementDoesAutoCompleteForElementWithId); + bindMethod("evaluateInWebInspector", &LayoutTestController::evaluateInWebInspector); + bindMethod("evaluateScriptInIsolatedWorld", &LayoutTestController::evaluateScriptInIsolatedWorld); + bindMethod("execCommand", &LayoutTestController::execCommand); + bindMethod("forceRedSelectionColors", &LayoutTestController::forceRedSelectionColors); + bindMethod("grantDesktopNotificationPermission", &LayoutTestController::grantDesktopNotificationPermission); + bindMethod("isCommandEnabled", &LayoutTestController::isCommandEnabled); + bindMethod("layerTreeAsText", &LayoutTestController::layerTreeAsText); + bindMethod("markerTextForListItem", &LayoutTestController::markerTextForListItem); + bindMethod("hasSpellingMarker", &LayoutTestController::hasSpellingMarker); + bindMethod("notifyDone", &LayoutTestController::notifyDone); + bindMethod("numberOfActiveAnimations", &LayoutTestController::numberOfActiveAnimations); + bindMethod("numberOfPages", &LayoutTestController::numberOfPages); + bindMethod("objCIdentityIsEqual", &LayoutTestController::objCIdentityIsEqual); + bindMethod("overridePreference", &LayoutTestController::overridePreference); + bindMethod("pageNumberForElementById", &LayoutTestController::pageNumberForElementById); + bindMethod("pathToLocalResource", &LayoutTestController::pathToLocalResource); + bindMethod("pauseAnimationAtTimeOnElementWithId", &LayoutTestController::pauseAnimationAtTimeOnElementWithId); + bindMethod("pauseTransitionAtTimeOnElementWithId", &LayoutTestController::pauseTransitionAtTimeOnElementWithId); + bindMethod("queueBackNavigation", &LayoutTestController::queueBackNavigation); + bindMethod("queueForwardNavigation", &LayoutTestController::queueForwardNavigation); + bindMethod("queueLoadingScript", &LayoutTestController::queueLoadingScript); + bindMethod("queueLoad", &LayoutTestController::queueLoad); + bindMethod("queueLoadHTMLString", &LayoutTestController::queueLoadHTMLString); + bindMethod("queueNonLoadingScript", &LayoutTestController::queueNonLoadingScript); + bindMethod("queueReload", &LayoutTestController::queueReload); + bindMethod("removeOriginAccessWhitelistEntry", &LayoutTestController::removeOriginAccessWhitelistEntry); + bindMethod("repaintSweepHorizontally", &LayoutTestController::repaintSweepHorizontally); + bindMethod("resumeAnimations", &LayoutTestController::resumeAnimations); + bindMethod("sampleSVGAnimationForElementAtTime", &LayoutTestController::sampleSVGAnimationForElementAtTime); + bindMethod("setAcceptsEditing", &LayoutTestController::setAcceptsEditing); + bindMethod("setAllowFileAccessFromFileURLs", &LayoutTestController::setAllowFileAccessFromFileURLs); + bindMethod("setAllowUniversalAccessFromFileURLs", &LayoutTestController::setAllowUniversalAccessFromFileURLs); + bindMethod("setAlwaysAcceptCookies", &LayoutTestController::setAlwaysAcceptCookies); + bindMethod("setAuthorAndUserStylesEnabled", &LayoutTestController::setAuthorAndUserStylesEnabled); + bindMethod("setCanOpenWindows", &LayoutTestController::setCanOpenWindows); + bindMethod("setCloseRemainingWindowsWhenComplete", &LayoutTestController::setCloseRemainingWindowsWhenComplete); + bindMethod("setCustomPolicyDelegate", &LayoutTestController::setCustomPolicyDelegate); + bindMethod("setDatabaseQuota", &LayoutTestController::setDatabaseQuota); + bindMethod("setDeferMainResourceDataLoad", &LayoutTestController::setDeferMainResourceDataLoad); + bindMethod("setDomainRelaxationForbiddenForURLScheme", &LayoutTestController::setDomainRelaxationForbiddenForURLScheme); + bindMethod("setEditingBehavior", &LayoutTestController::setEditingBehavior); + bindMethod("setGeolocationPermission", &LayoutTestController::setGeolocationPermission); + bindMethod("setIconDatabaseEnabled", &LayoutTestController::setIconDatabaseEnabled); + bindMethod("setJavaScriptCanAccessClipboard", &LayoutTestController::setJavaScriptCanAccessClipboard); + bindMethod("setMockDeviceOrientation", &LayoutTestController::setMockDeviceOrientation); + bindMethod("setMockGeolocationError", &LayoutTestController::setMockGeolocationError); + bindMethod("setMockGeolocationPosition", &LayoutTestController::setMockGeolocationPosition); + bindMethod("addMockSpeechInputResult", &LayoutTestController::addMockSpeechInputResult); + bindMethod("setPopupBlockingEnabled", &LayoutTestController::setPopupBlockingEnabled); + bindMethod("setPOSIXLocale", &LayoutTestController::setPOSIXLocale); + bindMethod("setScrollbarPolicy", &LayoutTestController::setScrollbarPolicy); + bindMethod("setSelectTrailingWhitespaceEnabled", &LayoutTestController::setSelectTrailingWhitespaceEnabled); + bindMethod("setSmartInsertDeleteEnabled", &LayoutTestController::setSmartInsertDeleteEnabled); + bindMethod("setStopProvisionalFrameLoads", &LayoutTestController::setStopProvisionalFrameLoads); + bindMethod("setTabKeyCyclesThroughElements", &LayoutTestController::setTabKeyCyclesThroughElements); + bindMethod("setTimelineProfilingEnabled", &LayoutTestController::setTimelineProfilingEnabled); + bindMethod("setUserStyleSheetEnabled", &LayoutTestController::setUserStyleSheetEnabled); + bindMethod("setUserStyleSheetLocation", &LayoutTestController::setUserStyleSheetLocation); + bindMethod("setWillSendRequestClearHeader", &LayoutTestController::setWillSendRequestClearHeader); + bindMethod("setWillSendRequestReturnsNull", &LayoutTestController::setWillSendRequestReturnsNull); + bindMethod("setWillSendRequestReturnsNullOnRedirect", &LayoutTestController::setWillSendRequestReturnsNullOnRedirect); + bindMethod("setWindowIsKey", &LayoutTestController::setWindowIsKey); + bindMethod("setXSSAuditorEnabled", &LayoutTestController::setXSSAuditorEnabled); + bindMethod("setAsynchronousSpellCheckingEnabled", &LayoutTestController::setAsynchronousSpellCheckingEnabled); + bindMethod("showWebInspector", &LayoutTestController::showWebInspector); + bindMethod("simulateDesktopNotificationClick", &LayoutTestController::simulateDesktopNotificationClick); + bindMethod("suspendAnimations", &LayoutTestController::suspendAnimations); + bindMethod("testRepaint", &LayoutTestController::testRepaint); + bindMethod("waitForPolicyDelegate", &LayoutTestController::waitForPolicyDelegate); + bindMethod("waitUntilDone", &LayoutTestController::waitUntilDone); + bindMethod("windowCount", &LayoutTestController::windowCount); + + // The following are stubs. + bindMethod("abortModal", &LayoutTestController::abortModal); + bindMethod("accessStoredWebScriptObject", &LayoutTestController::accessStoredWebScriptObject); + bindMethod("addDisallowedURL", &LayoutTestController::addDisallowedURL); + bindMethod("callShouldCloseOnWebView", &LayoutTestController::callShouldCloseOnWebView); + bindMethod("clearAllApplicationCaches", &LayoutTestController::clearAllApplicationCaches); + bindMethod("clearBackForwardList", &LayoutTestController::clearBackForwardList); + bindMethod("dumpAsWebArchive", &LayoutTestController::dumpAsWebArchive); + bindMethod("keepWebHistory", &LayoutTestController::keepWebHistory); + bindMethod("objCClassNameOf", &LayoutTestController::objCClassNameOf); + bindMethod("setApplicationCacheOriginQuota", &LayoutTestController::setApplicationCacheOriginQuota); + bindMethod("setCallCloseOnWebViews", &LayoutTestController::setCallCloseOnWebViews); + bindMethod("setMainFrameIsFirstResponder", &LayoutTestController::setMainFrameIsFirstResponder); + bindMethod("setPrivateBrowsingEnabled", &LayoutTestController::setPrivateBrowsingEnabled); + bindMethod("setUseDashboardCompatibilityMode", &LayoutTestController::setUseDashboardCompatibilityMode); + bindMethod("storeWebScriptObject", &LayoutTestController::storeWebScriptObject); + + // 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::~LayoutTestController() +{ +} + +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. + postTask(new WorkQueueTask(this)); + } 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.takeFirst(); + 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.takeFirst(); + } +} + +void LayoutTestController::WorkQueue::addWork(WorkItem* work) +{ + if (m_frozen) { + delete work; + return; + } + m_queue.append(work); +} + +void LayoutTestController::dumpAsText(const CppArgumentList& arguments, CppVariant* result) +{ + m_dumpAsText = true; + m_generatePixelResults = false; + + // Optional paramater, describing whether it's allowed to dump pixel results in dumpAsText mode. + if (arguments.size() > 0 && arguments[0].isBool()) + m_generatePixelResults = arguments[0].value.boolValue; + + 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::dumpUserGestureInFrameLoadCallbacks(const CppArgumentList&, CppVariant* result) +{ + m_dumpUserGestureInFrameLoadCallbacks = true; + result->setNull(); +} + +void LayoutTestController::dumpResourceLoadCallbacks(const CppArgumentList&, CppVariant* result) +{ + m_dumpResourceLoadCallbacks = true; + result->setNull(); +} + +void LayoutTestController::dumpResourceResponseMIMETypes(const CppArgumentList&, CppVariant* result) +{ + m_dumpResourceResponseMIMETypes = 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()) + postDelayedTask(new NotifyDoneTimedOutTask(this), m_shell->layoutTestTimeout()); + m_waitUntilDone = true; + result->setNull(); +} + +void LayoutTestController::notifyDone(const CppArgumentList&, CppVariant* result) +{ + // Test didn't timeout. Kill the timeout timer. + m_taskList.revokeAll(); + + completeNotifyDone(false); + result->setNull(); +} + +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(); +} + +class WorkItemLoadHTMLString : public LayoutTestController::WorkItem { +public: + WorkItemLoadHTMLString(const std::string& html, const WebURL& baseURL) + : m_html(html) + , m_baseURL(baseURL) {} + bool run(TestShell* shell) + { + shell->webView()->mainFrame()->loadHTMLString( + WebKit::WebData(m_html.data(), m_html.length()), m_baseURL); + return true; + } +private: + std::string m_html; + WebURL m_baseURL; +}; + +void LayoutTestController::queueLoadHTMLString(const CppArgumentList& arguments, CppVariant* result) +{ + if (arguments.size() > 0 && arguments[0].isString()) { + string html = arguments[0].toString(); + WebURL baseURL; + if (arguments.size() > 1 && arguments[1].isString()) + baseURL = WebURL(GURL(arguments[1].toString())); + m_workQueue.addWork(new WorkItemLoadHTMLString(html, baseURL)); + } + 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 !OS(DARWIN) && !OS(WINDOWS) // Actually, TOOLKIT_GTK + // (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 + m_shell->webView()->removeAllUserContent(); + } + m_dumpAsText = false; + m_dumpEditingCallbacks = false; + m_dumpFrameLoadCallbacks = false; + m_dumpUserGestureInFrameLoadCallbacks = false; + m_dumpResourceLoadCallbacks = false; + m_dumpResourceResponseMIMETypes = false; + m_dumpBackForwardList = false; + m_dumpChildFrameScrollPositions = false; + m_dumpChildFramesAsText = false; + m_dumpWindowStatusChanges = false; + m_dumpSelectionRect = false; + m_dumpTitleChanges = false; + m_generatePixelResults = true; + m_acceptsEditing = true; + m_waitUntilDone = false; + m_canOpenWindows = false; + m_testRepaint = false; + m_sweepHorizontally = false; + m_shouldAddFileToPasteboard = false; + m_stopProvisionalFrameLoads = false; + m_deferMainResourceDataLoad = true; + m_globalFlag.set(false); + m_webHistoryItemCount.set(0); + m_userStyleSheetLocation = WebURL(); + + 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(); + m_taskList.revokeAll(); +} + +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::setAsynchronousSpellCheckingEnabled(const CppArgumentList&, CppVariant*) +{ + // FIXME: Implement this. +} + +void LayoutTestController::showWebInspector(const CppArgumentList&, CppVariant* result) +{ + m_shell->showDevTools(); + result->setNull(); +} + +void LayoutTestController::closeWebInspector(const CppArgumentList& args, CppVariant* result) +{ + m_shell->closeDevTools(); + 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->preferences()->userStyleSheetLocation = arguments[0].value.boolValue ? m_userStyleSheetLocation : WebURL(); + m_shell->applyPreferences(); + } + result->setNull(); +} + +void LayoutTestController::setUserStyleSheetLocation(const CppArgumentList& arguments, CppVariant* result) +{ + if (arguments.size() > 0 && arguments[0].isString()) { + m_userStyleSheetLocation = webkit_support::LocalFileToDataURL( + webkit_support::RewriteLayoutTestsURL(arguments[0].toString())); + m_shell->preferences()->userStyleSheetLocation = m_userStyleSheetLocation; + m_shell->applyPreferences(); + } + result->setNull(); +} + +void LayoutTestController::setAuthorAndUserStylesEnabled(const CppArgumentList& arguments, CppVariant* result) +{ + if (arguments.size() > 0 && arguments[0].isBool()) { + m_shell->preferences()->authorAndUserStylesEnabled = arguments[0].value.boolValue; + m_shell->applyPreferences(); + } + 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->preferences()->javaScriptCanOpenWindowsAutomatically = !blockPopups; + m_shell->applyPreferences(); + } + result->setNull(); +} + +void LayoutTestController::setUseDashboardCompatibilityMode(const CppArgumentList&, CppVariant* result) +{ + // We have no need to support Dashboard Compatibility Mode (mac-only) + result->setNull(); +} + +void LayoutTestController::clearAllApplicationCaches(const CppArgumentList&, CppVariant* result) +{ + // FIXME: implement to support Application Cache Quotas. + result->setNull(); +} + +void LayoutTestController::setApplicationCacheOriginQuota(const CppArgumentList&, CppVariant* result) +{ + // FIXME: implement to support Application Cache Quotas. + result->setNull(); +} + +void LayoutTestController::setScrollbarPolicy(const CppArgumentList&, CppVariant* result) +{ + // FIXME: implement. + // Currently only has a non-null implementation on QT. + 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::setWillSendRequestClearHeader(const CppArgumentList& arguments, CppVariant* result) +{ + if (arguments.size() > 0 && arguments[0].isString()) { + string header = arguments[0].toString(); + if (!header.empty()) + m_shell->webViewHost()->addClearHeader(String::fromUTF8(header.c_str())); + } + 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 (!url.find("/tmp/")) { + // 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); + } + 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:///. + string lowerUrl = url; + transform(lowerUrl.begin(), lowerUrl.end(), lowerUrl.begin(), ::tolower); + while (!lowerUrl.find("file:////")) { + url = url.substr(0, 8) + url.substr(9); + lowerUrl = lowerUrl.substr(0, 8) + lowerUrl.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.to<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::suspendAnimations() +{ + WebFrame* webFrame = m_shell->webView()->mainFrame(); + if (!webFrame) + return; + + WebAnimationController* controller = webFrame->animationController(); + if (!controller) + return; + + controller->suspendAnimations(); +} + +void LayoutTestController::resumeAnimations() +{ + WebFrame* webFrame = m_shell->webView()->mainFrame(); + if (!webFrame) + return; + + WebAnimationController* controller = webFrame->animationController(); + if (!controller) + return; + + controller->resumeAnimations(); +} + +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::suspendAnimations(const CppArgumentList&, CppVariant* result) +{ + suspendAnimations(); + result->setNull(); +} + +void LayoutTestController::resumeAnimations(const CppArgumentList&, CppVariant* result) +{ + resumeAnimations(); + result->setNull(); +} + +void LayoutTestController::sampleSVGAnimationForElementAtTime(const CppArgumentList& arguments, CppVariant* result) +{ + if (arguments.size() != 3) { + result->setNull(); + return; + } + WebString animationId = cppVariantToWebString(arguments[0]); + double time = arguments[1].toDouble(); + WebString elementId = cppVariantToWebString(arguments[2]); + bool success = m_shell->webView()->mainFrame()->pauseSVGAnimation(animationId, time, elementId); + result->set(success); +} + +void LayoutTestController::disableImageLoading(const CppArgumentList&, CppVariant* result) +{ + m_shell->preferences()->loadsImagesAutomatically = false; + m_shell->applyPreferences(); + result->setNull(); +} + +void LayoutTestController::setIconDatabaseEnabled(const CppArgumentList&, CppVariant* result) +{ + // We don't use the WebKit icon database. + result->setNull(); +} + +void LayoutTestController::callShouldCloseOnWebView(const CppArgumentList&, CppVariant* result) +{ + result->set(m_shell->webView()->dispatchBeforeUnloadEvent()); +} + +void LayoutTestController::grantDesktopNotificationPermission(const CppArgumentList& arguments, CppVariant* result) +{ + if (arguments.size() != 1 || !arguments[0].isString()) { + result->set(false); + return; + } + m_shell->notificationPresenter()->grantPermission(cppVariantToWebString(arguments[0])); + result->set(true); +} + +void LayoutTestController::simulateDesktopNotificationClick(const CppArgumentList& arguments, CppVariant* result) +{ + if (arguments.size() != 1 || !arguments[0].isString()) { + result->set(false); + return; + } + if (m_shell->notificationPresenter()->simulateClick(cppVariantToWebString(arguments[0]))) + result->set(true); + else + result->set(false); +} + +void LayoutTestController::setDomainRelaxationForbiddenForURLScheme(const CppArgumentList& arguments, CppVariant* result) +{ + if (arguments.size() != 2 || !arguments[0].isBool() || !arguments[1].isString()) + return; + m_shell->webView()->setDomainRelaxationForbidden(cppVariantToBool(arguments[0]), cppVariantToWebString(arguments[1])); +} + +void LayoutTestController::setDeferMainResourceDataLoad(const CppArgumentList& arguments, CppVariant* result) +{ + if (arguments.size() == 1) + m_deferMainResourceDataLoad = cppVariantToBool(arguments[0]); +} + +// +// 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::setJavaScriptCanAccessClipboard(const CppArgumentList& arguments, CppVariant* result) +{ + if (arguments.size() > 0 && arguments[0].isBool()) { + m_shell->preferences()->javaScriptCanAccessClipboard = arguments[0].value.boolValue; + m_shell->applyPreferences(); + } + result->setNull(); +} + +void LayoutTestController::setXSSAuditorEnabled(const CppArgumentList& arguments, CppVariant* result) +{ + if (arguments.size() > 0 && arguments[0].isBool()) { + m_shell->preferences()->XSSAuditorEnabled = arguments[0].value.boolValue; + m_shell->applyPreferences(); + } + result->setNull(); +} + +void LayoutTestController::evaluateScriptInIsolatedWorld(const CppArgumentList& arguments, CppVariant* result) +{ + if (arguments.size() >= 2 && arguments[0].isNumber() && arguments[1].isString()) { + WebScriptSource source(cppVariantToWebString(arguments[1])); + // 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->preferences()->allowUniversalAccessFromFileURLs = arguments[0].value.boolValue; + m_shell->applyPreferences(); + } + result->setNull(); +} + +void LayoutTestController::setAllowFileAccessFromFileURLs(const CppArgumentList& arguments, CppVariant* result) +{ + if (arguments.size() > 0 && arguments[0].isBool()) { + m_shell->preferences()->allowFileAccessFromFileURLs = arguments[0].value.boolValue; + m_shell->applyPreferences(); + } + 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.isNumber()) + 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.isNumber()) + return value.toInt32(); + if (value.isString()) { + string stringSource = value.toString(); + const char* source = stringSource.data(); + char* end; + long number = strtol(source, &end, 10); + if (end == source + stringSource.length() && number >= numeric_limits<int32_t>::min() && number <= numeric_limits<int32_t>::max()) + return static_cast<int32_t>(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]; + WebPreferences* prefs = m_shell->preferences(); + if (key == "WebKitStandardFont") + prefs->standardFontFamily = cppVariantToWebString(value); + else if (key == "WebKitFixedFont") + prefs->fixedFontFamily = cppVariantToWebString(value); + else if (key == "WebKitSerifFont") + prefs->serifFontFamily = cppVariantToWebString(value); + else if (key == "WebKitSansSerifFont") + prefs->sansSerifFontFamily = cppVariantToWebString(value); + else if (key == "WebKitCursiveFont") + prefs->cursiveFontFamily = cppVariantToWebString(value); + else if (key == "WebKitFantasyFont") + prefs->fantasyFontFamily = cppVariantToWebString(value); + else if (key == "WebKitDefaultFontSize") + prefs->defaultFontSize = cppVariantToInt32(value); + else if (key == "WebKitDefaultFixedFontSize") + prefs->defaultFixedFontSize = cppVariantToInt32(value); + else if (key == "WebKitMinimumFontSize") + prefs->minimumFontSize = cppVariantToInt32(value); + else if (key == "WebKitMinimumLogicalFontSize") + prefs->minimumLogicalFontSize = cppVariantToInt32(value); + else if (key == "WebKitDefaultTextEncodingName") + prefs->defaultTextEncodingName = cppVariantToWebString(value); + else if (key == "WebKitJavaScriptEnabled") + prefs->javaScriptEnabled = cppVariantToBool(value); + else if (key == "WebKitWebSecurityEnabled") + prefs->webSecurityEnabled = cppVariantToBool(value); + else if (key == "WebKitJavaScriptCanOpenWindowsAutomatically") + prefs->javaScriptCanOpenWindowsAutomatically = cppVariantToBool(value); + else if (key == "WebKitDisplayImagesKey") + prefs->loadsImagesAutomatically = cppVariantToBool(value); + else if (key == "WebKitPluginsEnabled") + prefs->pluginsEnabled = cppVariantToBool(value); + else if (key == "WebKitDOMPasteAllowedPreferenceKey") + prefs->DOMPasteAllowed = cppVariantToBool(value); + else if (key == "WebKitDeveloperExtrasEnabledPreferenceKey") + prefs->developerExtrasEnabled = cppVariantToBool(value); + else if (key == "WebKitShrinksStandaloneImagesToFit") + prefs->shrinksStandaloneImagesToFit = cppVariantToBool(value); + else if (key == "WebKitTextAreasAreResizable") + prefs->textAreasAreResizable = cppVariantToBool(value); + else if (key == "WebKitJavaEnabled") + prefs->javaEnabled = cppVariantToBool(value); + else if (key == "WebKitUsesPageCachePreferenceKey") + prefs->usesPageCache = cppVariantToBool(value); + else if (key == "WebKitJavaScriptCanAccessClipboard") + prefs->javaScriptCanAccessClipboard = cppVariantToBool(value); + else if (key == "WebKitXSSAuditorEnabled") + prefs->XSSAuditorEnabled = cppVariantToBool(value); + else if (key == "WebKitLocalStorageEnabledPreferenceKey") + prefs->localStorageEnabled = cppVariantToBool(value); + else if (key == "WebKitOfflineWebApplicationCacheEnabled") + prefs->offlineWebApplicationCacheEnabled = cppVariantToBool(value); + else if (key == "WebKitTabToLinksPreferenceKey") + prefs->tabsToLinks = cppVariantToBool(value); + else if (key == "WebKitWebGLEnabled") + prefs->experimentalWebGLEnabled = cppVariantToBool(value); + else if (key == "WebKitHyperlinkAuditingEnabled") + prefs->hyperlinkAuditingEnabled = cppVariantToBool(value); + else if (key == "WebKitEnableCaretBrowsing") + prefs->caretBrowsingEnabled = cppVariantToBool(value); + else { + string message("Invalid name for preference: "); + message.append(key); + logErrorToConsole(message); + } + m_shell->applyPreferences(); +} + +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::addOriginAccessWhitelistEntry( + url, + cppVariantToWebString(arguments[1]), + cppVariantToWebString(arguments[2]), + arguments[3].toBoolean()); +} + +void LayoutTestController::removeOriginAccessWhitelistEntry(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::removeOriginAccessWhitelistEntry( + url, + cppVariantToWebString(arguments[1]), + cppVariantToWebString(arguments[2]), + 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].isNumber()) + 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; + m_shell->drtDevToolsAgent()->setTimelineProfilingEnabled(arguments[0].toBoolean()); +} + +void LayoutTestController::evaluateInWebInspector(const CppArgumentList& arguments, CppVariant* result) +{ + result->setNull(); + if (arguments.size() < 2 || !arguments[0].isNumber() || !arguments[1].isString()) + return; + m_shell->drtDevToolsAgent()->evaluateInWebInspector(arguments[0].toInt32(), arguments[1].toString()); +} + +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() < 3 || !arguments[0].isString() || !arguments[1].isBool() || !arguments[2].isBool()) + return; + WebView::addUserScript( + cppVariantToWebString(arguments[0]), WebVector<WebString>(), + arguments[1].toBoolean() ? WebView::UserScriptInjectAtDocumentStart : WebView::UserScriptInjectAtDocumentEnd, + arguments[2].toBoolean() ? WebView::UserContentInjectInAllFrames : WebView::UserContentInjectInTopFrameOnly); +} + +void LayoutTestController::addUserStyleSheet(const CppArgumentList& arguments, CppVariant* result) +{ + result->setNull(); + if (arguments.size() < 2 || !arguments[0].isString() || !arguments[1].isBool()) + return; + WebView::addUserStyleSheet( + cppVariantToWebString(arguments[0]), WebVector<WebString>(), + arguments[1].toBoolean() ? WebView::UserContentInjectInAllFrames : WebView::UserContentInjectInTopFrameOnly, + // Chromium defaults to InjectInSubsequentDocuments, but for compatibility + // with the other ports' DRTs, we use UserStyleInjectInExistingDocuments. + WebView::UserStyleInjectInExistingDocuments); +} + +void LayoutTestController::setEditingBehavior(const CppArgumentList& arguments, CppVariant* results) +{ + string key = arguments[0].toString(); + if (key == "mac") { + m_shell->preferences()->editingBehavior = WebSettings::EditingBehaviorMac; + m_shell->applyPreferences(); + } else if (key == "win") { + m_shell->preferences()->editingBehavior = WebSettings::EditingBehaviorWin; + m_shell->applyPreferences(); + } else if (key == "unix") { + m_shell->preferences()->editingBehavior = WebSettings::EditingBehaviorUnix; + m_shell->applyPreferences(); + } else + logErrorToConsole("Passed invalid editing behavior. Should be 'mac', 'win', or 'unix'."); +} + +void LayoutTestController::setMockDeviceOrientation(const CppArgumentList& arguments, CppVariant* result) +{ + result->setNull(); + if (arguments.size() < 6 || !arguments[0].isBool() || !arguments[1].isNumber() || !arguments[2].isBool() || !arguments[3].isNumber() || !arguments[4].isBool() || !arguments[5].isNumber()) + return; + + WebDeviceOrientation orientation(arguments[0].toBoolean(), arguments[1].toDouble(), arguments[2].toBoolean(), arguments[3].toDouble(), arguments[4].toBoolean(), arguments[5].toDouble()); + // Note that we only call setOrientation on the main page's mock since this is all that the + // tests require. If necessary, we could get a list of WebViewHosts from the TestShell and + // call setOrientation on each DeviceOrientationClientMock. + m_shell->webViewHost()->deviceOrientationClientMock()->setOrientation(orientation); +} + +void LayoutTestController::setGeolocationPermission(const CppArgumentList& arguments, CppVariant* result) +{ + result->setNull(); + if (arguments.size() < 1 || !arguments[0].isBool()) + return; +#if ENABLE(CLIENT_BASED_GEOLOCATION) + m_shell->webViewHost()->geolocationClientMock()->setPermission(arguments[0].toBoolean()); +#else + WebGeolocationServiceMock::setMockGeolocationPermission(arguments[0].toBoolean()); +#endif +} + +void LayoutTestController::setMockGeolocationPosition(const CppArgumentList& arguments, CppVariant* result) +{ + result->setNull(); + if (arguments.size() < 3 || !arguments[0].isNumber() || !arguments[1].isNumber() || !arguments[2].isNumber()) + return; +#if ENABLE(CLIENT_BASED_GEOLOCATION) + m_shell->webViewHost()->geolocationClientMock()->setPosition(arguments[0].toDouble(), arguments[1].toDouble(), arguments[2].toDouble()); +#else + WebGeolocationServiceMock::setMockGeolocationPosition(arguments[0].toDouble(), arguments[1].toDouble(), arguments[2].toDouble()); +#endif +} + +void LayoutTestController::setMockGeolocationError(const CppArgumentList& arguments, CppVariant* result) +{ + result->setNull(); + if (arguments.size() < 2 || !arguments[0].isNumber() || !arguments[1].isString()) + return; +#if ENABLE(CLIENT_BASED_GEOLOCATION) + m_shell->webViewHost()->geolocationClientMock()->setError(arguments[0].toInt32(), cppVariantToWebString(arguments[1])); +#else + WebGeolocationServiceMock::setMockGeolocationError(arguments[0].toInt32(), cppVariantToWebString(arguments[1])); +#endif +} + +void LayoutTestController::abortModal(const CppArgumentList& arguments, CppVariant* result) +{ + result->setNull(); +} + +void LayoutTestController::addMockSpeechInputResult(const CppArgumentList& arguments, CppVariant* result) +{ + result->setNull(); + if (arguments.size() < 3 || !arguments[0].isString() || !arguments[1].isNumber() || !arguments[2].isString()) + return; + + m_shell->webViewHost()->speechInputControllerMock()->addMockRecognitionResult(cppVariantToWebString(arguments[0]), arguments[1].toDouble(), cppVariantToWebString(arguments[2])); +} + +void LayoutTestController::layerTreeAsText(const CppArgumentList& args, CppVariant* result) +{ + result->set(m_shell->webView()->mainFrame()->layerTreeAsText().utf8()); +} + +void LayoutTestController::markerTextForListItem(const CppArgumentList& args, CppVariant* result) +{ + WebElement element; + if (!WebBindings::getElement(args[0].value.objectValue, &element)) + result->setNull(); + else + result->set(element.document().frame()->markerTextForListItem(element).utf8()); +} + +void LayoutTestController::hasSpellingMarker(const CppArgumentList& arguments, CppVariant* result) +{ + if (arguments.size() < 2 || !arguments[0].isNumber() || !arguments[1].isNumber()) + return; + result->set(m_shell->webView()->mainFrame()->selectionStartHasSpellingMarkerFor(arguments[0].toInt32(), arguments[1].toInt32())); +} diff --git a/Tools/DumpRenderTree/chromium/LayoutTestController.h b/Tools/DumpRenderTree/chromium/LayoutTestController.h new file mode 100644 index 0000000..13d1447 --- /dev/null +++ b/Tools/DumpRenderTree/chromium/LayoutTestController.h @@ -0,0 +1,551 @@ +/* + * 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 "Task.h" +#include "WebString.h" +#include "WebURL.h" +#include <wtf/Deque.h> +#include <wtf/OwnPtr.h> + +namespace WebKit { +class WebGeolocationClientMock; +class WebSpeechInputController; +class WebSpeechInputControllerMock; +class WebSpeechInputListener; +} + +class TestShell; + +class LayoutTestController : public CppBoundClass { +public: + // Builds the property and method lists needed to bind this class to a JS + // object. + LayoutTestController(TestShell*); + + ~LayoutTestController(); + + // 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 an optional argument, whether to dump pixels results or not. + 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 a line of + // user gesture status text for some frame load callbacks. It takes no + // arguments, and ignores any that may be present. + void dumpUserGestureInFrameLoadCallbacks(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 a descriptive + // line for each resource load callback. It takes no arguments, and ignores + // any that may be present. + void dumpResourceLoadCallbacks(const CppArgumentList&, CppVariant*); + + // This function sets a flag that tells the test_shell to dump the MIME type + // for each resource that was loaded. It takes no arguments, and ignores any + // that may be present. + void dumpResourceResponseMIMETypes(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*); + + // 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*); + void queueLoadHTMLString(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*); + + // Changes asynchronous spellchecking flag on the settings. + void setAsynchronousSpellCheckingEnabled(const CppArgumentList&, CppVariant*); + + // Shows DevTools window. + void showWebInspector(const CppArgumentList&, CppVariant*); + void closeWebInspector(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*); + + // Passes this preference through to WebSettings. + void setAuthorAndUserStylesEnabled(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*); + + void setScrollbarPolicy(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 clear certain headers. + void setWillSendRequestClearHeader(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 suspendAnimations(const CppArgumentList&, CppVariant*); + void resumeAnimations(const CppArgumentList&, CppVariant*); + void sampleSVGAnimationForElementAtTime(const CppArgumentList&, CppVariant*); + void disableImageLoading(const CppArgumentList&, CppVariant*); + void setIconDatabaseEnabled(const CppArgumentList&, CppVariant*); + void dumpSelectionRect(const CppArgumentList&, CppVariant*); + + // Grants permission for desktop notifications to an origin + void grantDesktopNotificationPermission(const CppArgumentList&, CppVariant*); + // Simulates a click on a desktop notification. + void simulateDesktopNotificationClick(const CppArgumentList&, CppVariant*); + + void setDomainRelaxationForbiddenForURLScheme(const CppArgumentList&, CppVariant*); + void setDeferMainResourceDataLoad(const CppArgumentList&, CppVariant*); + void setEditingBehavior(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 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 callShouldCloseOnWebView(const CppArgumentList&, CppVariant*); + void setCallCloseOnWebViews(const CppArgumentList&, CppVariant*); + void setPrivateBrowsingEnabled(const CppArgumentList&, CppVariant*); + + void setJavaScriptCanAccessClipboard(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 manage origins' whitelisting. + void addOriginAccessWhitelistEntry(const CppArgumentList&, CppVariant*); + void removeOriginAccessWhitelistEntry(const CppArgumentList&, CppVariant*); + + // Clears all Application Caches. + void clearAllApplicationCaches(const CppArgumentList&, CppVariant*); + // Sets the Application Quota for the localhost origin. + void setApplicationCacheOriginQuota(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 or user style sheet to be injected into new documents. + void addUserScript(const CppArgumentList&, CppVariant*); + void addUserStyleSheet(const CppArgumentList&, CppVariant*); + + // DeviceOrientation related functions + void setMockDeviceOrientation(const CppArgumentList&, CppVariant*); + + // Geolocation related functions. + void setGeolocationPermission(const CppArgumentList&, CppVariant*); + void setMockGeolocationPosition(const CppArgumentList&, CppVariant*); + void setMockGeolocationError(const CppArgumentList&, CppVariant*); + + // Empty stub method to keep parity with object model exposed by global LayoutTestController. + void abortModal(const CppArgumentList&, CppVariant*); + + // Speech input related functions. + void addMockSpeechInputResult(const CppArgumentList&, CppVariant*); + + void layerTreeAsText(const CppArgumentList& args, CppVariant* result); + + void markerTextForListItem(const CppArgumentList&, CppVariant*); + void hasSpellingMarker(const CppArgumentList&, CppVariant*); + +public: + // The following methods are not exposed to JavaScript. + void setWorkQueueFrozen(bool frozen) { m_workQueue.setFrozen(frozen); } + + WebKit::WebSpeechInputController* speechInputController(WebKit::WebSpeechInputListener*); + bool shouldDumpAsText() { return m_dumpAsText; } + bool shouldDumpEditingCallbacks() { return m_dumpEditingCallbacks; } + bool shouldDumpFrameLoadCallbacks() { return m_dumpFrameLoadCallbacks; } + void setShouldDumpFrameLoadCallbacks(bool value) { m_dumpFrameLoadCallbacks = value; } + bool shouldDumpUserGestureInFrameLoadCallbacks() { return m_dumpUserGestureInFrameLoadCallbacks; } + void setShouldDumpUserGestureInFrameLoadCallbacks(bool value) { m_dumpUserGestureInFrameLoadCallbacks = value; } + bool shouldDumpResourceLoadCallbacks() {return m_dumpResourceLoadCallbacks; } + void setShouldDumpResourceResponseMIMETypes(bool value) { m_dumpResourceResponseMIMETypes = value; } + bool shouldDumpResourceResponseMIMETypes() {return m_dumpResourceResponseMIMETypes; } + 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 shouldGeneratePixelResults() { return m_generatePixelResults; } + bool acceptsEditing() { return m_acceptsEditing; } + bool canOpenWindows() { return m_canOpenWindows; } + bool shouldAddFileToPasteboard() { return m_shouldAddFileToPasteboard; } + bool stopProvisionalFrameLoads() { return m_stopProvisionalFrameLoads; } + bool deferMainResourceDataLoad() { return m_deferMainResourceDataLoad; } + + 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; + }; + + TaskList* taskList() { return &m_taskList; } + +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_frozen(false), 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(); } + TaskList* taskList() { return &m_taskList; } + + private: + void processWork(); + class WorkQueueTask: public MethodTask<WorkQueue> { + public: + WorkQueueTask(WorkQueue* object): MethodTask<WorkQueue>(object) {} + virtual void runIfValid() { m_object->processWork(); } + }; + + TaskList m_taskList; + 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); + class NotifyDoneTimedOutTask: public MethodTask<LayoutTestController> { + public: + NotifyDoneTimedOutTask(LayoutTestController* object): MethodTask<LayoutTestController>(object) {} + virtual void runIfValid() { m_object->completeNotifyDone(true); } + }; + + + 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(); + void suspendAnimations(); + void resumeAnimations(); + + // Used for test timeouts. + TaskList m_taskList; + + // 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 line of the user gesture status + // text for some frame load callbacks. + bool m_dumpUserGestureInFrameLoadCallbacks; + + // If true, the test_shell will output a descriptive line for each resource + // load callback. + bool m_dumpResourceLoadCallbacks; + + // If true, the test_shell will output the MIME type for each resource that + // was loaded. + bool m_dumpResourceResponseMIMETypes; + + // 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 test_shell will generate pixel results in dumpAsText mode + bool m_generatePixelResults; + + // 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; + + // If false, all new requests will not defer the main resource data load. + bool m_deferMainResourceDataLoad; + + WorkQueue m_workQueue; + + CppVariant m_globalFlag; + + // Bound variable counting the number of top URLs visited. + CppVariant m_webHistoryItemCount; + + WebKit::WebURL m_userStyleSheetLocation; + + OwnPtr<WebKit::WebSpeechInputControllerMock> m_speechInputControllerMock; +}; + +#endif // LayoutTestController_h diff --git a/Tools/DumpRenderTree/chromium/LayoutTestHelper.mm b/Tools/DumpRenderTree/chromium/LayoutTestHelper.mm new file mode 100644 index 0000000..e34cf5f --- /dev/null +++ b/Tools/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/Tools/DumpRenderTree/chromium/LayoutTestHelperWin.cpp b/Tools/DumpRenderTree/chromium/LayoutTestHelperWin.cpp new file mode 100644 index 0000000..25efdcd --- /dev/null +++ b/Tools/DumpRenderTree/chromium/LayoutTestHelperWin.cpp @@ -0,0 +1,84 @@ +/* + * 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 <signal.h> +#include <stdio.h> +#include <stdlib.h> +#include <windows.h> + +static BOOL fontSmoothingEnabled = FALSE; + +static void saveInitialSettings(void) +{ + ::SystemParametersInfo(SPI_GETFONTSMOOTHING, 0, &fontSmoothingEnabled, 0); +} + +// Technically, all we need to do is disable ClearType. However, +// for some reason, the call to SPI_SETFONTSMOOTHINGTYPE doesn't +// seem to work, so we just disable font smoothing all together +// (which works reliably) +static void installLayoutTestSettings(void) +{ + ::SystemParametersInfo(SPI_SETFONTSMOOTHING, FALSE, 0, 0); +} + +static void restoreInitialSettings(void) +{ + ::SystemParametersInfo(SPI_SETFONTSMOOTHING, static_cast<UINT>(fontSmoothingEnabled), 0, 0); +} + +static void simpleSignalHandler(int signalNumber) +{ + // Try to restore the settings and then go down cleanly + restoreInitialSettings(); + exit(128 + signalNumber); +} + +int main(int, char*[]) +{ + // Hooks the ways we might get told to clean up... + signal(SIGINT, simpleSignalHandler); + signal(SIGTERM, simpleSignalHandler); + + saveInitialSettings(); + + installLayoutTestSettings(); + + // Let the script know we're ready + printf("ready\n"); + fflush(stdout); + + // Wait for any key (or signal) + getchar(); + + restoreInitialSettings(); + + return EXIT_SUCCESS; +} diff --git a/Tools/DumpRenderTree/chromium/MockSpellCheck.cpp b/Tools/DumpRenderTree/chromium/MockSpellCheck.cpp new file mode 100644 index 0000000..7243152 --- /dev/null +++ b/Tools/DumpRenderTree/chromium/MockSpellCheck.cpp @@ -0,0 +1,156 @@ +/* + * 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 "WebString.h" +#include <wtf/ASCIICType.h> +#include <wtf/Assertions.h> +#include <wtf/text/WTFString.h> + +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 WTF::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.) + WTF::String word = stringText.substring(wordOffset, wordLength); + if (!m_misspelledWords.contains(word)) + return true; + + *misspelledOffset = wordOffset; + *misspelledLength = wordLength; + return false; +} + +void MockSpellCheck::fillSuggestionList(const WebString& word, Vector<WebString>* suggestions) +{ + if (word == WebString::fromUTF8("wellcome")) + suggestions->append(WebString::fromUTF8("welcome")); +} + +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", + "wellcome" + }; + + m_misspelledWords.clear(); + for (size_t i = 0; i < arraysize(misspelledWords); ++i) + m_misspelledWords.add(WTF::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/Tools/DumpRenderTree/chromium/MockSpellCheck.h b/Tools/DumpRenderTree/chromium/MockSpellCheck.h new file mode 100644 index 0000000..dcd37f3 --- /dev/null +++ b/Tools/DumpRenderTree/chromium/MockSpellCheck.h @@ -0,0 +1,83 @@ +/* + * 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); + + void fillSuggestionList(const WebKit::WebString& word, Vector<WebKit::WebString>* suggestions); +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<WTF::String, bool> m_misspelledWords; + + // A flag representing whether or not this object is initialized. + bool m_initialized; +}; + +#endif // MockSpellCheck_h diff --git a/Tools/DumpRenderTree/chromium/NotificationPresenter.cpp b/Tools/DumpRenderTree/chromium/NotificationPresenter.cpp new file mode 100644 index 0000000..7e7053b --- /dev/null +++ b/Tools/DumpRenderTree/chromium/NotificationPresenter.cpp @@ -0,0 +1,148 @@ +/* + * 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 "NotificationPresenter.h" + +#include "WebKit.h" +#include "WebKitClient.h" +#include "WebNotification.h" +#include "WebNotificationPermissionCallback.h" +#include "WebSecurityOrigin.h" +#include "WebString.h" +#include "WebURL.h" +#include "googleurl/src/gurl.h" +#include <wtf/text/CString.h> +#include <wtf/text/WTFString.h> + +using namespace WebKit; + +static WebString identifierForNotification(const WebNotification& notification) +{ + if (notification.isHTML()) + return notification.url().spec().utf16(); + return notification.title(); +} + +static void deferredDisplayDispatch(void* context) +{ + WebNotification* notification = static_cast<WebNotification*>(context); + notification->dispatchDisplayEvent(); + delete notification; +} + +void NotificationPresenter::grantPermission(const WebString& origin) +{ + // Make sure it's in the form of an origin. + GURL url(origin); + m_allowedOrigins.add(WTF::String(url.GetOrigin().spec().c_str())); +} + +bool NotificationPresenter::simulateClick(const WebString& title) +{ + WTF::String id(title.data(), title.length()); + if (m_activeNotifications.find(id) == m_activeNotifications.end()) + return false; + + const WebNotification& notification = m_activeNotifications.find(id)->second; + WebNotification eventTarget(notification); + eventTarget.dispatchClickEvent(); + return true; +} + +// The output from all these methods matches what DumpRenderTree produces. +bool NotificationPresenter::show(const WebNotification& notification) +{ + WebString identifier = identifierForNotification(notification); + if (!notification.replaceId().isEmpty()) { + WTF::String replaceId(notification.replaceId().data(), notification.replaceId().length()); + if (m_replacements.find(replaceId) != m_replacements.end()) + printf("REPLACING NOTIFICATION %s\n", + m_replacements.find(replaceId)->second.utf8().data()); + + m_replacements.set(replaceId, WTF::String(identifier.data(), identifier.length())); + } + + if (notification.isHTML()) { + printf("DESKTOP NOTIFICATION: contents at %s\n", + notification.url().spec().data()); + } else { + printf("DESKTOP NOTIFICATION:%s icon %s, title %s, text %s\n", + notification.dir() == "rtl" ? "(RTL)" : "", + notification.iconURL().isEmpty() ? "" : + notification.iconURL().spec().data(), + notification.title().isEmpty() ? "" : + notification.title().utf8().data(), + notification.body().isEmpty() ? "" : + notification.body().utf8().data()); + } + + WTF::String id(identifier.data(), identifier.length()); + m_activeNotifications.set(id, notification); + + webKitClient()->callOnMainThread(deferredDisplayDispatch, new WebNotification(notification)); + return true; +} + +void NotificationPresenter::cancel(const WebNotification& notification) +{ + WebString identifier = identifierForNotification(notification); + printf("DESKTOP NOTIFICATION CLOSED: %s\n", identifier.utf8().data()); + WebNotification eventTarget(notification); + eventTarget.dispatchCloseEvent(false); + + WTF::String id(identifier.data(), identifier.length()); + m_activeNotifications.remove(id); +} + +void NotificationPresenter::objectDestroyed(const WebKit::WebNotification& notification) +{ + WebString identifier = identifierForNotification(notification); + WTF::String id(identifier.data(), identifier.length()); + m_activeNotifications.remove(id); +} + +WebNotificationPresenter::Permission NotificationPresenter::checkPermission(const WebURL& url) +{ + // Check with the layout test controller + WTF::String origin = WTF::String(static_cast<GURL>(url).GetOrigin().spec().c_str()); + bool allowed = m_allowedOrigins.find(origin) != m_allowedOrigins.end(); + return allowed ? WebNotificationPresenter::PermissionAllowed + : WebNotificationPresenter::PermissionDenied; +} + +void NotificationPresenter::requestPermission( + const WebSecurityOrigin& origin, + WebNotificationPermissionCallback* callback) +{ + printf("DESKTOP NOTIFICATION PERMISSION REQUESTED: %s\n", + origin.toString().utf8().data()); + callback->permissionRequestComplete(); +} diff --git a/Tools/DumpRenderTree/chromium/NotificationPresenter.h b/Tools/DumpRenderTree/chromium/NotificationPresenter.h new file mode 100644 index 0000000..689a908 --- /dev/null +++ b/Tools/DumpRenderTree/chromium/NotificationPresenter.h @@ -0,0 +1,77 @@ +/* + * 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 NotificationPresenter_h +#define NotificationPresenter_h + +#include "WebNotification.h" +#include "WebNotificationPresenter.h" +#include <wtf/HashMap.h> +#include <wtf/HashSet.h> +#include <wtf/text/StringHash.h> +#include <wtf/text/WTFString.h> + +class TestShell; + +// A class that implements WebNotificationPresenter for DRT. +class NotificationPresenter : public WebKit::WebNotificationPresenter { +public: + explicit NotificationPresenter(TestShell* shell) : m_shell(shell) {} + + // Called by the LayoutTestController to simulate a user granting permission. + void grantPermission(const WebKit::WebString& origin); + + // Called by the LayoutTestController to simulate a user clicking on a notification. + bool simulateClick(const WebKit::WebString& notificationIdentifier); + + // WebKit::WebNotificationPresenter interface + virtual bool show(const WebKit::WebNotification&); + virtual void cancel(const WebKit::WebNotification&); + virtual void objectDestroyed(const WebKit::WebNotification&); + virtual Permission checkPermission(const WebKit::WebURL&); + virtual void requestPermission(const WebKit::WebSecurityOrigin&, WebKit::WebNotificationPermissionCallback*); + + void reset() { m_allowedOrigins.clear(); } + +private: + // Non-owned pointer. The NotificationPresenter is owned by the test shell. + TestShell* m_shell; + + // Set of allowed origins. + HashSet<WTF::String> m_allowedOrigins; + + // Map of active notifications. + HashMap<WTF::String, WebKit::WebNotification> m_activeNotifications; + + // Map of active replacement IDs to the titles of those notifications + HashMap<WTF::String, WTF::String> m_replacements; +}; + +#endif // NotificationPresenter_h diff --git a/Tools/DumpRenderTree/chromium/PlainTextController.cpp b/Tools/DumpRenderTree/chromium/PlainTextController.cpp new file mode 100644 index 0000000..c8bdabd --- /dev/null +++ b/Tools/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 "WebBindings.h" +#include "WebRange.h" +#include "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/Tools/DumpRenderTree/chromium/PlainTextController.h b/Tools/DumpRenderTree/chromium/PlainTextController.h new file mode 100644 index 0000000..3d3a04c --- /dev/null +++ b/Tools/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/Tools/DumpRenderTree/chromium/Task.cpp b/Tools/DumpRenderTree/chromium/Task.cpp new file mode 100644 index 0000000..007a479 --- /dev/null +++ b/Tools/DumpRenderTree/chromium/Task.cpp @@ -0,0 +1,75 @@ +/* + * 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 "Task.h" + +#include "WebKit.h" +#include "WebKitClient.h" +#include "webkit/support/webkit_support.h" + +WebTask::WebTask(TaskList* list): m_taskList(list) { m_taskList->registerTask(this); } +WebTask::~WebTask() +{ + if (m_taskList) + m_taskList->unregisterTask(this); +} + +void TaskList::unregisterTask(WebTask* task) +{ + size_t index = m_tasks.find(task); + if (index != notFound) + m_tasks.remove(index); +} + +void TaskList::revokeAll() +{ + while (!m_tasks.isEmpty()) + m_tasks[0]->cancel(); +} + +static void invokeTask(void* context) +{ + WebTask* task = static_cast<WebTask*>(context); + task->run(); + delete task; +} + +void postTask(WebTask* task) +{ + WebKit::webKitClient()->callOnMainThread(invokeTask, static_cast<void*>(task)); +} + +void postDelayedTask(WebTask* task, int64_t ms) +{ + webkit_support::PostDelayedTask(invokeTask, static_cast<void*>(task), ms); +} + + diff --git a/Tools/DumpRenderTree/chromium/Task.h b/Tools/DumpRenderTree/chromium/Task.h new file mode 100644 index 0000000..f29dc7d --- /dev/null +++ b/Tools/DumpRenderTree/chromium/Task.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 Task_h +#define Task_h + +#include <wtf/Vector.h> + +class TaskList; + +// WebTask represents a task which can run by postTask() or postDelayedTask(). +// it is named "WebTask", not "Task", to avoid conflist with base/task.h. +class WebTask { +public: + WebTask(TaskList*); + // The main code of this task. + // An implementation of run() should return immediately if cancel() was called. + virtual void run() = 0; + virtual void cancel() = 0; + virtual ~WebTask(); +protected: + TaskList* m_taskList; +}; + +class TaskList { +public: + TaskList() {} + ~TaskList() { revokeAll(); } + void registerTask(WebTask* task) { m_tasks.append(task); } + void unregisterTask(WebTask* task); + void revokeAll(); +private: + Vector<WebTask*> m_tasks; +}; + +// A task containing an object pointer of class T. Is is supposed that +// runifValid() calls a member function of the object pointer. +// Class T must have "TaskList* taskList()". +template<class T> class MethodTask: public WebTask { +public: + MethodTask(T* object): WebTask(object->taskList()), m_object(object) {} + virtual void run() + { + if (m_object) + runIfValid(); + } + virtual void cancel() + { + m_object = 0; + m_taskList->unregisterTask(this); + m_taskList = 0; + } + virtual void runIfValid() = 0; +protected: + T* m_object; +}; + +void postTask(WebTask* task); +void postDelayedTask(WebTask* task, int64_t ms); + +#endif // Task_h diff --git a/Tools/DumpRenderTree/chromium/TestEventPrinter.cpp b/Tools/DumpRenderTree/chromium/TestEventPrinter.cpp new file mode 100644 index 0000000..2130534 --- /dev/null +++ b/Tools/DumpRenderTree/chromium/TestEventPrinter.cpp @@ -0,0 +1,168 @@ +/* + * 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 "TestEventPrinter.h" + +#include <stdio.h> +#include <stdlib.h> +#include <wtf/Assertions.h> + +class DRTPrinter : public TestEventPrinter { +public: + DRTPrinter() {} + void handleTestHeader(const char* url) const; + void handleTimedOut() const; + void handleTextHeader() const; + void handleTextFooter() const; + void handleImage(const char* actualHash, const char* expectedHash, const unsigned char* imageData, size_t imageSize, const char* fileName) const; + void handleImageFooter() const; + void handleTestFooter(bool dumpedAnything) const; +}; + +class TestShellPrinter : public TestEventPrinter { +public: + TestShellPrinter() {} + void handleTestHeader(const char* url) const; + void handleTimedOut() const; + void handleTextHeader() const; + void handleTextFooter() const; + void handleImage(const char* actualHash, const char* expectedHash, const unsigned char* imageData, size_t imageSize, const char* fileName) const; + void handleImageFooter() const; + void handleTestFooter(bool dumpedAnything) const; +}; + +TestEventPrinter* TestEventPrinter::createDRTPrinter() +{ + return new DRTPrinter; +} + +TestEventPrinter* TestEventPrinter::createTestShellPrinter() +{ + return new TestShellPrinter; +} + +// ---------------------------------------------------------------- + +void DRTPrinter::handleTestHeader(const char*) const +{ +} + +void DRTPrinter::handleTimedOut() const +{ + fprintf(stderr, "FAIL: Timed out waiting for notifyDone to be called\n"); + fprintf(stdout, "FAIL: Timed out waiting for notifyDone to be called\n"); +} + +void DRTPrinter::handleTextHeader() const +{ + printf("Content-Type: text/plain\n"); +} + +void DRTPrinter::handleTextFooter() const +{ + printf("#EOF\n"); +} + +void DRTPrinter::handleImage(const char* actualHash, const char* expectedHash, const unsigned char* imageData, size_t imageSize, const char*) const +{ + ASSERT(actualHash); + printf("\nActualHash: %s\n", actualHash); + if (expectedHash && expectedHash[0]) + printf("\nExpectedHash: %s\n", expectedHash); + if (imageData && imageSize) { + printf("Content-Type: image/png\n"); + // Printf formatting for size_t on 32-bit, 64-bit, and on Windows is hard so just cast to an int. + printf("Content-Length: %d\n", static_cast<int>(imageSize)); + if (fwrite(imageData, 1, imageSize, stdout) != imageSize) { + fprintf(stderr, "Short write to stdout.\n"); + exit(1); + } + } +} + +void DRTPrinter::handleImageFooter() const +{ + printf("#EOF\n"); +} + +void DRTPrinter::handleTestFooter(bool) const +{ +} + +// ---------------------------------------------------------------- + +void TestShellPrinter::handleTestHeader(const char* url) const +{ + printf("#URL:%s\n", url); +} + +void TestShellPrinter::handleTimedOut() const +{ + puts("#TEST_TIMED_OUT\n"); +} + +void TestShellPrinter::handleTextHeader() const +{ +} + +void TestShellPrinter::handleTextFooter() const +{ +} + +void TestShellPrinter::handleImage(const char* actualHash, const char*, const unsigned char* imageData, size_t imageSize, const char* fileName) const +{ + ASSERT(actualHash); + if (imageData && imageSize) { + ASSERT(fileName); + FILE* fp = fopen(fileName, "wb"); + if (!fp) { + perror(fileName); + exit(EXIT_FAILURE); + } + if (fwrite(imageData, 1, imageSize, fp) != imageSize) { + perror(fileName); + fclose(fp); + exit(EXIT_FAILURE); + } + fclose(fp); + } + printf("#MD5:%s\n", actualHash); +} + +void TestShellPrinter::handleImageFooter() const +{ +} + +void TestShellPrinter::handleTestFooter(bool dumpedAnything) const +{ + if (dumpedAnything) + printf("#EOF\n"); +} diff --git a/Tools/DumpRenderTree/chromium/TestEventPrinter.h b/Tools/DumpRenderTree/chromium/TestEventPrinter.h new file mode 100644 index 0000000..fdbfd02 --- /dev/null +++ b/Tools/DumpRenderTree/chromium/TestEventPrinter.h @@ -0,0 +1,43 @@ +/* + * 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. + */ + +class TestEventPrinter { +public: + static TestEventPrinter* createDRTPrinter(); + static TestEventPrinter* createTestShellPrinter(); + + virtual void handleTestHeader(const char* url) const = 0; + virtual void handleTimedOut() const = 0; + virtual void handleTextHeader() const = 0; + virtual void handleTextFooter() const = 0; + virtual void handleImage(const char* actualHash, const char* expectedHash, const unsigned char* imageData, size_t imageSize, const char* fileName) const = 0; + virtual void handleImageFooter() const = 0; + virtual void handleTestFooter(bool dumpedAnything) const = 0; +}; diff --git a/Tools/DumpRenderTree/chromium/TestNavigationController.cpp b/Tools/DumpRenderTree/chromium/TestNavigationController.cpp new file mode 100644 index 0000000..9653c07 --- /dev/null +++ b/Tools/DumpRenderTree/chromium/TestNavigationController.cpp @@ -0,0 +1,277 @@ +/* + * 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 + +PassRefPtr<TestNavigationEntry> TestNavigationEntry::create() +{ + return adoptRef(new TestNavigationEntry); +} + +PassRefPtr<TestNavigationEntry> TestNavigationEntry::create( + int pageID, const WebURL& url, const WebString& title, const WebString& targetFrame) +{ + return adoptRef(new TestNavigationEntry(pageID, url, title, targetFrame)); +} + +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.get(); + 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.get()) { + // 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.clear(); + } 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; + } + + updateMaxPageID(); +} + +void TestNavigationController::discardPendingEntry() +{ + m_pendingEntry.clear(); + 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(RefPtr<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]; + } + + if (m_host->navigate(*m_pendingEntry.get(), 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/Tools/DumpRenderTree/chromium/TestNavigationController.h b/Tools/DumpRenderTree/chromium/TestNavigationController.h new file mode 100644 index 0000000..b671489 --- /dev/null +++ b/Tools/DumpRenderTree/chromium/TestNavigationController.h @@ -0,0 +1,211 @@ +/* + * 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 "WebDataSource.h" +#include "WebHistoryItem.h" +#include "WebString.h" +#include "WebURL.h" +#include "webkit/support/webkit_support.h" +#include <string> +#include <wtf/RefCounted.h> +#include <wtf/RefPtr.h> +#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 RefCounted<TestNavigationEntry> { +public: + static PassRefPtr<TestNavigationEntry> create(); + static PassRefPtr<TestNavigationEntry> create( + int pageID, + const WebKit::WebURL&, + const WebKit::WebString& title, + const WebKit::WebString& targetFrame); + + // Virtual to allow test_shell to extend the class. + virtual ~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: + TestNavigationEntry(); + TestNavigationEntry(int pageID, + const WebKit::WebURL&, + const WebKit::WebString& title, + const WebKit::WebString& targetFrame); + + // 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. + // 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. 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<RefPtr<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. + RefPtr<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/Tools/DumpRenderTree/chromium/TestNetscapePlugIn/ForwardingHeaders/WebKit/npapi.h b/Tools/DumpRenderTree/chromium/TestNetscapePlugIn/ForwardingHeaders/WebKit/npapi.h new file mode 100644 index 0000000..9fa3fff --- /dev/null +++ b/Tools/DumpRenderTree/chromium/TestNetscapePlugIn/ForwardingHeaders/WebKit/npapi.h @@ -0,0 +1,9 @@ +#include "bindings/npapi.h" + +// These are defined in WebCore/brdige/npapi.h and we need them on Linux/Win. +#ifndef FALSE +#define FALSE (0) +#endif +#ifndef TRUE +#define TRUE (1) +#endif diff --git a/Tools/DumpRenderTree/chromium/TestNetscapePlugIn/ForwardingHeaders/WebKit/npfunctions.h b/Tools/DumpRenderTree/chromium/TestNetscapePlugIn/ForwardingHeaders/WebKit/npfunctions.h new file mode 100644 index 0000000..59ae666 --- /dev/null +++ b/Tools/DumpRenderTree/chromium/TestNetscapePlugIn/ForwardingHeaders/WebKit/npfunctions.h @@ -0,0 +1,8 @@ +#include "npapi.h" +#include "bindings/npfunctions.h" + +// Non-standard event types can be passed to HandleEvent. +// npapi.h that comes with WebKit.framework adds these events. +#define getFocusEvent (osEvt + 16) +#define loseFocusEvent (osEvt + 17) +#define adjustCursorEvent (osEvt + 18) diff --git a/Tools/DumpRenderTree/chromium/TestNetscapePlugIn/ForwardingHeaders/WebKit/npruntime.h b/Tools/DumpRenderTree/chromium/TestNetscapePlugIn/ForwardingHeaders/WebKit/npruntime.h new file mode 100644 index 0000000..597d4ad --- /dev/null +++ b/Tools/DumpRenderTree/chromium/TestNetscapePlugIn/ForwardingHeaders/WebKit/npruntime.h @@ -0,0 +1 @@ +#include "bindings/npruntime.h" diff --git a/Tools/DumpRenderTree/chromium/TestNetscapePlugIn/Info.plist b/Tools/DumpRenderTree/chromium/TestNetscapePlugIn/Info.plist new file mode 100644 index 0000000..663f058 --- /dev/null +++ b/Tools/DumpRenderTree/chromium/TestNetscapePlugIn/Info.plist @@ -0,0 +1,60 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> +<plist version="1.0"> +<dict> + <key>CFBundleDevelopmentRegion</key> + <string>English</string> + <key>CFBundleExecutable</key> + <string>WebKitTestNetscapePlugIn</string> + <key>CFBundleGetInfoString</key> + <string>420+, Copyright 2006-2009 Apple Inc.</string> + <key>CFBundleIconFile</key> + <string></string> + <key>CFBundleIdentifier</key> + <string>com.apple.testnetscapeplugin</string> + <key>CFBundleInfoDictionaryVersion</key> + <string>6.0</string> + <key>CFBundlePackageType</key> + <string>BRPL</string> + <key>CFBundleShortVersionString</key> + <string>1.0</string> + <key>CFBundleSignature</key> + <string>????</string> + <key>CFBundleVersion</key> + <string>1.0</string> + <key>CFPlugInDynamicRegisterFunction</key> + <string></string> + <key>CFPlugInDynamicRegistration</key> + <string>NO</string> + <key>CFPlugInFactories</key> + <dict> + <key>00000000-0000-0000-0000-000000000000</key> + <string>MyFactoryFunction</string> + </dict> + <key>CFPlugInTypes</key> + <dict> + <key>00000000-0000-0000-0000-000000000000</key> + <array> + <string>00000000-0000-0000-0000-000000000000</string> + </array> + </dict> + <key>CFPlugInUnloadFunction</key> + <string></string> + <key>WebPluginDescription</key> + <string>Simple Netscape plug-in that handles test content for WebKit</string> + <key>WebPluginMIMETypes</key> + <dict> + <key>application/x-webkit-test-netscape</key> + <dict> + <key>WebPluginExtensions</key> + <array> + <string>testnetscape</string> + </array> + <key>WebPluginTypeDescription</key> + <string>test netscape content</string> + </dict> + </dict> + <key>WebPluginName</key> + <string>WebKit Test PlugIn</string> +</dict> +</plist> diff --git a/Tools/DumpRenderTree/chromium/TestShell.cpp b/Tools/DumpRenderTree/chromium/TestShell.cpp new file mode 100644 index 0000000..21f4208 --- /dev/null +++ b/Tools/DumpRenderTree/chromium/TestShell.cpp @@ -0,0 +1,621 @@ +/* + * 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 "DRTDevToolsAgent.h" +#include "DRTDevToolsClient.h" +#include "LayoutTestController.h" +#include "WebDataSource.h" +#include "WebDocument.h" +#include "WebElement.h" +#include "WebFrame.h" +#include "WebHistoryItem.h" +#include "WebKit.h" +#include "WebRuntimeFeatures.h" +#include "WebScriptController.h" +#include "WebSettings.h" +#include "WebSize.h" +#include "WebSpeechInputControllerMock.h" +#include "WebString.h" +#include "WebURLRequest.h" +#include "WebURLResponse.h" +#include "WebView.h" +#include "WebViewHost.h" +#include "skia/ext/bitmap_platform_device.h" +#include "skia/ext/platform_canvas.h" +#include "webkit/support/webkit_support.h" +#include "webkit/support/webkit_support_gfx.h" +#include <algorithm> +#include <cctype> +#include <vector> +#include <wtf/MD5.h> + +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(bool testShellMode) + : m_testIsPending(false) + , m_testIsPreparing(false) + , m_focusedWidget(0) + , m_testShellMode(testShellMode) + , m_devTools(0) + , m_allowExternalPages(false) + , m_acceleratedCompositingEnabled(false) + , m_accelerated2dCanvasEnabled(false) + , m_loadCount(1) + , m_dumpWhenFinished(true) +{ + WebRuntimeFeatures::enableGeolocation(true); + WebRuntimeFeatures::enableIndexedDatabase(true); + WebRuntimeFeatures::enableFileSystem(true); + 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_notificationPresenter.set(new NotificationPresenter(this)); + m_printer.set(m_testShellMode ? TestEventPrinter::createTestShellPrinter() : TestEventPrinter::createDRTPrinter()); + + // 30 second is the same as the value in Mac DRT. + // If we use a value smaller than the timeout value of + // (new-)run-webkit-tests, (new-)run-webkit-tests misunderstands that a + // timed-out DRT process was crashed. + m_timeout = 30 * 1000; + + m_drtDevToolsAgent.set(new DRTDevToolsAgent); + m_webViewHost = createWebView(); + m_webView = m_webViewHost->webView(); + m_drtDevToolsAgent->setWebView(m_webView); +} + +TestShell::~TestShell() +{ + // Note: DevTools are closed together with all the other windows in the + // windows list. + + // Destroy the WebView before its WebViewHost. + m_drtDevToolsAgent->setWebView(0); +} + +void TestShell::createDRTDevToolsClient(DRTDevToolsAgent* agent) +{ + m_drtDevToolsClient.set(new DRTDevToolsClient(agent, m_devTools->webView())); +} + +void TestShell::showDevTools() +{ + if (!m_devTools) { + WebURL url = webkit_support::GetDevToolsPathAsURL(); + if (!url.isValid()) { + ASSERT(false); + return; + } + m_devTools = createNewWindow(url); + ASSERT(m_devTools); + createDRTDevToolsClient(m_drtDevToolsAgent.get()); + } + m_devTools->show(WebKit::WebNavigationPolicyNewWindow); +} + +void TestShell::closeDevTools() +{ + if (m_devTools) { + m_drtDevToolsAgent->reset(); + m_drtDevToolsClient.clear(); + closeWindow(m_devTools); + m_devTools = 0; + } +} + +void TestShell::resetWebSettings(WebView& webView) +{ + m_prefs.reset(); + m_prefs.acceleratedCompositingEnabled = m_acceleratedCompositingEnabled; + m_prefs.accelerated2dCanvasEnabled = m_accelerated2dCanvasEnabled; + m_prefs.applyTo(&webView); +} + +void TestShell::runFileTest(const TestParams& params) +{ + ASSERT(params.testUrl.isValid()); + 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_prefs.developerExtrasEnabled = inspectorTestMode; + applyPreferences(); + + if (testUrl.find("loading/") != string::npos + || testUrl.find("loading\\") != string::npos) + m_layoutTestController->setShouldDumpFrameLoadCallbacks(true); + + if (inspectorTestMode) + showDevTools(); + + if (m_dumpWhenFinished) + m_printer->handleTestHeader(testUrl.c_str()); + 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() +{ + resetWebSettings(*webView()); + m_accessibilityController->reset(); + m_layoutTestController->reset(); + m_eventSender->reset(); + m_webViewHost->reset(); + m_notificationPresenter->reset(); + m_drtDevToolsAgent->reset(); + if (m_drtDevToolsClient) + m_drtDevToolsClient->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; + if (m_dumpWhenFinished) + dump(); + webkit_support::QuitMessageLoop(); +} + +void TestShell::testTimedOut() +{ + m_printer->handleTimedOut(); + 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 = webkit_support::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 shouldGeneratePixelResults = m_layoutTestController->shouldGeneratePixelResults(); + bool dumpedAnything = false; + if (m_params.dumpTree) { + dumpedAnything = true; + m_printer->handleTextHeader(); + // 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(); + if (mimeType == "text/plain") { + shouldDumpAsText = true; + shouldGeneratePixelResults = false; + } + } + 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) + m_printer->handleTextFooter(); + + if (m_params.dumpPixels && shouldGeneratePixelResults) { + // 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); + } + } + + dumpImage(m_webViewHost->canvas()); + } + m_printer->handleImageFooter(); + m_printer->handleTestFooter(dumpedAnything); + fflush(stdout); + fflush(stderr); +} + +void TestShell::dumpImage(skia::PlatformCanvas* canvas) const +{ + 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 match Windows. +#if OS(MAC_OS_X) + bool discardTransparency = false; +#else + bool discardTransparency = true; + device.makeOpaque(0, 0, sourceBitmap.width(), sourceBitmap.height()); +#endif + + // Compute MD5 sum. + MD5 digester; + Vector<uint8_t, 16> digestValue; + digester.addBytes(reinterpret_cast<const uint8_t*>(sourceBitmap.getPixels()), sourceBitmap.getSize()); + digester.checksum(digestValue); + string md5hash; + md5hash.reserve(16 * 2); + for (unsigned i = 0; i < 16; ++i) { + char hex[3]; + // Use "x", not "X". The string must be lowercased. + sprintf(hex, "%02x", digestValue[i]); + md5hash.append(hex); + } + + // Only encode and dump the png if the hashes don't match. Encoding the image + // is really expensive. + if (md5hash.compare(m_params.pixelHash)) { + std::vector<unsigned char> png; + webkit_support::EncodeBGRAPNG( + reinterpret_cast<const unsigned char*>(sourceBitmap.getPixels()), + sourceBitmap.width(), sourceBitmap.height(), + static_cast<int>(sourceBitmap.rowBytes()), discardTransparency, &png); + + m_printer->handleImage(md5hash.c_str(), m_params.pixelHash.c_str(), &png[0], png.size(), m_params.pixelFileName.c_str()); + } else + m_printer->handleImage(md5hash.c_str(), m_params.pixelHash.c_str(), 0, 0, m_params.pixelFileName.c_str()); +} + +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")); +} + +WebViewHost* TestShell::createWebView() +{ + return createNewWindow(WebURL()); +} + +WebViewHost* TestShell::createNewWindow(const WebURL& url) +{ + WebViewHost* host = new WebViewHost(this); + WebView* view = WebView::create(host, m_drtDevToolsAgent.get()); + host->setWebWidget(view); + m_prefs.applyTo(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); + WebWidget* focusedWidget = m_focusedWidget; + if (window->webWidget() == m_focusedWidget) + focusedWidget = 0; + + delete window; + // We set the focused widget after deleting the web view host because it + // can change the focus. + m_focusedWidget = focusedWidget; + if (m_focusedWidget) { + webView()->setIsActive(true); + m_focusedWidget->setFocus(true); + } +} + +void TestShell::closeRemainingWindows() +{ + // Just close devTools window manually because we have custom deinitialization code for it. + closeDevTools(); + + // Iterate through the window list and close everything except the main + // window. 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/Tools/DumpRenderTree/chromium/TestShell.h b/Tools/DumpRenderTree/chromium/TestShell.h new file mode 100644 index 0000000..1da4e17 --- /dev/null +++ b/Tools/DumpRenderTree/chromium/TestShell.h @@ -0,0 +1,221 @@ +/* + * 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 TestShell_h +#define TestShell_h + +#include "AccessibilityController.h" +#include "EventSender.h" +#include "LayoutTestController.h" +#include "NotificationPresenter.h" +#include "PlainTextController.h" +#include "TestEventPrinter.h" +#include "TextInputController.h" +#include "WebPreferences.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 WebDevToolsAgentClient; +class WebFrame; +class WebNotificationPresenter; +class WebView; +class WebURL; +} +namespace skia { +class PlatformCanvas; +} + +class DRTDevToolsAgent; +class DRTDevToolsCallArgs; +class DRTDevToolsClient; + +struct TestParams { + bool dumpTree; + bool dumpPixels; + bool printSeparators; + WebKit::WebURL testUrl; + // Resultant image file name. Required only if the test_shell mode. + std::string pixelFileName; + std::string pixelHash; + + TestParams() + : dumpTree(true) + , dumpPixels(false) + , printSeparators(false) {} +}; + +class TestShell { +public: + TestShell(bool testShellMode); + ~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(); } + EventSender* eventSender() const { return m_eventSender.get(); } + AccessibilityController* accessibilityController() const { return m_accessibilityController.get(); } + NotificationPresenter* notificationPresenter() const { return m_notificationPresenter.get(); } + TestEventPrinter* printer() const { return m_printer.get(); } + + WebPreferences* preferences() { return &m_prefs; } + void applyPreferences() { m_prefs.applyTo(m_webView); } + + 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 shouldDumpUserGestureInFrameLoadCallbacks() const { return (m_testIsPreparing || m_testIsPending) && layoutTestController()->shouldDumpUserGestureInFrameLoadCallbacks(); } + bool shouldDumpResourceLoadCallbacks() const { return (m_testIsPreparing || m_testIsPending) && layoutTestController()->shouldDumpResourceLoadCallbacks(); } + bool shouldDumpResourceResponseMIMETypes() const { return (m_testIsPreparing || m_testIsPending) && layoutTestController()->shouldDumpResourceResponseMIMETypes(); } + 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(); + + bool allowExternalPages() const { return m_allowExternalPages; } + void setAllowExternalPages(bool allowExternalPages) { m_allowExternalPages = allowExternalPages; } + + void setAcceleratedCompositingEnabled(bool enabled) { m_acceleratedCompositingEnabled = enabled; } + void setAccelerated2dCanvasEnabled(bool enabled) { m_accelerated2dCanvasEnabled = enabled; } + +#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. + int layoutTestTimeout() { return m_timeout; } + int layoutTestTimeoutForWatchDog() { return layoutTestTimeout() + 1000; } + void setLayoutTestTimeout(int timeout) { m_timeout = timeout; } + + // Number of times to load each URL. + int loadCount() { return m_loadCount; } + void setLoadCount(int loadCount) { m_loadCount = loadCount; } + + // The JavaScript flags are specified as a vector of strings. Each element of the vector is full flags string + // which can contain multiple flags (e.g. "--xxx --yyy"). With multiple load testing it is possible to specify + // separate sets of flags to each load. + std::string javaScriptFlagsForLoad(size_t load) { return (load < m_javaScriptFlags.size()) ? m_javaScriptFlags[load] : ""; } + void setJavaScriptFlags(Vector<std::string> javaScriptFlags) { m_javaScriptFlags = javaScriptFlags; } + + // Set whether to dump when the loaded page has finished processing. This is used with multiple load + // testing where we only want to have the output from the last load. + void setDumpWhenFinished(bool dumpWhenFinished) { m_dumpWhenFinished = dumpWhenFinished; } + + WebViewHost* createWebView(); + WebViewHost* createNewWindow(const WebKit::WebURL&); + void closeWindow(WebViewHost*); + void closeRemainingWindows(); + int windowCount(); + static void resizeWindowForTest(WebViewHost*, const WebKit::WebURL&); + + void showDevTools(); + void closeDevTools(); + + DRTDevToolsAgent* drtDevToolsAgent() { return m_drtDevToolsAgent.get(); } + DRTDevToolsClient* drtDevToolsClient() { return m_drtDevToolsClient.get(); } + WebViewHost* devToolsWebView() { return m_devTools; } + + static const int virtualWindowBorder = 3; + +private: + void createDRTDevToolsClient(DRTDevToolsAgent*); + + void resetWebSettings(WebKit::WebView&); + void dump(); + std::string dumpAllBackForwardLists(); + void dumpImage(skia::PlatformCanvas*) const; + + bool m_testIsPending; + bool m_testIsPreparing; + bool m_isLoading; + WebKit::WebView* m_webView; + WebKit::WebWidget* m_focusedWidget; + bool m_testShellMode; + WebViewHost* m_webViewHost; + WebViewHost* m_devTools; + OwnPtr<DRTDevToolsAgent> m_drtDevToolsAgent; + OwnPtr<DRTDevToolsClient> m_drtDevToolsClient; + OwnPtr<AccessibilityController> m_accessibilityController; + OwnPtr<EventSender> m_eventSender; + OwnPtr<LayoutTestController> m_layoutTestController; + OwnPtr<PlainTextController> m_plainTextController; + OwnPtr<TextInputController> m_textInputController; + OwnPtr<NotificationPresenter> m_notificationPresenter; + OwnPtr<TestEventPrinter> m_printer; + TestParams m_params; + int m_timeout; // timeout value in millisecond + bool m_allowExternalPages; + bool m_acceleratedCompositingEnabled; + bool m_accelerated2dCanvasEnabled; + WebPreferences m_prefs; + int m_loadCount; + Vector<std::string> m_javaScriptFlags; + bool m_dumpWhenFinished; + + + // 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 +}; + +void platformInit(int*, char***); +void openStartupDialog(); +bool checkLayoutTestSystemDependencies(); + +#endif // TestShell_h diff --git a/Tools/DumpRenderTree/chromium/TestShellGtk.cpp b/Tools/DumpRenderTree/chromium/TestShellGtk.cpp new file mode 100644 index 0000000..1cf7c56 --- /dev/null +++ b/Tools/DumpRenderTree/chromium/TestShellGtk.cpp @@ -0,0 +1,211 @@ +/* + * 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 <fontconfig/fontconfig.h> +#include <gtk/gtk.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); +} + +static void setupFontconfig() +{ + // We wish to make the layout tests reproducable with respect to fonts. Skia + // uses fontconfig to resolve font family names from WebKit into actual font + // files found on the current system. This means that fonts vary based on the + // system and also on the fontconfig configuration. + // + // To avoid this we initialise fontconfig here and install a configuration + // which only knows about a few, select, fonts. + + // We have fontconfig parse a config file from our resources file. This + // sets a number of aliases ("sans"->"Arial" etc), but doesn't include any + // font directories. + FcInit(); + + char drtPath[PATH_MAX + 1]; + int drtPathSize = readlink("/proc/self/exe", drtPath, PATH_MAX); + if (drtPathSize < 0 || drtPathSize > PATH_MAX) { + fputs("Unable to resolve /proc/self/exe.", stderr); + exit(1); + } + drtPath[drtPathSize] = 0; + std::string drtDirPath(drtPath); + size_t lastPathPos = drtDirPath.rfind("/"); + ASSERT(lastPathPos != std::string::npos); + drtDirPath.erase(lastPathPos + 1); + + FcConfig* fontcfg = FcConfigCreate(); + std::string fontconfigPath = drtDirPath + "fonts.conf"; + if (!FcConfigParseAndLoad(fontcfg, reinterpret_cast<const FcChar8*>(fontconfigPath.c_str()), true)) { + fputs("Failed to parse fontconfig config file\n", stderr); + exit(1); + } + + // This is the list of fonts that fontconfig will know about. It + // will try its best to match based only on the fonts here in. The + // paths are where these fonts are found on our Ubuntu boxes. + static const char *const fonts[] = { + "/usr/share/fonts/truetype/kochi/kochi-gothic.ttf", + "/usr/share/fonts/truetype/kochi/kochi-mincho.ttf", + "/usr/share/fonts/truetype/msttcorefonts/Arial.ttf", + "/usr/share/fonts/truetype/msttcorefonts/Arial_Bold.ttf", + "/usr/share/fonts/truetype/msttcorefonts/Arial_Bold_Italic.ttf", + "/usr/share/fonts/truetype/msttcorefonts/Arial_Italic.ttf", + "/usr/share/fonts/truetype/msttcorefonts/Comic_Sans_MS.ttf", + "/usr/share/fonts/truetype/msttcorefonts/Comic_Sans_MS_Bold.ttf", + "/usr/share/fonts/truetype/msttcorefonts/Courier_New.ttf", + "/usr/share/fonts/truetype/msttcorefonts/Courier_New_Bold.ttf", + "/usr/share/fonts/truetype/msttcorefonts/Courier_New_Bold_Italic.ttf", + "/usr/share/fonts/truetype/msttcorefonts/Courier_New_Italic.ttf", + "/usr/share/fonts/truetype/msttcorefonts/Georgia.ttf", + "/usr/share/fonts/truetype/msttcorefonts/Georgia_Bold.ttf", + "/usr/share/fonts/truetype/msttcorefonts/Georgia_Bold_Italic.ttf", + "/usr/share/fonts/truetype/msttcorefonts/Georgia_Italic.ttf", + "/usr/share/fonts/truetype/msttcorefonts/Impact.ttf", + "/usr/share/fonts/truetype/msttcorefonts/Trebuchet_MS.ttf", + "/usr/share/fonts/truetype/msttcorefonts/Trebuchet_MS_Bold.ttf", + "/usr/share/fonts/truetype/msttcorefonts/Trebuchet_MS_Bold_Italic.ttf", + "/usr/share/fonts/truetype/msttcorefonts/Trebuchet_MS_Italic.ttf", + "/usr/share/fonts/truetype/msttcorefonts/Times_New_Roman.ttf", + "/usr/share/fonts/truetype/msttcorefonts/Times_New_Roman_Bold.ttf", + "/usr/share/fonts/truetype/msttcorefonts/Times_New_Roman_Bold_Italic.ttf", + "/usr/share/fonts/truetype/msttcorefonts/Times_New_Roman_Italic.ttf", + "/usr/share/fonts/truetype/msttcorefonts/Verdana.ttf", + "/usr/share/fonts/truetype/msttcorefonts/Verdana_Bold.ttf", + "/usr/share/fonts/truetype/msttcorefonts/Verdana_Bold_Italic.ttf", + "/usr/share/fonts/truetype/msttcorefonts/Verdana_Italic.ttf", + // The DejaVuSans font is used by the css2.1 tests. + "/usr/share/fonts/truetype/ttf-dejavu/DejaVuSans.ttf", + "/usr/share/fonts/truetype/ttf-indic-fonts-core/lohit_ta.ttf", + "/usr/share/fonts/truetype/ttf-indic-fonts-core/MuktiNarrow.ttf", + }; + for (size_t i = 0; i < arraysize(fonts); ++i) { + if (access(fonts[i], R_OK)) { + fprintf(stderr, "You are missing %s. Try installing msttcorefonts. Also see " + "http://code.google.com/p/chromium/wiki/LinuxBuildInstructions", + fonts[i]); + exit(1); + } + if (!FcConfigAppFontAddFile(fontcfg, (FcChar8 *) fonts[i])) { + fprintf(stderr, "Failed to load font %s\n", fonts[i]); + exit(1); + } + } + + // We special case these fonts because they're only needed in a + // few layout tests. + static const char* const optionalFonts[] = { + "/usr/share/fonts/truetype/ttf-lucida/LucidaSansRegular.ttf", + "/usr/share/fonts/truetype/ttf-indic-fonts-core/lohit_pa.ttf", + }; + for (size_t i = 0; i < arraysize(optionalFonts); ++i) { + const char* font = optionalFonts[i]; + + // This font changed paths across Ubuntu releases, so try checking in both locations. + if (!strcmp(font, "/usr/share/fonts/truetype/ttf-indic-fonts-core/lohit_pa.ttf") + && access(font, R_OK) < 0) + font = "/usr/share/fonts/truetype/ttf-punjabi-fonts/lohit_pa.ttf"; + + if (access(font, R_OK) < 0) { + fprintf(stderr, "You are missing %s. Without this, some layout tests may fail. " + "See http://code.google.com/p/chromium/wiki/LinuxBuildInstructionsPrerequisites " + "for more.\n", font); + } else if (!FcConfigAppFontAddFile(fontcfg, (FcChar8 *) font)) { + fprintf(stderr, "Failed to load font %s\n", font); + exit(1); + } + } + + // Also load the layout-test-specific "Ahem" font. + std::string ahemPath = drtDirPath + "AHEM____.TTF"; + if (!FcConfigAppFontAddFile(fontcfg, reinterpret_cast<const FcChar8*>(ahemPath.c_str()))) { + fprintf(stderr, "Failed to load font %s\n", ahemPath.c_str()); + exit(1); + } + + if (!FcConfigSetCurrent(fontcfg)) { + fputs("Failed to set the default font configuration\n", stderr); + exit(1); + } +} + +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); +} + +void platformInit(int* argc, char*** argv) +{ + // FIXME: It's better call gtk_init() only when we run plugin tests. + // See http://groups.google.com/a/chromium.org/group/chromium-dev/browse_thread/thread/633ea167cde196ca# + gtk_init(argc, argv); + + setupFontconfig(); +} + +void openStartupDialog() +{ + GtkWidget* dialog = gtk_message_dialog_new( + 0, GTK_DIALOG_MODAL, GTK_MESSAGE_INFO, GTK_BUTTONS_OK, "Attach to me?"); + gtk_window_set_title(GTK_WINDOW(dialog), "DumpRenderTree"); + gtk_dialog_run(GTK_DIALOG(dialog)); // Runs a nested message loop. + gtk_widget_destroy(dialog); +} + +bool checkLayoutTestSystemDependencies() +{ + return true; +} diff --git a/Tools/DumpRenderTree/chromium/TestShellMac.mm b/Tools/DumpRenderTree/chromium/TestShellMac.mm new file mode 100644 index 0000000..53ede56 --- /dev/null +++ b/Tools/DumpRenderTree/chromium/TestShellMac.mm @@ -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. + */ + +#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]; +} + +void platformInit(int*, char***) +{ +} + +void openStartupDialog() +{ + // FIXME: This code doesn't work. Need NSApplication event loop? + NSAlert* alert = [[[NSAlert alloc] init] autorelease]; + alert.messageText = @"Attach to me?"; + alert.informativeText = @"This would probably be a good time to attach your debugger."; + [alert addButtonWithTitle:@"OK"]; + [alert runModal]; +} + +bool checkLayoutTestSystemDependencies() +{ + return true; +} + diff --git a/Tools/DumpRenderTree/chromium/TestShellWin.cpp b/Tools/DumpRenderTree/chromium/TestShellWin.cpp new file mode 100644 index 0000000..3b3ddd9 --- /dev/null +++ b/Tools/DumpRenderTree/chromium/TestShellWin.cpp @@ -0,0 +1,244 @@ +/* + * 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 "WebThemeEngineDRTWin.h" +#include "webkit/support/webkit_support.h" +#include <fcntl.h> +#include <io.h> +#include <list> +#include <process.h> +#include <shlwapi.h> +#include <string> +#include <sys/stat.h> +#include <windows.h> + +#define SIZEOF_STRUCT_WITH_SPECIFIED_LAST_MEMBER(structName, member) \ + offsetof(structName, member) + \ + (sizeof static_cast<structName*>(0)->member) +#define NONCLIENTMETRICS_SIZE_PRE_VISTA \ + SIZEOF_STRUCT_WITH_SPECIFIED_LAST_MEMBER(NONCLIENTMETRICS, lfMessageFont) + +// Theme engine +static WebThemeEngineDRTWin themeEngine; + +// 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>(shell->layoutTestTimeoutForWatchDog()); + 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("\n#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); +} + +void platformInit(int*, char***) +{ + // Set stdout/stderr binary mode. + _setmode(_fileno(stdout), _O_BINARY); + _setmode(_fileno(stderr), _O_BINARY); + + // Set theme engine. + webkit_support::SetThemeEngine(&themeEngine); + + // Load Ahem font. + // AHEM____.TTF is copied to the directory of DumpRenderTree.exe by WebKit.gyp. + WCHAR path[_MAX_PATH]; + if (!::GetModuleFileName(0, path, _MAX_PATH)) { + fprintf(stderr, "Can't get the module path.\n"); + exit(1); + } + ::PathRemoveFileSpec(path); + wcscat_s(path, _MAX_PATH, L"/AHEM____.TTF"); + struct _stat ahemStat; + if (_wstat(path, &ahemStat) == -1) { + fprintf(stderr, "Can't access: '%S'\n", path); + exit(1); + } + + FILE* fp = _wfopen(path, L"rb"); + if (!fp) { + _wperror(path); + exit(1); + } + size_t size = ahemStat.st_size; + char* fontBuffer = new char[size]; + if (fread(fontBuffer, 1, size, fp) != size) { + fprintf(stderr, "Can't read the font: '%S'\n", path); + fclose(fp); + exit(1); + } + fclose(fp); + DWORD numFonts = 1; + HANDLE fontHandle = ::AddFontMemResourceEx(fontBuffer, size, 0, &numFonts); + delete[] fontBuffer; // OS owns a copy of the buffer. + if (!fontHandle) { + fprintf(stderr, "Failed to register Ahem font: '%S'\n", path); + exit(1); + } + // We don't need to release the font explicitly. +} + +void openStartupDialog() +{ + ::MessageBox(0, L"Attach to me?", L"DumpRenderTree", MB_OK); +} + +bool checkLayoutTestSystemDependencies() +{ + std::list<std::string> errors; + + OSVERSIONINFOEX versionInfo; + ::ZeroMemory(&versionInfo, sizeof(OSVERSIONINFOEX)); + versionInfo.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX); + ::GetVersionEx(reinterpret_cast<OSVERSIONINFO*>(&versionInfo)); + + // Default to XP metrics, override if on Vista or win 7. + int requiredVScrollSize = 17; + int requiredFontSize = -11; // 8 pt + const wchar_t* requiredFont = L"Tahoma"; + bool isVista = false; + bool isWin7 = false; + const DWORD major = versionInfo.dwMajorVersion; + const DWORD minor = versionInfo.dwMinorVersion; + const WORD type = versionInfo.wProductType; + if (major == 6 && minor == 1 && type == VER_NT_WORKSTATION) { + requiredFont = L"Segoe UI"; + requiredFontSize = -12; + isWin7 = true; + } else if (major == 6 && !minor && type == VER_NT_WORKSTATION) { + requiredFont = L"Segoe UI"; + requiredFontSize = -12; // 9 pt + isVista = true; + } else if (!(major == 5 && minor == 1 && type == VER_NT_WORKSTATION)) { + // The above check is for XP, so that means ... + errors.push_back("Unsupported Operating System version " + "(must use XP, Vista, or Windows 7)."); + } + + // This metric will be 17 when font size is "Normal". + // The size of drop-down menus depends on it. + int verticalScrollSize = ::GetSystemMetrics(SM_CXVSCROLL); + if (verticalScrollSize != requiredVScrollSize) + errors.push_back("Must use normal size fonts (96 dpi)."); + + // ClearType must be disabled, because the rendering is unpredictable. + BOOL fontSmoothingEnabled; + ::SystemParametersInfo(SPI_GETFONTSMOOTHING, 0, &fontSmoothingEnabled, 0); + int fontSmoothingType; + ::SystemParametersInfo(SPI_GETFONTSMOOTHINGTYPE, 0, &fontSmoothingType, 0); + if (fontSmoothingEnabled && (fontSmoothingType == FE_FONTSMOOTHINGCLEARTYPE)) + errors.push_back("ClearType must be disabled."); + + // Check that we're using the default system fonts + NONCLIENTMETRICS metrics; + // Checks Vista or later. + metrics.cbSize = major >= 6 ? sizeof(NONCLIENTMETRICS) : NONCLIENTMETRICS_SIZE_PRE_VISTA; + const bool success = !!::SystemParametersInfo(SPI_GETNONCLIENTMETRICS, metrics.cbSize, &metrics, 0); + ASSERT(success); + LOGFONTW* systemFonts[] = + {&metrics.lfStatusFont, &metrics.lfMenuFont, &metrics.lfSmCaptionFont}; + + for (size_t i = 0; i < arraysize(systemFonts); ++i) { + if (systemFonts[i]->lfHeight != requiredFontSize || wcscmp(requiredFont, systemFonts[i]->lfFaceName)) { + if (isVista || isWin7) + errors.push_back("Must use either the Aero or Basic theme."); + else + errors.push_back("Must use the default XP theme (Luna)."); + break; + } + } + + if (!errors.empty()) { + fprintf(stderr, "%s", + "##################################################################\n" + "## Layout test system dependencies check failed.\n" + "##\n"); + for (std::list<std::string>::iterator it = errors.begin(); it != errors.end(); ++it) + fprintf(stderr, "## %s\n", it->c_str()); + fprintf(stderr, "%s", + "##\n" + "##################################################################\n"); + } + return errors.empty(); +} diff --git a/Tools/DumpRenderTree/chromium/TestWebWorker.h b/Tools/DumpRenderTree/chromium/TestWebWorker.h new file mode 100644 index 0000000..89e19af --- /dev/null +++ b/Tools/DumpRenderTree/chromium/TestWebWorker.h @@ -0,0 +1,93 @@ +/* + * 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 "WebMessagePortChannel.h" +#include "WebWorker.h" +#include "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() + { + // This class expects refcounting semantics like those found in + // Chromium's base::RefCounted, so it's OK to call ref() directly. + relaxAdoptionRequirement(); + 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; } + virtual WebKit::WebApplicationCacheHost* createApplicationCacheHost(WebKit::WebApplicationCacheHostClient*) { return 0; } + virtual bool allowDatabase(WebKit::WebFrame*, const WebKit::WebString&, const WebKit::WebString&, unsigned long) { return true; } + +private: + ~TestWebWorker() {} + friend class WTF::RefCounted<TestWebWorker>; +}; + +#endif // TestWebWorker_h diff --git a/Tools/DumpRenderTree/chromium/TextInputController.cpp b/Tools/DumpRenderTree/chromium/TextInputController.cpp new file mode 100644 index 0000000..3603840 --- /dev/null +++ b/Tools/DumpRenderTree/chromium/TextInputController.cpp @@ -0,0 +1,262 @@ +/* + * 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 "WebBindings.h" +#include "WebCompositionUnderline.h" +#include "WebFrame.h" +#include "WebRange.h" +#include "WebString.h" +#include "WebVector.h" +#include "WebView.h" +#include <wtf/StringExtras.h> +#include <string> + +using namespace WebKit; + +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("attributedSubstringFromRange", &TextInputController::attributedSubstringFromRange); + bindMethod("characterIndexForPoint", &TextInputController::characterIndexForPoint); + bindMethod("conversationIdentifier", &TextInputController::conversationIdentifier); + bindMethod("doCommand", &TextInputController::doCommand); + bindMethod("firstRectForCharacterRange", &TextInputController::firstRectForCharacterRange); + bindMethod("hasMarkedText", &TextInputController::hasMarkedText); + bindMethod("insertText", &TextInputController::insertText); + bindMethod("makeAttributedString", &TextInputController::makeAttributedString); + bindMethod("markedRange", &TextInputController::markedRange); + bindMethod("selectedRange", &TextInputController::selectedRange); + bindMethod("setMarkedText", &TextInputController::setMarkedText); + bindMethod("substringFromRange", &TextInputController::substringFromRange); + bindMethod("unmarkText", &TextInputController::unmarkText); + bindMethod("validAttributesForMarkedText", &TextInputController::validAttributesForMarkedText); + bindMethod("setComposition", &TextInputController::setComposition); +} + +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(); + Vector<int> intArray(2); + intArray[0] = range.startOffset(); + intArray[1] = range.endOffset(); + result->set(WebBindings::makeIntArray(intArray)); +} + +void TextInputController::selectedRange(const CppArgumentList&, CppVariant* result) +{ + result->setNull(); + + WebFrame* mainFrame = getMainFrame(); + if (!mainFrame) + return; + + WebRange range = mainFrame->selectionRange(); + Vector<int> intArray(2); + intArray[0] = range.startOffset(); + intArray[1] = range.endOffset(); + result->set(WebBindings::makeIntArray(intArray)); +} + +void TextInputController::firstRectForCharacterRange(const CppArgumentList& arguments, CppVariant* result) +{ + result->setNull(); + + WebFrame* mainFrame = getMainFrame(); + if (!mainFrame) + return; + + if (arguments.size() < 2 || !arguments[0].isNumber() || !arguments[1].isNumber()) + return; + + WebRect rect; + if (!mainFrame->firstRectForCharacterRange(arguments[0].toInt32(), arguments[1].toInt32(), rect)) + return; + + Vector<int> intArray(4); + intArray[0] = rect.x; + intArray[1] = rect.y; + intArray[2] = rect.width; + intArray[3] = rect.height; + result->set(WebBindings::makeIntArray(intArray)); +} + +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(); +} + +void TextInputController::setComposition(const CppArgumentList& arguments, CppVariant* result) +{ + result->setNull(); + + WebView* view = getMainFrame() ? getMainFrame()->view() : 0; + if (!view) + return; + + if (arguments.size() < 1) + return; + + // Sends a keydown event with key code = 0xE5 to emulate input method behavior. + WebKeyboardEvent keyDown; + keyDown.type = WebInputEvent::RawKeyDown; + keyDown.modifiers = 0; + keyDown.windowsKeyCode = 0xE5; // VKEY_PROCESSKEY + keyDown.setKeyIdentifierFromWindowsKeyCode(); + view->handleInputEvent(keyDown); + + WebVector<WebCompositionUnderline> underlines; + WebString text(WebString::fromUTF8(arguments[0].toString())); + view->setComposition(text, underlines, 0, text.length()); +} diff --git a/Tools/DumpRenderTree/chromium/TextInputController.h b/Tools/DumpRenderTree/chromium/TextInputController.h new file mode 100644 index 0000000..3a3907f --- /dev/null +++ b/Tools/DumpRenderTree/chromium/TextInputController.h @@ -0,0 +1,74 @@ +/* + * 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. + */ + +// 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*); + void setComposition(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 diff --git a/Tools/DumpRenderTree/chromium/WebPreferences.cpp b/Tools/DumpRenderTree/chromium/WebPreferences.cpp new file mode 100644 index 0000000..7c274c3 --- /dev/null +++ b/Tools/DumpRenderTree/chromium/WebPreferences.cpp @@ -0,0 +1,166 @@ +/* + * 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 "WebPreferences.h" + +#include "WebView.h" + +using namespace WebKit; + +void WebPreferences::reset() +{ +#if OS(MAC_OS_X) + cursiveFontFamily = WebString::fromUTF8("Apple Chancery"); + fantasyFontFamily = WebString::fromUTF8("Papyrus"); + WebString serif = WebString::fromUTF8("Times"); +#else + // 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. + cursiveFontFamily = WebString::fromUTF8("Comic Sans MS"); + fantasyFontFamily = WebString::fromUTF8("Impact"); + // NOTE: case matters here, this must be 'times new roman', else + // some layout tests fail. + WebString serif = WebString::fromUTF8("times new roman"); +#endif + serifFontFamily = serif; + standardFontFamily = serif; + fixedFontFamily = WebString::fromUTF8("Courier"); + sansSerifFontFamily = WebString::fromUTF8("Helvetica"); + + defaultFontSize = 16; + defaultFixedFontSize = 13; + minimumFontSize = 0; + minimumLogicalFontSize = 9; + + DOMPasteAllowed = true; + XSSAuditorEnabled = false; + allowFileAccessFromFileURLs = true; + authorAndUserStylesEnabled = true; + defaultTextEncodingName = WebString::fromUTF8("ISO-8859-1"); + developerExtrasEnabled = false; + experimentalWebGLEnabled = false; + javaEnabled = false; + javaScriptCanAccessClipboard = true; + javaScriptCanOpenWindowsAutomatically = true; + javaScriptEnabled = true; + loadsImagesAutomatically = true; + localStorageEnabled = true; + offlineWebApplicationCacheEnabled = true; + pluginsEnabled = true; + shrinksStandaloneImagesToFit = false; + textAreasAreResizable = false; + userStyleSheetLocation = WebURL(); + usesPageCache = false; + webSecurityEnabled = true; + caretBrowsingEnabled = false; + + // Allow those layout tests running as local files, i.e. under + // LayoutTests/http/tests/local, to access http server. + allowUniversalAccessFromFileURLs = true; + +#if OS(DARWIN) + editingBehavior = WebSettings::EditingBehaviorMac; +#else + editingBehavior = WebSettings::EditingBehaviorWin; +#endif + + tabsToLinks = false; + hyperlinkAuditingEnabled = false; + acceleratedCompositingEnabled = false; + accelerated2dCanvasEnabled = false; +} + +void WebPreferences::applyTo(WebView* webView) +{ + WebSettings* settings = webView->settings(); + settings->setCursiveFontFamily(cursiveFontFamily); + settings->setFantasyFontFamily(fantasyFontFamily); + settings->setSerifFontFamily(serifFontFamily); + settings->setStandardFontFamily(standardFontFamily); + settings->setFixedFontFamily(fixedFontFamily); + settings->setSansSerifFontFamily(sansSerifFontFamily); + + settings->setDefaultFontSize(defaultFontSize); + settings->setDefaultFixedFontSize(defaultFixedFontSize); + settings->setMinimumFontSize(minimumFontSize); + settings->setMinimumLogicalFontSize(minimumLogicalFontSize); + + settings->setDOMPasteAllowed(DOMPasteAllowed); + settings->setXSSAuditorEnabled(XSSAuditorEnabled); + settings->setAllowFileAccessFromFileURLs(allowFileAccessFromFileURLs); + settings->setAuthorAndUserStylesEnabled(authorAndUserStylesEnabled); + settings->setDefaultTextEncodingName(defaultTextEncodingName); + settings->setDeveloperExtrasEnabled(developerExtrasEnabled); + settings->setExperimentalWebGLEnabled(experimentalWebGLEnabled); + settings->setJavaEnabled(javaEnabled); + settings->setJavaScriptCanAccessClipboard(javaScriptCanAccessClipboard); + settings->setJavaScriptCanOpenWindowsAutomatically(javaScriptCanOpenWindowsAutomatically); + settings->setJavaScriptEnabled(javaScriptEnabled); + settings->setLoadsImagesAutomatically(loadsImagesAutomatically); + settings->setLocalStorageEnabled(localStorageEnabled); + settings->setOfflineWebApplicationCacheEnabled(offlineWebApplicationCacheEnabled); + settings->setPluginsEnabled(pluginsEnabled); + settings->setShrinksStandaloneImagesToFit(shrinksStandaloneImagesToFit); + settings->setTextAreasAreResizable(textAreasAreResizable); + settings->setUserStyleSheetLocation(userStyleSheetLocation); + settings->setUsesPageCache(usesPageCache); + settings->setWebSecurityEnabled(webSecurityEnabled); + settings->setAllowUniversalAccessFromFileURLs(allowUniversalAccessFromFileURLs); + settings->setEditingBehavior(editingBehavior); + settings->setHyperlinkAuditingEnabled(hyperlinkAuditingEnabled); + // LayoutTests were written with Safari Mac in mind which does not allow + // tabbing to links by default. + webView->setTabsToLinks(tabsToLinks); + settings->setCaretBrowsingEnabled(caretBrowsingEnabled); + + // Fixed values. + settings->setShouldPaintCustomScrollbars(true); + settings->setTextDirectionSubmenuInclusionBehaviorNeverIncluded(); + settings->setDownloadableBinaryFontsEnabled(true); + settings->setAllowScriptsToCloseWindows(false); + settings->setNeedsSiteSpecificQuirks(true); + settings->setEditableLinkBehaviorNeverLive(); + settings->setFontRenderingModeNormal(); + settings->setTextDirectionSubmenuInclusionBehaviorNeverIncluded(); + settings->setUsesEncodingDetector(false); + settings->setImagesEnabled(true); + settings->setAcceleratedCompositingEnabled(acceleratedCompositingEnabled); + settings->setAccelerated2dCanvasEnabled(accelerated2dCanvasEnabled); +} + diff --git a/Tools/DumpRenderTree/chromium/WebPreferences.h b/Tools/DumpRenderTree/chromium/WebPreferences.h new file mode 100644 index 0000000..46877c0 --- /dev/null +++ b/Tools/DumpRenderTree/chromium/WebPreferences.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 WebPreferences_h +#define WebPerferences_h + +#include "WebSettings.h" +#include "WebString.h" +#include "WebURL.h" + +namespace WebKit { +class WebView; +} + +struct WebPreferences { + WebKit::WebString cursiveFontFamily; + WebKit::WebString fantasyFontFamily; + WebKit::WebString serifFontFamily; + WebKit::WebString standardFontFamily; + WebKit::WebString fixedFontFamily; + WebKit::WebString sansSerifFontFamily; + + int defaultFontSize; + int defaultFixedFontSize; + int minimumFontSize; + int minimumLogicalFontSize; + + bool DOMPasteAllowed; + bool XSSAuditorEnabled; + bool allowFileAccessFromFileURLs; + bool authorAndUserStylesEnabled; + WebKit::WebString defaultTextEncodingName; + bool developerExtrasEnabled; + bool experimentalWebGLEnabled; + bool javaEnabled; + bool javaScriptCanAccessClipboard; + bool javaScriptCanOpenWindowsAutomatically; + bool javaScriptEnabled; + bool loadsImagesAutomatically; + bool localStorageEnabled; + bool offlineWebApplicationCacheEnabled; + bool pluginsEnabled; + bool shrinksStandaloneImagesToFit; + bool textAreasAreResizable; + WebKit::WebURL userStyleSheetLocation; + bool usesPageCache; + bool webSecurityEnabled; + bool allowUniversalAccessFromFileURLs; + WebKit::WebSettings::EditingBehavior editingBehavior; + bool tabsToLinks; + bool hyperlinkAuditingEnabled; + bool caretBrowsingEnabled; + bool acceleratedCompositingEnabled; + bool accelerated2dCanvasEnabled; + + WebPreferences() { reset(); } + void reset(); + void applyTo(WebKit::WebView*); +}; + +#endif // WebPreferences_h diff --git a/Tools/DumpRenderTree/chromium/WebThemeControlDRTWin.cpp b/Tools/DumpRenderTree/chromium/WebThemeControlDRTWin.cpp new file mode 100755 index 0000000..b1d9fa1 --- /dev/null +++ b/Tools/DumpRenderTree/chromium/WebThemeControlDRTWin.cpp @@ -0,0 +1,523 @@ +/* + * 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 implements a simple generic version of the WebThemeEngine, +// which is used to draw all the native controls on a web page. We use this +// file when running in layout test mode in order to remove any +// platform-specific rendering differences due to themes, colors, etc. +// + +#include "config.h" +#include "WebThemeControlDRTWin.h" + +#include "skia/ext/platform_canvas.h" +#include "skia/ext/skia_utils_win.h" +#include "third_party/skia/include/core/SkPaint.h" +#include "third_party/skia/include/core/SkPath.h" +#include "third_party/skia/include/core/SkRect.h" + +#include <wtf/Assertions.h> + +using namespace std; +using namespace skia; + +static const SkColor edgeColor = SK_ColorBLACK; +static const SkColor readOnlyColor = SkColorSetRGB(0xe9, 0xc2, 0xa6); +static const SkColor fgColor = SK_ColorBLACK; +static const SkColor bgColors[] = { + SK_ColorBLACK, // Unknown + SkColorSetRGB(0xc9, 0xc9, 0xc9), // Disabled + SkColorSetRGB(0xf3, 0xe0, 0xd0), // Readonly + SkColorSetRGB(0x89, 0xc4, 0xff), // Normal + SkColorSetRGB(0x43, 0xf9, 0xff), // Hot + SkColorSetRGB(0x20, 0xf6, 0xcc), // Focused + SkColorSetRGB(0x00, 0xf3, 0xac), // Hover + SkColorSetRGB(0xa9, 0xff, 0x12), // Pressed + SkColorSetRGB(0xcc, 0xcc, 0xcc) // Indeterminate +}; + +static SkIRect validate(const SkIRect& rect, WebThemeControlDRTWin::Type ctype) +{ + switch (ctype) { + case WebThemeControlDRTWin::UncheckedBoxType: + case WebThemeControlDRTWin::CheckedBoxType: + case WebThemeControlDRTWin::UncheckedRadioType: + case WebThemeControlDRTWin::CheckedRadioType: { + SkIRect retval = rect; + + // The maximum width and height is 13. + // Center the square in the passed rectangle. + const int maxControlSize = 13; + int controlSize = min(rect.width(), rect.height()); + controlSize = min(controlSize, maxControlSize); + + retval.fLeft = rect.fLeft + (rect.width() / 2) - (controlSize / 2); + retval.fRight = retval.fLeft + controlSize - 1; + retval.fTop = rect.fTop + (rect.height() / 2) - (controlSize / 2); + retval.fBottom = retval.fTop + controlSize - 1; + + return retval; + } + + default: + return rect; + } +} + +// WebThemeControlDRTWin + +WebThemeControlDRTWin::WebThemeControlDRTWin(PlatformCanvas* canvas, + const SkIRect& irect, + Type ctype, + State cstate) + : m_canvas(canvas) + , m_irect(validate(irect, ctype)) + , m_type(ctype) + , m_state(cstate) + , m_left(m_irect.fLeft) + , m_right(m_irect.fRight) + , m_top(m_irect.fTop) + , m_bottom(m_irect.fBottom) + , m_height(m_irect.height()) + , m_width(m_irect.width()) + , m_edgeColor(edgeColor) + , m_bgColor(bgColors[cstate]) + , m_fgColor(fgColor) +{ +} + +WebThemeControlDRTWin::~WebThemeControlDRTWin() +{ +} + +void WebThemeControlDRTWin::box(const SkIRect& rect, SkColor fillColor) +{ + SkPaint paint; + + paint.setStyle(SkPaint::kFill_Style); + paint.setColor(fillColor); + m_canvas->drawIRect(rect, paint); + + paint.setColor(m_edgeColor); + paint.setStyle(SkPaint::kStroke_Style); + m_canvas->drawIRect(rect, paint); +} + +void WebThemeControlDRTWin::line(int x0, int y0, int x1, int y1, SkColor color) +{ + SkPaint paint; + paint.setColor(color); + m_canvas->drawLine(SkIntToScalar(x0), SkIntToScalar(y0), + SkIntToScalar(x1), SkIntToScalar(y1), + paint); +} + +void WebThemeControlDRTWin::triangle(int x0, int y0, + int x1, int y1, + int x2, int y2, + SkColor color) +{ + SkPath path; + SkPaint paint; + + paint.setColor(color); + paint.setStyle(SkPaint::kFill_Style); + path.incReserve(4); + path.moveTo(SkIntToScalar(x0), SkIntToScalar(y0)); + path.lineTo(SkIntToScalar(x1), SkIntToScalar(y1)); + path.lineTo(SkIntToScalar(x2), SkIntToScalar(y2)); + path.close(); + m_canvas->drawPath(path, paint); + + paint.setColor(m_edgeColor); + paint.setStyle(SkPaint::kStroke_Style); + m_canvas->drawPath(path, paint); +} + +void WebThemeControlDRTWin::roundRect(SkColor color) +{ + SkRect rect; + SkScalar radius = SkIntToScalar(5); + SkPaint paint; + + rect.set(m_irect); + paint.setColor(color); + paint.setStyle(SkPaint::kFill_Style); + m_canvas->drawRoundRect(rect, radius, radius, paint); + + paint.setColor(m_edgeColor); + paint.setStyle(SkPaint::kStroke_Style); + m_canvas->drawRoundRect(rect, radius, radius, paint); +} + +void WebThemeControlDRTWin::oval(SkColor color) +{ + SkRect rect; + SkPaint paint; + + rect.set(m_irect); + paint.setColor(color); + paint.setStyle(SkPaint::kFill_Style); + m_canvas->drawOval(rect, paint); + + paint.setColor(m_edgeColor); + paint.setStyle(SkPaint::kStroke_Style); + m_canvas->drawOval(rect, paint); +} + +void WebThemeControlDRTWin::circle(SkScalar radius, SkColor color) +{ + SkScalar cy = SkIntToScalar(m_top + m_height / 2); + SkScalar cx = SkIntToScalar(m_left + m_width / 2); + SkPaint paint; + + paint.setColor(color); + paint.setStyle(SkPaint::kFill_Style); + m_canvas->drawCircle(cx, cy, radius, paint); + + paint.setColor(m_edgeColor); + paint.setStyle(SkPaint::kStroke_Style); + m_canvas->drawCircle(cx, cy, radius, paint); +} + +void WebThemeControlDRTWin::nestedBoxes(int indentLeft, + int indentTop, + int indentRight, + int indentBottom, + SkColor outerColor, + SkColor innerColor) +{ + SkIRect lirect; + box(m_irect, outerColor); + lirect.set(m_irect.fLeft + indentLeft, + m_irect.fTop + indentTop, + m_irect.fRight - indentRight, + m_irect.fBottom - indentBottom); + box(lirect, innerColor); +} + +void WebThemeControlDRTWin::markState() +{ + // The horizontal lines in a read only control are spaced by this amount. + const int readOnlyLineOffset = 5; + + // The length of a triangle side for the corner marks. + const int triangleSize = 5; + + switch (m_state) { + case UnknownState: + case DisabledState: + case NormalState: + // Don't visually mark these states (color is enough). + break; + case ReadOnlyState: + // Drawing lines across the control. + for (int i = m_top + readOnlyLineOffset; i < m_bottom; i += readOnlyLineOffset) + line(m_left + 1, i, m_right - 1, i, readOnlyColor); + break; + + case HotState: + // Draw a triangle in the upper left corner of the control. + triangle(m_left, m_top, + m_left + triangleSize, m_top, + m_left, m_top + triangleSize, m_edgeColor); + break; + + case HoverState: + // Draw a triangle in the upper right corner of the control. + triangle(m_right, m_top, + m_right, m_top + triangleSize, + m_right - triangleSize, m_top, m_edgeColor); + break; + + case FocusedState: + // Draw a triangle in the bottom right corner of the control. + triangle(m_right, m_bottom, + m_right - triangleSize, m_bottom, + m_right, m_bottom - triangleSize, m_edgeColor); + break; + + case PressedState: + // Draw a triangle in the bottom left corner of the control. + triangle(m_left, m_bottom, + m_left, m_bottom - triangleSize, + m_left + triangleSize, m_bottom, m_edgeColor); + break; + + default: + ASSERT_NOT_REACHED(); + CRASH(); + break; + } +} + +void WebThemeControlDRTWin::draw() +{ + int halfWidth = m_width / 2; + int halfHeight = m_height / 2; + int quarterWidth = m_width / 4; + int quarterHeight = m_height / 4; + + // Indent amounts for the check in a checkbox or radio button. + const int checkIndent = 3; + + // Indent amounts for short and long sides of the scrollbar notches. + const int notchLongOffset = 1; + const int notchShortOffset = 4; + const int noOffset = 0; + + // Indent amounts for the short and long sides of a scroll thumb box. + const int thumbLongIndent = 0; + const int thumbShortIndent = 2; + + // Indents for the crosshatch on a scroll grip. + const int gripLongIndent = 3; + const int gripShortIndent = 5; + + // Indents for the the slider track. + const int sliderIndent = 2; + + m_canvas->beginPlatformPaint(); + + switch (m_type) { + case UnknownType: + ASSERT_NOT_REACHED(); + CRASH(); + break; + + case TextFieldType: + // We render this by hand outside of this function. + ASSERT_NOT_REACHED(); + CRASH(); + break; + + case PushButtonType: + // push buttons render as a rounded rectangle + roundRect(m_bgColor); + break; + + case UncheckedBoxType: + // Unchecked boxes are simply plain boxes. + box(m_irect, m_bgColor); + break; + + case CheckedBoxType: + nestedBoxes(checkIndent, checkIndent, checkIndent, checkIndent, m_bgColor, m_fgColor); + break; + + case IndeterminateCheckboxType: + // Indeterminate checkbox is a box containing '-'. + nestedBoxes(checkIndent, halfHeight, checkIndent, halfHeight, m_bgColor, m_fgColor); + break; + + case UncheckedRadioType: + circle(SkIntToScalar(halfHeight), m_bgColor); + break; + + case CheckedRadioType: + circle(SkIntToScalar(halfHeight), m_bgColor); + circle(SkIntToScalar(halfHeight - checkIndent), m_fgColor); + break; + + case HorizontalScrollTrackBackType: { + // Draw a box with a notch at the left. + int longOffset = halfHeight - notchLongOffset; + int shortOffset = m_width - notchShortOffset; + nestedBoxes(noOffset, longOffset, shortOffset, longOffset, m_bgColor, m_edgeColor); + break; + } + + case HorizontalScrollTrackForwardType: { + // Draw a box with a notch at the right. + int longOffset = halfHeight - notchLongOffset; + int shortOffset = m_width - notchShortOffset; + nestedBoxes(shortOffset, longOffset, noOffset, longOffset, m_bgColor, m_fgColor); + break; + } + + case VerticalScrollTrackBackType: { + // Draw a box with a notch at the top. + int longOffset = halfWidth - notchLongOffset; + int shortOffset = m_height - notchShortOffset; + nestedBoxes(longOffset, noOffset, longOffset, shortOffset, m_bgColor, m_fgColor); + break; + } + + case VerticalScrollTrackForwardType: { + // Draw a box with a notch at the bottom. + int longOffset = halfWidth - notchLongOffset; + int shortOffset = m_height - notchShortOffset; + nestedBoxes(longOffset, shortOffset, longOffset, noOffset, m_bgColor, m_fgColor); + break; + } + + case HorizontalScrollThumbType: + // Draw a narrower box on top of the outside box. + nestedBoxes(thumbLongIndent, thumbShortIndent, thumbLongIndent, thumbShortIndent, m_bgColor, m_bgColor); + break; + + case VerticalScrollThumbType: + // Draw a shorter box on top of the outside box. + nestedBoxes(thumbShortIndent, thumbLongIndent, thumbShortIndent, thumbLongIndent, m_bgColor, m_bgColor); + break; + + case HorizontalSliderThumbType: + // Slider thumbs are ovals. + oval(m_bgColor); + break; + + case HorizontalScrollGripType: { + // Draw a horizontal crosshatch for the grip. + int longOffset = halfWidth - gripLongIndent; + line(m_left + gripLongIndent, m_top + halfHeight, + m_right - gripLongIndent, m_top + halfHeight, m_fgColor); + line(m_left + longOffset, m_top + gripShortIndent, + m_left + longOffset, m_bottom - gripShortIndent, m_fgColor); + line(m_right - longOffset, m_top + gripShortIndent, + m_right - longOffset, m_bottom - gripShortIndent, m_fgColor); + break; + } + + case VerticalScrollGripType: { + // Draw a vertical crosshatch for the grip. + int longOffset = halfHeight - gripLongIndent; + line(m_left + halfWidth, m_top + gripLongIndent, + m_left + halfWidth, m_bottom - gripLongIndent, m_fgColor); + line(m_left + gripShortIndent, m_top + longOffset, + m_right - gripShortIndent, m_top + longOffset, m_fgColor); + line(m_left + gripShortIndent, m_bottom - longOffset, + m_right - gripShortIndent, m_bottom - longOffset, m_fgColor); + break; + } + + case LeftArrowType: + // Draw a left arrow inside a box. + box(m_irect, m_bgColor); + triangle(m_right - quarterWidth, m_top + quarterHeight, + m_right - quarterWidth, m_bottom - quarterHeight, + m_left + quarterWidth, m_top + halfHeight, m_fgColor); + break; + + case RightArrowType: + // Draw a left arrow inside a box. + box(m_irect, m_bgColor); + triangle(m_left + quarterWidth, m_top + quarterHeight, + m_right - quarterWidth, m_top + halfHeight, + m_left + quarterWidth, m_bottom - quarterHeight, m_fgColor); + break; + + case UpArrowType: + // Draw an up arrow inside a box. + box(m_irect, m_bgColor); + triangle(m_left + quarterWidth, m_bottom - quarterHeight, + m_left + halfWidth, m_top + quarterHeight, + m_right - quarterWidth, m_bottom - quarterHeight, m_fgColor); + break; + + case DownArrowType: + // Draw a down arrow inside a box. + box(m_irect, m_bgColor); + triangle(m_left + quarterWidth, m_top + quarterHeight, + m_right - quarterWidth, m_top + quarterHeight, + m_left + halfWidth, m_bottom - quarterHeight, m_fgColor); + break; + + case HorizontalSliderTrackType: { + // Draw a narrow rect for the track plus box hatches on the ends. + SkIRect lirect; + lirect = m_irect; + lirect.inset(noOffset, halfHeight - sliderIndent); + box(lirect, m_bgColor); + line(m_left, m_top, m_left, m_bottom, m_edgeColor); + line(m_right, m_top, m_right, m_bottom, m_edgeColor); + break; + } + + case DropDownButtonType: + // Draw a box with a big down arrow on top. + box(m_irect, m_bgColor); + triangle(m_left + quarterWidth, m_top, + m_right - quarterWidth, m_top, + m_left + halfWidth, m_bottom, m_fgColor); + break; + + default: + ASSERT_NOT_REACHED(); + CRASH(); + break; + } + + markState(); + m_canvas->endPlatformPaint(); +} + +// Because rendering a text field is dependent on input +// parameters the other controls don't have, we render it directly +// rather than trying to overcomplicate draw() further. +void WebThemeControlDRTWin::drawTextField(bool drawEdges, bool fillContentArea, SkColor color) +{ + SkPaint paint; + + m_canvas->beginPlatformPaint(); + if (fillContentArea) { + paint.setColor(color); + paint.setStyle(SkPaint::kFill_Style); + m_canvas->drawIRect(m_irect, paint); + } + if (drawEdges) { + paint.setColor(m_edgeColor); + paint.setStyle(SkPaint::kStroke_Style); + m_canvas->drawIRect(m_irect, paint); + } + + markState(); + m_canvas->endPlatformPaint(); +} + +void WebThemeControlDRTWin::drawProgressBar(const SkIRect& fillRect) +{ + SkPaint paint; + + m_canvas->beginPlatformPaint(); + paint.setColor(m_bgColor); + paint.setStyle(SkPaint::kFill_Style); + m_canvas->drawIRect(m_irect, paint); + + // Emulate clipping + SkIRect tofill; + tofill.intersect(m_irect, fillRect); + paint.setColor(m_fgColor); + paint.setStyle(SkPaint::kFill_Style); + m_canvas->drawIRect(tofill, paint); + + markState(); + m_canvas->endPlatformPaint(); +} + diff --git a/Tools/DumpRenderTree/chromium/WebThemeControlDRTWin.h b/Tools/DumpRenderTree/chromium/WebThemeControlDRTWin.h new file mode 100644 index 0000000..4e22461 --- /dev/null +++ b/Tools/DumpRenderTree/chromium/WebThemeControlDRTWin.h @@ -0,0 +1,203 @@ +/* + * 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. + */ + +// WebThemeControlDRTWin implements the generic rendering of controls +// needed by WebThemeEngineDRTWin. See the comments in that class +// header file for why this class is needed and used. +// +// This class implements a generic set of widgets using Skia. The widgets +// are optimized for testability, not a pleasing appearance. +// + +#ifndef WebThemeControlDRTWin_h +#define WebThemeControlDRTWin_h + +#include "skia/ext/platform_canvas.h" +#include "third_party/skia/include/core/SkColor.h" +#include <wtf/Noncopyable.h> + +// Skia forward declarations +struct SkIRect; + +class WebThemeControlDRTWin : public Noncopyable { +public: + // This list of states mostly mirrors the list in WebCore/platform/ThemeTypes.h + // but is maintained separately since that isn't public and also to minimize + // dependencies. + // Note that the WebKit ThemeTypes seem to imply that a control can be + // in multiple states simultaneously but WebThemeEngine only allows for + // a single state at a time. + // + // Some definitions for the various states: + // Disabled - indicates that a control can't be modified or selected + // (corresponds to HTML 'disabled' attribute) + // ReadOnly - indicates that a control can't be modified but can be + // selected + // Normal - the normal state of control on the page when it isn't + // focused or otherwise active + // Hot - when the mouse is hovering over a part of the control, + // all the other parts are considered "hot" + // Hover - when the mouse is directly over a control (the CSS + // :hover pseudo-class) + // Focused - when the control has the keyboard focus + // Pressed - when the control is being triggered (by a mousedown or + // a key event). + // Indeterminate - when set to indeterminate (only for progress bar) + enum State { + UnknownState = 0, + DisabledState, + ReadOnlyState, + NormalState, + HotState, + HoverState, + FocusedState, + PressedState, + IndeterminateState + }; + + // This list of types mostly mirrors the list in + // WebCore/platform/ThemeTypes.h but is maintained + // separately since that isn't public and also to minimize dependencies. + // + // Note that what the user might think of as a single control can be + // made up of multiple parts. For example, a single scroll bar contains + // six clickable parts - two arrows, the "thumb" indicating the current + // position on the bar, the other two parts of the bar (before and after + // the thumb) and the "gripper" on the thumb itself. + // + enum Type { + UnknownType = 0, + TextFieldType, + PushButtonType, + UncheckedBoxType, + CheckedBoxType, + IndeterminateCheckboxType, + UncheckedRadioType, + CheckedRadioType, + HorizontalScrollTrackBackType, + HorizontalScrollTrackForwardType, + HorizontalScrollThumbType, + HorizontalScrollGripType, + VerticalScrollTrackBackType, + VerticalScrollTrackForwardType, + VerticalScrollThumbType, + VerticalScrollGripType, + LeftArrowType, + RightArrowType, + UpArrowType, + DownArrowType, + HorizontalSliderTrackType, + HorizontalSliderThumbType, + DropDownButtonType, + ProgressBarType + }; + + // canvas is the canvas to draw onto, and rect gives the size of the + // control. ctype and cstate specify the type and state of the control. + WebThemeControlDRTWin(skia::PlatformCanvas* canvas, + const SkIRect& rect, + Type ctype, + State cstate); + ~WebThemeControlDRTWin(); + + // Draws the control. + void draw(); + + // Use this for TextField controls instead, because the logic + // for drawing them is dependent on what WebKit tells us to do. + // If drawEdges is true, draw an edge around the control. If + // fillContentArea is true, fill the content area with the given color. + void drawTextField(bool drawEdges, bool fillContentArea, SkColor color); + + // Use this for drawing ProgressBar controls instead, since we + // need to know the rect to fill inside the bar. + void drawProgressBar(const SkIRect& fillRect); + +private: + // Draws a box of size specified by irect, filled with the given color. + // The box will have a border drawn in the default edge color. + void box(const SkIRect& irect, SkColor color); + + + // Draws a triangle of size specified by the three pairs of coordinates, + // filled with the given color. The box will have an edge drawn in the + // default edge color. + void triangle(int x0, int y0, int x1, int y1, int x2, int y2, SkColor color); + + // Draws a rectangle the size of the control with rounded corners, filled + // with the specified color (and with a border in the default edge color). + void roundRect(SkColor color); + + // Draws an oval the size of the control, filled with the specified color + // and with a border in the default edge color. + void oval(SkColor color); + + // Draws a circle centered in the control with the specified radius, + // filled with the specified color, and with a border draw in the + // default edge color. + void circle(SkScalar radius, SkColor color); + + // Draws a box the size of the control, filled with the outerColor and + // with a border in the default edge color, and then draws another box + // indented on all four sides by the specified amounts, filled with the + // inner color and with a border in the default edge color. + void nestedBoxes(int indentLeft, + int indentTop, + int indentRight, + int indentBottom, + SkColor outerColor, + SkColor innerColor); + + // Draws a line between the two points in the given color. + void line(int x0, int y0, int x1, int y1, SkColor color); + + // Draws a distinctive mark on the control for each state, so that the + // state of the control can be determined without needing to know which + // color is which. + void markState(); + + skia::PlatformCanvas* m_canvas; + const SkIRect m_irect; + const Type m_type; + const State m_state; + const SkColor m_edgeColor; + const SkColor m_bgColor; + const SkColor m_fgColor; + + // The following are convenience accessors for m_irect. + const int m_left; + const int m_right; + const int m_top; + const int m_bottom; + const int m_width; + const int m_height; +}; + +#endif // WebThemeControlDRTWin_h diff --git a/Tools/DumpRenderTree/chromium/WebThemeEngineDRTWin.cpp b/Tools/DumpRenderTree/chromium/WebThemeEngineDRTWin.cpp new file mode 100755 index 0000000..103b295 --- /dev/null +++ b/Tools/DumpRenderTree/chromium/WebThemeEngineDRTWin.cpp @@ -0,0 +1,758 @@ +/* + * 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 "WebThemeEngineDRTWin.h" + +#include "WebRect.h" +#include "WebThemeControlDRTWin.h" +#include "third_party/skia/include/core/SkRect.h" + +// Although all this code is generic, we include these headers +// to pull in the Windows #defines for the parts and states of +// the controls. +#include <vsstyle.h> +#include <windows.h> + +#include <wtf/Assertions.h> + +using namespace WebKit; + +// We define this for clarity, although there really should be a DFCS_NORMAL in winuser.h. +static const int dfcsNormal = 0x0000; + +static SkIRect webRectToSkIRect(const WebRect& webRect) +{ + SkIRect irect; + irect.set(webRect.x, webRect.y, webRect.x + webRect.width, webRect.y + webRect.height); + return irect; +} + +static void drawControl(WebCanvas* canvas, + const WebRect& rect, + WebThemeControlDRTWin::Type ctype, + WebThemeControlDRTWin::State cstate) +{ + WebThemeControlDRTWin control(canvas, webRectToSkIRect(rect), ctype, cstate); + control.draw(); +} + +static void drawTextField(WebCanvas* canvas, + const WebRect& rect, + WebThemeControlDRTWin::Type ctype, + WebThemeControlDRTWin::State cstate, + bool drawEdges, + bool fillContentArea, + WebColor color) +{ + WebThemeControlDRTWin control(canvas, webRectToSkIRect(rect), ctype, cstate); + control.drawTextField(drawEdges, fillContentArea, color); +} + +static void drawProgressBar(WebCanvas* canvas, + WebThemeControlDRTWin::Type ctype, + WebThemeControlDRTWin::State cstate, + const WebRect& barRect, + const WebRect& fillRect) +{ + WebThemeControlDRTWin control(canvas, webRectToSkIRect(barRect), ctype, cstate); + control.drawProgressBar(webRectToSkIRect(fillRect)); +} + +// WebThemeEngineDRTWin + +void WebThemeEngineDRTWin::paintButton(WebCanvas* canvas, + int part, + int state, + int classicState, + const WebRect& rect) +{ + WebThemeControlDRTWin::Type ctype = WebThemeControlDRTWin::UnknownType; + WebThemeControlDRTWin::State cstate = WebThemeControlDRTWin::UnknownState; + + if (part == BP_CHECKBOX) { + switch (state) { + case CBS_UNCHECKEDNORMAL: + ASSERT(classicState == dfcsNormal); + ctype = WebThemeControlDRTWin::UncheckedBoxType; + cstate = WebThemeControlDRTWin::NormalState; + break; + + case CBS_UNCHECKEDHOT: + ASSERT(classicState == (DFCS_BUTTONCHECK | DFCS_HOT)); + ctype = WebThemeControlDRTWin::UncheckedBoxType; + cstate = WebThemeControlDRTWin::HotState; + break; + + case CBS_UNCHECKEDPRESSED: + ASSERT(classicState == (DFCS_BUTTONCHECK | DFCS_PUSHED)); + ctype = WebThemeControlDRTWin::UncheckedBoxType; + cstate = WebThemeControlDRTWin::PressedState; + break; + + case CBS_UNCHECKEDDISABLED: + ASSERT(classicState == (DFCS_BUTTONCHECK | DFCS_INACTIVE)); + ctype = WebThemeControlDRTWin::UncheckedBoxType; + cstate = WebThemeControlDRTWin::DisabledState; + break; + + case CBS_CHECKEDNORMAL: + ASSERT(classicState == (DFCS_BUTTONCHECK | DFCS_CHECKED)); + ctype = WebThemeControlDRTWin::CheckedBoxType; + cstate = WebThemeControlDRTWin::NormalState; + break; + + case CBS_CHECKEDHOT: + ASSERT(classicState == (DFCS_BUTTONCHECK | DFCS_CHECKED | DFCS_HOT)); + ctype = WebThemeControlDRTWin::CheckedBoxType; + cstate = WebThemeControlDRTWin::HotState; + break; + + case CBS_CHECKEDPRESSED: + ASSERT(classicState == (DFCS_BUTTONCHECK | DFCS_CHECKED | DFCS_PUSHED)); + ctype = WebThemeControlDRTWin::CheckedBoxType; + cstate = WebThemeControlDRTWin::PressedState; + break; + + case CBS_CHECKEDDISABLED: + ASSERT(classicState == (DFCS_BUTTONCHECK | DFCS_CHECKED | DFCS_INACTIVE)); + ctype = WebThemeControlDRTWin::CheckedBoxType; + cstate = WebThemeControlDRTWin::DisabledState; + break; + + case CBS_MIXEDNORMAL: + // Classic theme can't represent mixed state checkbox. We assume + // it's equivalent to unchecked. + ASSERT(classicState == DFCS_BUTTONCHECK); + ctype = WebThemeControlDRTWin::IndeterminateCheckboxType; + cstate = WebThemeControlDRTWin::NormalState; + break; + + case CBS_MIXEDHOT: + ASSERT(classicState == (DFCS_BUTTONCHECK | DFCS_HOT)); + ctype = WebThemeControlDRTWin::IndeterminateCheckboxType; + cstate = WebThemeControlDRTWin::HotState; + break; + + case CBS_MIXEDPRESSED: + ASSERT(classicState == (DFCS_BUTTONCHECK | DFCS_PUSHED)); + ctype = WebThemeControlDRTWin::IndeterminateCheckboxType; + cstate = WebThemeControlDRTWin::PressedState; + break; + + case CBS_MIXEDDISABLED: + ASSERT(classicState == (DFCS_BUTTONCHECK | DFCS_INACTIVE)); + ctype = WebThemeControlDRTWin::IndeterminateCheckboxType; + cstate = WebThemeControlDRTWin::DisabledState; + break; + + default: + ASSERT_NOT_REACHED(); + break; + } + } else if (BP_RADIOBUTTON == part) { + switch (state) { + case RBS_UNCHECKEDNORMAL: + ASSERT(classicState == DFCS_BUTTONRADIO); + ctype = WebThemeControlDRTWin::UncheckedRadioType; + cstate = WebThemeControlDRTWin::NormalState; + break; + + case RBS_UNCHECKEDHOT: + ASSERT(classicState == (DFCS_BUTTONRADIO | DFCS_HOT)); + ctype = WebThemeControlDRTWin::UncheckedRadioType; + cstate = WebThemeControlDRTWin::HotState; + break; + + case RBS_UNCHECKEDPRESSED: + ASSERT(classicState == (DFCS_BUTTONRADIO | DFCS_PUSHED)); + ctype = WebThemeControlDRTWin::UncheckedRadioType; + cstate = WebThemeControlDRTWin::PressedState; + break; + + case RBS_UNCHECKEDDISABLED: + ASSERT(classicState == (DFCS_BUTTONRADIO | DFCS_INACTIVE)); + ctype = WebThemeControlDRTWin::UncheckedRadioType; + cstate = WebThemeControlDRTWin::DisabledState; + break; + + case RBS_CHECKEDNORMAL: + ASSERT(classicState == (DFCS_BUTTONRADIO | DFCS_CHECKED)); + ctype = WebThemeControlDRTWin::CheckedRadioType; + cstate = WebThemeControlDRTWin::NormalState; + break; + + case RBS_CHECKEDHOT: + ASSERT(classicState == (DFCS_BUTTONRADIO | DFCS_CHECKED | DFCS_HOT)); + ctype = WebThemeControlDRTWin::CheckedRadioType; + cstate = WebThemeControlDRTWin::HotState; + break; + + case RBS_CHECKEDPRESSED: + ASSERT(classicState == (DFCS_BUTTONRADIO | DFCS_CHECKED | DFCS_PUSHED)); + ctype = WebThemeControlDRTWin::CheckedRadioType; + cstate = WebThemeControlDRTWin::PressedState; + break; + + case RBS_CHECKEDDISABLED: + ASSERT(classicState == (DFCS_BUTTONRADIO | DFCS_CHECKED | DFCS_INACTIVE)); + ctype = WebThemeControlDRTWin::CheckedRadioType; + cstate = WebThemeControlDRTWin::DisabledState; + break; + + default: + ASSERT_NOT_REACHED(); + break; + } + } else if (BP_PUSHBUTTON == part) { + switch (state) { + case PBS_NORMAL: + ASSERT(classicState == DFCS_BUTTONPUSH); + ctype = WebThemeControlDRTWin::PushButtonType; + cstate = WebThemeControlDRTWin::NormalState; + break; + + case PBS_HOT: + ASSERT(classicState == (DFCS_BUTTONPUSH | DFCS_HOT)); + ctype = WebThemeControlDRTWin::PushButtonType; + cstate = WebThemeControlDRTWin::HotState; + break; + + case PBS_PRESSED: + ASSERT(classicState == (DFCS_BUTTONPUSH | DFCS_PUSHED)); + ctype = WebThemeControlDRTWin::PushButtonType; + cstate = WebThemeControlDRTWin::PressedState; + break; + + case PBS_DISABLED: + ASSERT(classicState == (DFCS_BUTTONPUSH | DFCS_INACTIVE)); + ctype = WebThemeControlDRTWin::PushButtonType; + cstate = WebThemeControlDRTWin::DisabledState; + break; + + case PBS_DEFAULTED: + ASSERT(classicState == DFCS_BUTTONPUSH); + ctype = WebThemeControlDRTWin::PushButtonType; + cstate = WebThemeControlDRTWin::FocusedState; + break; + + default: + ASSERT_NOT_REACHED(); + break; + } + } else { + ASSERT_NOT_REACHED(); + } + + drawControl(canvas, rect, ctype, cstate); +} + + +void WebThemeEngineDRTWin::paintMenuList(WebCanvas* canvas, + int part, + int state, + int classicState, + const WebRect& rect) +{ + WebThemeControlDRTWin::Type ctype = WebThemeControlDRTWin::UnknownType; + WebThemeControlDRTWin::State cstate = WebThemeControlDRTWin::UnknownState; + + if (CP_DROPDOWNBUTTON == part) { + ctype = WebThemeControlDRTWin::DropDownButtonType; + switch (state) { + case CBXS_NORMAL: + ASSERT(classicState == DFCS_MENUARROW); + cstate = WebThemeControlDRTWin::NormalState; + break; + + case CBXS_HOT: + ASSERT(classicState == (DFCS_MENUARROW | DFCS_HOT)); + cstate = WebThemeControlDRTWin::HoverState; + break; + + case CBXS_PRESSED: + ASSERT(classicState == (DFCS_MENUARROW | DFCS_PUSHED)); + cstate = WebThemeControlDRTWin::PressedState; + break; + + case CBXS_DISABLED: + ASSERT(classicState == (DFCS_MENUARROW | DFCS_INACTIVE)); + cstate = WebThemeControlDRTWin::DisabledState; + break; + + default: + CRASH(); + break; + } + } else { + CRASH(); + } + + drawControl(canvas, rect, ctype, cstate); +} + +void WebThemeEngineDRTWin::paintScrollbarArrow(WebCanvas* canvas, + int state, + int classicState, + const WebRect& rect) +{ + WebThemeControlDRTWin::Type ctype = WebThemeControlDRTWin::UnknownType; + WebThemeControlDRTWin::State cstate = WebThemeControlDRTWin::UnknownState; + + switch (state) { + case ABS_UPNORMAL: + ASSERT(classicState == DFCS_SCROLLUP); + ctype = WebThemeControlDRTWin::UpArrowType; + cstate = WebThemeControlDRTWin::NormalState; + break; + + case ABS_DOWNNORMAL: + ASSERT(classicState == DFCS_SCROLLDOWN); + ctype = WebThemeControlDRTWin::DownArrowType; + cstate = WebThemeControlDRTWin::NormalState; + break; + + case ABS_LEFTNORMAL: + ASSERT(classicState == DFCS_SCROLLLEFT); + ctype = WebThemeControlDRTWin::LeftArrowType; + cstate = WebThemeControlDRTWin::NormalState; + break; + + case ABS_RIGHTNORMAL: + ASSERT(classicState == DFCS_SCROLLRIGHT); + ctype = WebThemeControlDRTWin::RightArrowType; + cstate = WebThemeControlDRTWin::NormalState; + break; + + case ABS_UPHOT: + ASSERT(classicState == (DFCS_SCROLLUP | DFCS_HOT)); + ctype = WebThemeControlDRTWin::UpArrowType; + cstate = WebThemeControlDRTWin::HotState; + break; + + case ABS_DOWNHOT: + ASSERT(classicState == (DFCS_SCROLLDOWN | DFCS_HOT)); + ctype = WebThemeControlDRTWin::DownArrowType; + cstate = WebThemeControlDRTWin::HotState; + break; + + case ABS_LEFTHOT: + ASSERT(classicState == (DFCS_SCROLLLEFT | DFCS_HOT)); + ctype = WebThemeControlDRTWin::LeftArrowType; + cstate = WebThemeControlDRTWin::HotState; + break; + + case ABS_RIGHTHOT: + ASSERT(classicState == (DFCS_SCROLLRIGHT | DFCS_HOT)); + ctype = WebThemeControlDRTWin::RightArrowType; + cstate = WebThemeControlDRTWin::HotState; + break; + + case ABS_UPHOVER: + ASSERT(classicState == DFCS_SCROLLUP); + ctype = WebThemeControlDRTWin::UpArrowType; + cstate = WebThemeControlDRTWin::HoverState; + break; + + case ABS_DOWNHOVER: + ASSERT(classicState == DFCS_SCROLLDOWN); + ctype = WebThemeControlDRTWin::DownArrowType; + cstate = WebThemeControlDRTWin::HoverState; + break; + + case ABS_LEFTHOVER: + ASSERT(classicState == DFCS_SCROLLLEFT); + ctype = WebThemeControlDRTWin::LeftArrowType; + cstate = WebThemeControlDRTWin::HoverState; + break; + + case ABS_RIGHTHOVER: + ASSERT(classicState == DFCS_SCROLLRIGHT); + ctype = WebThemeControlDRTWin::RightArrowType; + cstate = WebThemeControlDRTWin::HoverState; + break; + + case ABS_UPPRESSED: + ASSERT(classicState == (DFCS_SCROLLUP | DFCS_PUSHED | DFCS_FLAT)); + ctype = WebThemeControlDRTWin::UpArrowType; + cstate = WebThemeControlDRTWin::PressedState; + break; + + case ABS_DOWNPRESSED: + ASSERT(classicState == (DFCS_SCROLLDOWN | DFCS_PUSHED | DFCS_FLAT)); + ctype = WebThemeControlDRTWin::DownArrowType; + cstate = WebThemeControlDRTWin::PressedState; + break; + + case ABS_LEFTPRESSED: + ASSERT(classicState == (DFCS_SCROLLLEFT | DFCS_PUSHED | DFCS_FLAT)); + ctype = WebThemeControlDRTWin::LeftArrowType; + cstate = WebThemeControlDRTWin::PressedState; + break; + + case ABS_RIGHTPRESSED: + ASSERT(classicState == (DFCS_SCROLLRIGHT | DFCS_PUSHED | DFCS_FLAT)); + ctype = WebThemeControlDRTWin::RightArrowType; + cstate = WebThemeControlDRTWin::PressedState; + break; + + case ABS_UPDISABLED: + ASSERT(classicState == (DFCS_SCROLLUP | DFCS_INACTIVE)); + ctype = WebThemeControlDRTWin::UpArrowType; + cstate = WebThemeControlDRTWin::DisabledState; + break; + + case ABS_DOWNDISABLED: + ASSERT(classicState == (DFCS_SCROLLDOWN | DFCS_INACTIVE)); + ctype = WebThemeControlDRTWin::DownArrowType; + cstate = WebThemeControlDRTWin::DisabledState; + break; + + case ABS_LEFTDISABLED: + ASSERT(classicState == (DFCS_SCROLLLEFT | DFCS_INACTIVE)); + ctype = WebThemeControlDRTWin::LeftArrowType; + cstate = WebThemeControlDRTWin::DisabledState; + break; + + case ABS_RIGHTDISABLED: + ASSERT(classicState == (DFCS_SCROLLRIGHT | DFCS_INACTIVE)); + ctype = WebThemeControlDRTWin::RightArrowType; + cstate = WebThemeControlDRTWin::DisabledState; + break; + + default: + ASSERT_NOT_REACHED(); + break; + } + + drawControl(canvas, rect, ctype, cstate); +} + +void WebThemeEngineDRTWin::paintScrollbarThumb(WebCanvas* canvas, + int part, + int state, + int classicState, + const WebRect& rect) +{ + WebThemeControlDRTWin::Type ctype = WebThemeControlDRTWin::UnknownType; + WebThemeControlDRTWin::State cstate = WebThemeControlDRTWin::UnknownState; + + switch (part) { + case SBP_THUMBBTNHORZ: + ctype = WebThemeControlDRTWin::HorizontalScrollThumbType; + break; + + case SBP_THUMBBTNVERT: + ctype = WebThemeControlDRTWin::VerticalScrollThumbType; + break; + + case SBP_GRIPPERHORZ: + ctype = WebThemeControlDRTWin::HorizontalScrollGripType; + break; + + case SBP_GRIPPERVERT: + ctype = WebThemeControlDRTWin::VerticalScrollGripType; + break; + + default: + ASSERT_NOT_REACHED(); + break; + } + + switch (state) { + case SCRBS_NORMAL: + ASSERT(classicState == dfcsNormal); + cstate = WebThemeControlDRTWin::NormalState; + break; + + case SCRBS_HOT: + ASSERT(classicState == DFCS_HOT); + cstate = WebThemeControlDRTWin::HotState; + break; + + case SCRBS_HOVER: + ASSERT(classicState == dfcsNormal); + cstate = WebThemeControlDRTWin::HoverState; + break; + + case SCRBS_PRESSED: + ASSERT(classicState == dfcsNormal); + cstate = WebThemeControlDRTWin::PressedState; + break; + + case SCRBS_DISABLED: + ASSERT_NOT_REACHED(); // This should never happen in practice. + break; + + default: + ASSERT_NOT_REACHED(); + break; + } + + drawControl(canvas, rect, ctype, cstate); +} + +void WebThemeEngineDRTWin::paintScrollbarTrack(WebCanvas* canvas, + int part, + int state, + int classicState, + const WebRect& rect, + const WebRect& alignRect) +{ + WebThemeControlDRTWin::Type ctype = WebThemeControlDRTWin::UnknownType; + WebThemeControlDRTWin::State cstate = WebThemeControlDRTWin::UnknownState; + + switch (part) { + case SBP_UPPERTRACKHORZ: + ctype = WebThemeControlDRTWin::HorizontalScrollTrackBackType; + break; + + case SBP_LOWERTRACKHORZ: + ctype = WebThemeControlDRTWin::HorizontalScrollTrackForwardType; + break; + + case SBP_UPPERTRACKVERT: + ctype = WebThemeControlDRTWin::VerticalScrollTrackBackType; + break; + + case SBP_LOWERTRACKVERT: + ctype = WebThemeControlDRTWin::VerticalScrollTrackForwardType; + break; + + default: + ASSERT_NOT_REACHED(); + break; + } + + switch (state) { + case SCRBS_NORMAL: + ASSERT(classicState == dfcsNormal); + cstate = WebThemeControlDRTWin::NormalState; + break; + + case SCRBS_HOT: + ASSERT_NOT_REACHED(); // This should never happen in practice. + break; + + case SCRBS_HOVER: + ASSERT(classicState == dfcsNormal); + cstate = WebThemeControlDRTWin::HoverState; + break; + + case SCRBS_PRESSED: + ASSERT_NOT_REACHED(); // This should never happen in practice. + break; + + case SCRBS_DISABLED: + ASSERT(classicState == DFCS_INACTIVE); + cstate = WebThemeControlDRTWin::DisabledState; + break; + + default: + CRASH(); + break; + } + + drawControl(canvas, rect, ctype, cstate); +} + +void WebThemeEngineDRTWin::paintSpinButton(WebCanvas* canvas, + int part, + int state, + int classicState, + const WebRect& rect) +{ + WebThemeControlDRTWin::Type ctype = WebThemeControlDRTWin::UnknownType; + WebThemeControlDRTWin::State cstate = WebThemeControlDRTWin::UnknownState; + + if (part == SPNP_UP) { + ctype = WebThemeControlDRTWin::UpArrowType; + switch (state) { + case UPS_NORMAL: + ASSERT(classicState == DFCS_SCROLLUP); + cstate = WebThemeControlDRTWin::NormalState; + break; + case UPS_DISABLED: + ASSERT(classicState == (DFCS_SCROLLUP | DFCS_INACTIVE)); + cstate = WebThemeControlDRTWin::DisabledState; + break; + case UPS_PRESSED: + ASSERT(classicState == (DFCS_SCROLLUP | DFCS_PUSHED)); + cstate = WebThemeControlDRTWin::PressedState; + break; + case UPS_HOT: + ASSERT(classicState == (DFCS_SCROLLUP | DFCS_HOT)); + cstate = WebThemeControlDRTWin::HoverState; + break; + default: + ASSERT_NOT_REACHED(); + } + } else if (part == SPNP_DOWN) { + ctype = WebThemeControlDRTWin::DownArrowType; + switch (state) { + case DNS_NORMAL: + ASSERT(classicState == DFCS_SCROLLDOWN); + cstate = WebThemeControlDRTWin::NormalState; + break; + case DNS_DISABLED: + ASSERT(classicState == (DFCS_SCROLLDOWN | DFCS_INACTIVE)); + cstate = WebThemeControlDRTWin::DisabledState; + break; + case DNS_PRESSED: + ASSERT(classicState == (DFCS_SCROLLDOWN | DFCS_PUSHED)); + cstate = WebThemeControlDRTWin::PressedState; + break; + case DNS_HOT: + ASSERT(classicState == (DFCS_SCROLLDOWN | DFCS_HOT)); + cstate = WebThemeControlDRTWin::HoverState; + break; + default: + ASSERT_NOT_REACHED(); + } + } else + ASSERT_NOT_REACHED(); + drawControl(canvas, rect, ctype, cstate); +} + +void WebThemeEngineDRTWin::paintTextField(WebCanvas* canvas, + int part, + int state, + int classicState, + const WebRect& rect, + WebColor color, + bool fillContentArea, + bool drawEdges) +{ + WebThemeControlDRTWin::Type ctype = WebThemeControlDRTWin::UnknownType; + WebThemeControlDRTWin::State cstate = WebThemeControlDRTWin::UnknownState; + + ASSERT(EP_EDITTEXT == part); + ctype = WebThemeControlDRTWin::TextFieldType; + + switch (state) { + case ETS_NORMAL: + ASSERT(classicState == dfcsNormal); + cstate = WebThemeControlDRTWin::NormalState; + break; + + case ETS_HOT: + ASSERT(classicState == DFCS_HOT); + cstate = WebThemeControlDRTWin::HotState; + break; + + case ETS_DISABLED: + ASSERT(classicState == DFCS_INACTIVE); + cstate = WebThemeControlDRTWin::DisabledState; + break; + + case ETS_SELECTED: + ASSERT(classicState == DFCS_PUSHED); + cstate = WebThemeControlDRTWin::PressedState; + break; + + case ETS_FOCUSED: + ASSERT(classicState == dfcsNormal); + cstate = WebThemeControlDRTWin::FocusedState; + break; + + case ETS_READONLY: + ASSERT(classicState == dfcsNormal); + cstate = WebThemeControlDRTWin::ReadOnlyState; + break; + + default: + ASSERT_NOT_REACHED(); + break; + } + + drawTextField(canvas, rect, ctype, cstate, drawEdges, fillContentArea, color); +} + +void WebThemeEngineDRTWin::paintTrackbar(WebCanvas* canvas, + int part, + int state, + int classicState, + const WebRect& rect) +{ + WebThemeControlDRTWin::Type ctype = WebThemeControlDRTWin::UnknownType; + WebThemeControlDRTWin::State cstate = WebThemeControlDRTWin::UnknownState; + + if (TKP_THUMBBOTTOM == part) { + ctype = WebThemeControlDRTWin::HorizontalSliderThumbType; + switch (state) { + case TUS_NORMAL: + ASSERT(classicState == dfcsNormal); + cstate = WebThemeControlDRTWin::NormalState; + break; + + case TUS_HOT: + ASSERT(classicState == DFCS_HOT); + cstate = WebThemeControlDRTWin::HotState; + break; + + case TUS_DISABLED: + ASSERT(classicState == DFCS_INACTIVE); + cstate = WebThemeControlDRTWin::DisabledState; + break; + + case TUS_PRESSED: + ASSERT(classicState == DFCS_PUSHED); + cstate = WebThemeControlDRTWin::PressedState; + break; + + default: + ASSERT_NOT_REACHED(); + break; + } + } else if (TKP_TRACK == part) { + ctype = WebThemeControlDRTWin::HorizontalSliderTrackType; + ASSERT(part == TUS_NORMAL); + ASSERT(classicState == dfcsNormal); + cstate = WebThemeControlDRTWin::NormalState; + } else { + ASSERT_NOT_REACHED(); + } + + drawControl(canvas, rect, ctype, cstate); +} + + +void WebThemeEngineDRTWin::paintProgressBar(WebKit::WebCanvas* canvas, + const WebKit::WebRect& barRect, + const WebKit::WebRect& valueRect, + bool determinate, + double) +{ + WebThemeControlDRTWin::Type ctype = WebThemeControlDRTWin::ProgressBarType; + WebThemeControlDRTWin::State cstate = determinate ? WebThemeControlDRTWin::NormalState + : WebThemeControlDRTWin::IndeterminateState; + drawProgressBar(canvas, ctype, cstate, barRect, valueRect); +} + diff --git a/Tools/DumpRenderTree/chromium/WebThemeEngineDRTWin.h b/Tools/DumpRenderTree/chromium/WebThemeEngineDRTWin.h new file mode 100644 index 0000000..2e15cf8 --- /dev/null +++ b/Tools/DumpRenderTree/chromium/WebThemeEngineDRTWin.h @@ -0,0 +1,97 @@ +/* + * 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 implements the WebThemeEngine API used by the Windows version of +// Chromium to render native form controls like checkboxes, radio buttons, +// and scroll bars. +// The normal implementation (native_theme) renders the controls using either +// the UXTheme theming engine present in XP, Vista, and Win 7, or the "classic" +// theme used if that theme is selected in the Desktop settings. +// Unfortunately, both of these themes render controls differently on the +// different versions of Windows. +// +// In order to ensure maximum consistency of baselines across the different +// Windows versions, we provide a simple implementation for DRT here +// instead. These controls are actually platform-independent (they're rendered +// using Skia) and could be used on Linux and the Mac as well, should we +// choose to do so at some point. +// + +#ifndef WebThemeEngineDRTWin_h +#define WebThemeEngineDRTWin_h + +#include "win/WebThemeEngine.h" +#include <wtf/Noncopyable.h> + +class WebThemeEngineDRTWin : public WebKit::WebThemeEngine, public Noncopyable { +public: + WebThemeEngineDRTWin() {} + + // WebThemeEngine methods: + virtual void paintButton( + WebKit::WebCanvas*, int part, int state, int classicState, + const WebKit::WebRect&); + + virtual void paintMenuList( + WebKit::WebCanvas*, int part, int state, int classicState, + const WebKit::WebRect&); + + virtual void paintScrollbarArrow( + WebKit::WebCanvas*, int state, int classicState, + const WebKit::WebRect&); + + virtual void paintScrollbarThumb( + WebKit::WebCanvas*, int part, int state, int classicState, + const WebKit::WebRect&); + + virtual void paintScrollbarTrack( + WebKit::WebCanvas*, int part, int state, int classicState, + const WebKit::WebRect&, const WebKit::WebRect& alignRect); + + virtual void paintSpinButton( + WebKit::WebCanvas*, int part, int state, int classicState, + const WebKit::WebRect&); + + virtual void paintTextField( + WebKit::WebCanvas*, int part, int state, int classicState, + const WebKit::WebRect&, WebKit::WebColor, bool fillContentArea, + bool drawEdges); + + virtual void paintTrackbar( + WebKit::WebCanvas*, int part, int state, int classicState, + const WebKit::WebRect&); + + virtual void paintProgressBar( + WebKit::WebCanvas*, const WebKit::WebRect& barRect, + const WebKit::WebRect& valueRect, + bool determinate, double time); +}; + +#endif // WebThemeEngineDRTWin_h diff --git a/Tools/DumpRenderTree/chromium/WebViewHost.cpp b/Tools/DumpRenderTree/chromium/WebViewHost.cpp new file mode 100644 index 0000000..e3c8c28 --- /dev/null +++ b/Tools/DumpRenderTree/chromium/WebViewHost.cpp @@ -0,0 +1,1519 @@ +/* + * 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 "WebCString.h" +#include "WebConsoleMessage.h" +#include "WebContextMenuData.h" +#include "WebDataSource.h" +#include "WebDeviceOrientationClientMock.h" +#include "WebDragData.h" +#include "WebElement.h" +#include "WebFrame.h" +#if ENABLE(CLIENT_BASED_GEOLOCATION) +#include "WebGeolocationClientMock.h" +#else +#include "WebGeolocationServiceMock.h" +#endif +#include "WebHistoryItem.h" +#include "WebNode.h" +#include "WebRange.h" +#include "WebRect.h" +#include "WebScreenInfo.h" +#include "WebSize.h" +#include "WebSpeechInputControllerMock.h" +#include "WebStorageNamespace.h" +#include "WebURLRequest.h" +#include "WebURLResponse.h" +#include "WebView.h" +#include "WebWindowFeatures.h" +#include "skia/ext/platform_canvas.h" +#include "webkit/support/webkit_support.h" +#include <wtf/Assertions.h> +#include <wtf/PassOwnPtr.h> + +using namespace WebCore; +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 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*, const WebWindowFeatures&, const WebString&) +{ + if (!layoutTestController()->canOpenWindows()) + return 0; + return m_shell->createWebView()->webView(); +} + +WebWidget* WebViewHost::createPopupMenu(WebPopupType) +{ + return 0; +} + +WebWidget* WebViewHost::createPopupMenu(const WebPopupMenuInfo&) +{ + return 0; +} + +WebStorageNamespace* WebViewHost::createSessionStorageNamespace(unsigned quota) +{ + return WebKit::WebStorageNamespace::createSessionStorageNamespace(quota); +} + +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& contextMenuData) +{ + m_lastContextMenuData = adoptPtr(new WebContextMenuData(contextMenuData)); +} + +void WebViewHost::clearContextMenuData() +{ + m_lastContextMenuData.clear(); +} + +WebContextMenuData* WebViewHost::lastContextMenuData() const +{ + return m_lastContextMenuData.get(); +} + +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 WebDragData& data, WebDragOperationsMask mask, const WebImage&, const WebPoint&) +{ + 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. + m_shell->eventSender()->doDragDrop(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::postAccessibilityNotification(const WebAccessibilityObject& obj, WebAccessibilityNotification notification) +{ + if (notification == WebAccessibilityNotificationFocusedUIElementChanged) + m_shell->accessibilityController()->setFocusedElement(obj); + + if (m_shell->accessibilityController()->shouldDumpAccessibilityNotifications()) { + printf("AccessibilityNotification - "); + + switch (notification) { + case WebAccessibilityNotificationActiveDescendantChanged: + printf("ActiveDescendantChanged"); + break; + case WebAccessibilityNotificationCheckedStateChanged: + printf("CheckedStateChanged"); + break; + case WebAccessibilityNotificationChildrenChanged: + printf("ChildrenChanged"); + break; + case WebAccessibilityNotificationFocusedUIElementChanged: + printf("FocusedUIElementChanged"); + break; + case WebAccessibilityNotificationLayoutComplete: + printf("LayoutComplete"); + break; + case WebAccessibilityNotificationLoadComplete: + printf("LoadComplete"); + break; + case WebAccessibilityNotificationSelectedChildrenChanged: + printf("SelectedChildrenChanged"); + break; + case WebAccessibilityNotificationSelectedTextChanged: + printf("SelectedTextChanged"); + break; + case WebAccessibilityNotificationValueChanged: + printf("ValueChanged"); + break; + case WebAccessibilityNotificationScrolledToAnchor: + printf("ScrolledToAnchor"); + break; + case WebAccessibilityNotificationLiveRegionChanged: + printf("LiveRegionChanged"); + break; + case WebAccessibilityNotificationMenuListValueChanged: + printf("MenuListValueChanged"); + break; + case WebAccessibilityNotificationRowCountChanged: + printf("RowCountChanged"); + break; + case WebAccessibilityNotificationRowCollapsed: + printf("RowCollapsed"); + break; + case WebAccessibilityNotificationRowExpanded: + printf("RowExpanded"); + break; + default: + break; + } + + WebKit::WebNode node = obj.node(); + if (!node.isNull() && node.isElementNode()) { + WebKit::WebElement element = node.to<WebKit::WebElement>(); + if (element.hasAttribute("id")) + printf(" - id:%s", element.getAttribute("id").utf8().data()); + } + + printf("\n"); + } +} + +WebNotificationPresenter* WebViewHost::notificationPresenter() +{ + return m_shell->notificationPresenter(); +} + +#if ENABLE(CLIENT_BASED_GEOLOCATION) +WebKit::WebGeolocationClient* WebViewHost::geolocationClient() +{ + return geolocationClientMock(); +} + +WebKit::WebGeolocationClientMock* WebViewHost::geolocationClientMock() +{ + if (!m_geolocationClientMock) + m_geolocationClientMock.set(WebGeolocationClientMock::create()); + return m_geolocationClientMock.get(); +} +#else +WebKit::WebGeolocationService* WebViewHost::geolocationService() +{ + if (!m_geolocationServiceMock) + m_geolocationServiceMock.set(WebGeolocationServiceMock::createWebGeolocationServiceMock()); + return m_geolocationServiceMock.get(); +} +#endif + +WebSpeechInputController* WebViewHost::speechInputController(WebKit::WebSpeechInputListener* listener) +{ + if (!m_speechInputControllerMock) + m_speechInputControllerMock.set(WebSpeechInputControllerMock::create(listener)); + return m_speechInputControllerMock.get(); +} + +WebDeviceOrientationClientMock* WebViewHost::deviceOrientationClientMock() +{ + if (!m_deviceOrientationClientMock.get()) + m_deviceOrientationClientMock.set(WebDeviceOrientationClientMock::create()); + return m_deviceOrientationClientMock.get(); +} + +MockSpellCheck* WebViewHost::mockSpellCheck() +{ + return &m_spellcheck; +} + +WebDeviceOrientationClient* WebViewHost::deviceOrientationClient() +{ + return deviceOrientationClientMock(); +} + +// WebWidgetClient ----------------------------------------------------------- + +void WebViewHost::didInvalidateRect(const WebRect& rect) +{ + 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::scheduleComposite() +{ + WebSize widgetSize = webWidget()->size(); + WebRect clientRect(0, 0, widgetSize.width, widgetSize.height); + didInvalidateRect(clientRect); +} + +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::closeWidget() +{ + m_hasWindow = false; + m_shell->closeWindow(this); + // No more code here, we should be deleted at this point. +} + +static void invokeCloseWidget(void* context) +{ + WebViewHost* wvh = static_cast<WebViewHost*>(context); + wvh->closeWidget(); +} + +void WebViewHost::closeWidgetSoon() +{ + webkit_support::PostDelayedTask(invokeCloseWidget, static_cast<void*>(this), 0); +} + +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() +{ + bool oldState = webkit_support::MessageLoopNestableTasksAllowed(); + webkit_support::MessageLoopSetNestableTasksAllowed(true); + m_inModalLoop = true; + webkit_support::RunMessageLoop(); + webkit_support::MessageLoopSetNestableTasksAllowed(oldState); +} + +// 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); +} + +WebApplicationCacheHost* WebViewHost::createApplicationCacheHost(WebFrame* frame, WebApplicationCacheHostClient* client) +{ + return webkit_support::CreateApplicationCacheHost(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) +{ + return webkit_support::CreateCancelledError(request); +} + +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.leakPtr()); + if (!layoutTestController()->deferMainResourceDataLoad()) + ds->setDeferMainResourceDataLoad(false); +} + +void WebViewHost::didStartProvisionalLoad(WebFrame* frame) +{ + if (m_shell->shouldDumpUserGestureInFrameLoadCallbacks()) + printFrameUserGestureStatus(frame, " - in didStartProvisionalLoadForFrame\n"); + 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::didNavigateWithinPage(WebFrame* frame, bool isNewNavigation) +{ + frame->dataSource()->setExtraData(m_pendingExtraData.leakPtr()); + + updateForCommittedLoad(frame, isNewNavigation); +} + +void WebViewHost::didChangeLocationWithinPage(WebFrame* frame) +{ + if (m_shell->shouldDumpFrameLoadCallbacks()) { + printFrameDescription(frame); + fputs(" - didChangeLocationWithinPageForFrame\n", stdout); + } +} + +void WebViewHost::assignIdentifierToRequest(WebFrame*, unsigned identifier, const WebURLRequest& request) +{ + if (!m_shell->shouldDumpResourceLoadCallbacks()) + return; + ASSERT(!m_resourceIdentifierMap.contains(identifier)); + m_resourceIdentifierMap.set(identifier, descriptionSuitableForTestResult(request.url().spec())); +} + +void WebViewHost::removeIdentifierForRequest(unsigned identifier) +{ + m_resourceIdentifierMap.remove(identifier); +} + +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 ", + 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" + && !m_shell->allowExternalPages()) { + 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; + } + + HashSet<String>::const_iterator end = m_clearHeaders.end(); + for (HashSet<String>::const_iterator header = m_clearHeaders.begin(); header != end; ++header) + request.clearHTTPHeaderField(WebString(header->characters(), header->length())); + + // 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()) { + printResourceDescription(identifier); + fputs(" - didReceiveResponse ", stdout); + printResponseDescription(response); + fputs("\n", stdout); + } + if (m_shell->shouldDumpResourceResponseMIMETypes()) { + GURL url = response.url(); + WebString mimeType = response.mimeType(); + printf("%s has MIME type %s\n", + url.ExtractFileName().c_str(), + // Simulate NSURLResponse's mapping of empty/unknown MIME types to application/octet-stream + mimeType.isEmpty() ? "application/octet-stream" : mimeType.utf8().data()); + } +} + +void WebViewHost::didFinishResourceLoad(WebFrame*, unsigned identifier) +{ + if (m_shell->shouldDumpResourceLoadCallbacks()) { + printResourceDescription(identifier); + fputs(" - didFinishLoading\n", stdout); + } + removeIdentifierForRequest(identifier); +} + +void WebViewHost::didFailResourceLoad(WebFrame*, unsigned identifier, const WebURLError& error) +{ + if (m_shell->shouldDumpResourceLoadCallbacks()) { + printResourceDescription(identifier); + fputs(" - didFailLoadingWithError: ", stdout); + fputs(webkit_support::MakeURLErrorDescription(error).c_str(), stdout); + fputs("\n", stdout); + } + removeIdentifierForRequest(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; +} + +void WebViewHost::openFileSystem(WebFrame* frame, WebFileSystem::Type type, long long size, bool create, WebFileSystemCallbacks* callbacks) +{ + webkit_support::OpenFileSystem(frame, type, size, create, callbacks); +} + +// Public functions ----------------------------------------------------------- + +WebViewHost::WebViewHost(TestShell* shell) + : m_shell(shell) + , m_webWidget(0) +{ + reset(); +} + +WebViewHost::~WebViewHost() +{ + // DevTools frontend page is supposed to be navigated only once and + // loading another URL in that Page is an error. + if (m_shell->devToolsWebView() != this) { + // Navigate to an empty page to fire all the destruction logic for the + // current page. + loadURLForFrame(GURL("about:blank"), WebString()); + } + + // Call GC twice to clean up garbage. + m_shell->callJSGC(); + m_shell->callJSGC(); + + webWidget()->close(); + + if (m_inModalLoop) + webkit_support::QuitMessageLoop(); +} + +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() +{ + m_policyDelegateEnabled = false; + m_policyDelegateIsPermissive = false; + m_policyDelegateShouldNotifyDone = false; + m_topLoadingFrame = 0; + m_pageId = -1; + m_lastPageIdUpdated = -1; + m_hasWindow = false; + m_inModalLoop = false; + m_smartInsertDeleteEnabled = true; +#if OS(WINDOWS) + m_selectTrailingWhitespaceEnabled = true; +#else + m_selectTrailingWhitespaceEnabled = false; +#endif + m_blocksRedirects = false; + m_requestReturnNull = false; + m_isPainting = false; + m_canvas.clear(); + + m_navigationController.set(new TestNavigationController(this)); + + m_pendingExtraData.clear(); + m_resourceIdentifierMap.clear(); + m_clearHeaders.clear(); + m_editCommandName.clear(); + m_editCommandValue.clear(); + +#if ENABLE(CLIENT_BASED_GEOLOCATION) + if (m_geolocationClientMock.get()) + m_geolocationClientMock->resetMock(); +#else + m_geolocationServiceMock.clear(); +#endif + + if (m_speechInputControllerMock.get()) + m_speechInputControllerMock->clearResults(); + + m_currentCursor = WebCursorInfo(); + m_windowRect = WebRect(); + m_paintRect = WebRect(); + + if (m_webWidget) + webView()->mainFrame()->setName(WebString()); +} + +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(TestNavigationEntry::create(-1, url, WebString(), frameName).get()); +} + +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().url()); +} + +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(); + RefPtr<TestNavigationEntry> entry(TestNavigationEntry::create()); + + // 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.get()); + updateAddressBar(frame->view()); + 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 = 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::printFrameUserGestureStatus(WebFrame* webframe, const char* msg) +{ + bool isUserGesture = webframe->isProcessingUserGesture(); + printf("Frame with user gesture \"%s\"%s", isUserGesture ? "true" : "false", msg); +} + +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(canvas()->getTopPlatformDevice().GetBitmapContext(), rect); +#else + webWidget()->paint(canvas(), 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 + // for the first two calls. 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 < 3; ++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); + } + ASSERT(m_paintRect.isEmpty()); +} + +PlatformCanvas* WebViewHost::canvas() +{ + if (m_canvas) + return m_canvas.get(); + WebSize widgetSize = webWidget()->size(); + resetScrollRect(); + 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/Tools/DumpRenderTree/chromium/WebViewHost.h b/Tools/DumpRenderTree/chromium/WebViewHost.h new file mode 100644 index 0000000..20b29c0 --- /dev/null +++ b/Tools/DumpRenderTree/chromium/WebViewHost.h @@ -0,0 +1,332 @@ +/* + * 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 "WebAccessibilityNotification.h" +#include "WebCursorInfo.h" +#include "WebFrameClient.h" +#include "WebViewClient.h" +#include <wtf/HashMap.h> +#include <wtf/HashSet.h> +#include <wtf/Vector.h> +#include <wtf/text/WTFString.h> + +class LayoutTestController; +class TestShell; +namespace WebKit { +class WebFrame; +class WebDeviceOrientationClient; +class WebDeviceOrientationClientMock; +class WebGeolocationClient; +class WebGeolocationClientMock; +class WebGeolocationServiceMock; +class WebSpeechInputController; +class WebSpeechInputControllerMock; +class WebSpeechInputListener; +class WebURL; +struct WebRect; +struct WebURLError; +struct WebWindowFeatures; +} +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(); } + + void addClearHeader(const WTF::String& header) { m_clearHeaders.add(header); } + const HashSet<WTF::String>& clearHeaders() const { return m_clearHeaders; } + void closeWidget(); + + WebKit::WebContextMenuData* lastContextMenuData() const; + void clearContextMenuData(); + + WebKit::WebSpeechInputControllerMock* speechInputControllerMock() { return m_speechInputControllerMock.get(); } + + // NavigationHost + virtual bool navigate(const TestNavigationEntry&, bool reload); + + // WebKit::WebViewClient + virtual WebKit::WebView* createView(WebKit::WebFrame*, const WebKit::WebWindowFeatures&, const WebKit::WebString&); + virtual WebKit::WebWidget* createPopupMenu(WebKit::WebPopupType); + virtual WebKit::WebWidget* createPopupMenu(const WebKit::WebPopupMenuInfo&); + virtual WebKit::WebStorageNamespace* createSessionStorageNamespace(unsigned quota); + 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::WebDragData&, WebKit::WebDragOperationsMask, const WebKit::WebImage&, const WebKit::WebPoint&); + virtual void navigateBackForwardSoon(int offset); + virtual int historyBackListCount(); + virtual int historyForwardListCount(); + virtual void postAccessibilityNotification(const WebKit::WebAccessibilityObject&, WebKit::WebAccessibilityNotification); + virtual WebKit::WebNotificationPresenter* notificationPresenter(); +#if ENABLE(CLIENT_BASED_GEOLOCATION) + virtual WebKit::WebGeolocationClient* geolocationClient(); +#else + virtual WebKit::WebGeolocationService* geolocationService(); +#endif + virtual WebKit::WebSpeechInputController* speechInputController(WebKit::WebSpeechInputListener*); + virtual WebKit::WebDeviceOrientationClient* deviceOrientationClient(); + + // WebKit::WebWidgetClient + virtual void didInvalidateRect(const WebKit::WebRect&); + virtual void didScrollRect(int dx, int dy, const WebKit::WebRect&); + virtual void scheduleComposite(); + 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 WebKit::WebApplicationCacheHost* createApplicationCacheHost(WebKit::WebFrame*, WebKit::WebApplicationCacheHostClient*); + 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 didNavigateWithinPage(WebKit::WebFrame*, bool isNewNavigation); + virtual void didChangeLocationWithinPage(WebKit::WebFrame*); + virtual void assignIdentifierToRequest(WebKit::WebFrame*, unsigned identifier, const WebKit::WebURLRequest&); + virtual void removeIdentifierForRequest(unsigned identifier); + 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); + virtual void openFileSystem(WebKit::WebFrame*, WebKit::WebFileSystem::Type, long long size, bool create, WebKit::WebFileSystemCallbacks*); + + WebKit::WebDeviceOrientationClientMock* deviceOrientationClientMock(); + MockSpellCheck* mockSpellCheck(); + +#if ENABLE(CLIENT_BASED_GEOLOCATION) + // Geolocation client mocks for LayoutTestController + WebKit::WebGeolocationClientMock* geolocationClientMock(); +#endif + +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*); + + // Dumping the user gesture status to the console. + void printFrameUserGestureStatus(WebKit::WebFrame*, const char*); + + 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; + bool m_inModalLoop; + 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; + + // Set of headers to clear in willSendRequest. + HashSet<WTF::String> m_clearHeaders; + + // 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<WebKit::WebContextMenuData> m_lastContextMenuData; + + // Geolocation +#if ENABLE(CLIENT_BASED_GEOLOCATION) + OwnPtr<WebKit::WebGeolocationClientMock> m_geolocationClientMock; +#else + OwnPtr<WebKit::WebGeolocationServiceMock> m_geolocationServiceMock; +#endif + + OwnPtr<WebKit::WebDeviceOrientationClientMock> m_deviceOrientationClientMock; + OwnPtr<WebKit::WebSpeechInputControllerMock> m_speechInputControllerMock; + + OwnPtr<TestNavigationController*> m_navigationController; +}; + +#endif // WebViewHost_h diff --git a/Tools/DumpRenderTree/chromium/config.h b/Tools/DumpRenderTree/chromium/config.h new file mode 100644 index 0000000..7dfda18 --- /dev/null +++ b/Tools/DumpRenderTree/chromium/config.h @@ -0,0 +1,56 @@ +/* + * 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> + +#if OS(WINDOWS) && !COMPILER(GCC) +// Allow 'this' to be used in base member initializer list. +#pragma warning(disable : 4355) +// JS_EXPORTDATA is needed to inlucde wtf/WTFString.h. +#define JS_EXPORTDATA __declspec(dllimport) +#else +#define JS_EXPORTDATA +#endif + +#endif // config_h diff --git a/Tools/DumpRenderTree/chromium/fonts.conf b/Tools/DumpRenderTree/chromium/fonts.conf new file mode 100644 index 0000000..be214c6 --- /dev/null +++ b/Tools/DumpRenderTree/chromium/fonts.conf @@ -0,0 +1,155 @@ +<?xml version="1.0"?> +<!DOCTYPE fontconfig SYSTEM "fonts.dtd"> +<!-- /etc/fonts/fonts.conf file to configure system font access --> +<fontconfig> + <match target="pattern"> + <test qual="any" name="family"> + <string>Times</string> + </test> + <edit name="family" mode="assign"> + <string>Times New Roman</string> + </edit> + </match> + + <match target="pattern"> + <test qual="any" name="family"> + <string>sans</string> + </test> + <edit name="family" mode="assign"> + <string>Arial</string> + </edit> + </match> + + <match target="pattern"> + <test qual="any" name="family"> + <string>sans serif</string> + </test> + <edit name="family" mode="assign"> + <string>Arial</string> + </edit> + </match> + + <!-- Some layout tests specify Helvetica as a family and we need to make sure + that we don't fallback to Times New Roman for them --> + <match target="pattern"> + <test qual="any" name="family"> + <string>Helvetica</string> + </test> + <edit name="family" mode="assign"> + <string>Arial</string> + </edit> + </match> + + <match target="pattern"> + <test qual="any" name="family"> + <string>sans-serif</string> + </test> + <edit name="family" mode="assign"> + <string>Arial</string> + </edit> + </match> + + <match target="pattern"> + <test qual="any" name="family"> + <string>serif</string> + </test> + <edit name="family" mode="assign"> + <string>Times New Roman</string> + </edit> + </match> + + <match target="pattern"> + <test qual="any" name="family"> + <string>mono</string> + </test> + <edit name="family" mode="assign"> + <string>Courier New</string> + </edit> + </match> + + <match target="pattern"> + <test qual="any" name="family"> + <string>monospace</string> + </test> + <edit name="family" mode="assign"> + <string>Courier New</string> + </edit> + </match> + + <match target="pattern"> + <test qual="any" name="family"> + <string>Courier</string> + </test> + <edit name="family" mode="assign"> + <string>Courier New</string> + </edit> + </match> + + <match target="pattern"> + <test qual="any" name="family"> + <string>cursive</string> + </test> + <edit name="family" mode="assign"> + <string>Comic Sans MS</string> + </edit> + </match> + + <match target="pattern"> + <test qual="any" name="family"> + <string>fantasy</string> + </test> + <edit name="family" mode="assign"> + <string>Impact</string> + </edit> + </match> + + <match target="pattern"> + <test qual="any" name="family"> + <string>Monaco</string> + </test> + <edit name="family" mode="assign"> + <string>Times New Roman</string> + </edit> + </match> + + <match target="pattern"> + <test name="family" compare="eq"> + <string>NonAntiAliasedSans</string> + </test> + <edit name="family" mode="assign"> + <string>Arial</string> + </edit> + <edit name="antialias" mode="assign"> + <bool>false</bool> + </edit> + </match> + + <match target="pattern"> + <test name="family" compare="eq"> + <string>SlightHintedGeorgia</string> + </test> + <edit name="family" mode="assign"> + <string>Georgia</string> + </edit> + <edit name="hintstyle" mode="assign"> + <const>hintslight</const> + </edit> + </match> + + <match target="pattern"> + <test name="family" compare="eq"> + <string>NonHintedSans</string> + </test> + <edit name="family" mode="assign"> + <string>Verdana</string> + </edit> + <!-- These deliberately contradict each other. The 'hinting' preference + should take priority --> + <edit name="hintstyle" mode="assign"> + <const>hintfull</const> + </edit> + <edit name="hinting" mode="assign"> + <bool>false</bool> + </edit> + </match> +</fontconfig> |