diff options
author | Simon Hausmann <simon.hausmann@nokia.com> | 2012-01-06 14:44:00 +0100 |
---|---|---|
committer | Simon Hausmann <simon.hausmann@nokia.com> | 2012-01-06 14:44:00 +0100 |
commit | 40736c5763bf61337c8c14e16d8587db021a87d4 (patch) | |
tree | b17a9c00042ad89cb1308e2484491799aa14e9f8 /Tools/DumpRenderTree/chromium | |
download | qtwebkit-40736c5763bf61337c8c14e16d8587db021a87d4.tar.gz |
Imported WebKit commit 2ea9d364d0f6efa8fa64acf19f451504c59be0e4 (http://svn.webkit.org/repository/webkit/trunk@104285)
Diffstat (limited to 'Tools/DumpRenderTree/chromium')
63 files changed, 16988 insertions, 0 deletions
diff --git a/Tools/DumpRenderTree/chromium/AccessibilityController.cpp b/Tools/DumpRenderTree/chromium/AccessibilityController.cpp new file mode 100644 index 000000000..860a747aa --- /dev/null +++ b/Tools/DumpRenderTree/chromium/AccessibilityController.cpp @@ -0,0 +1,151 @@ +/* + * 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 "WebAccessibilityObject.h" +#include "WebFrame.h" +#include "platform/WebString.h" +#include "WebView.h" + +using namespace WebKit; + +AccessibilityController::AccessibilityController(TestShell* shell) + : m_logAccessibilityEvents(false) + , m_shell(shell) +{ + + bindMethod("logAccessibilityEvents", &AccessibilityController::logAccessibilityEventsCallback); + bindMethod("addNotificationListener", &AccessibilityController::addNotificationListenerCallback); + bindMethod("removeNotificationListener", &AccessibilityController::removeNotificationListenerCallback); + + bindProperty("focusedElement", &AccessibilityController::focusedElementGetterCallback); + bindProperty("rootElement", &AccessibilityController::rootElementGetterCallback); + + bindFallbackMethod(&AccessibilityController::fallbackCallback); +} + +void AccessibilityController::bindToJavascript(WebFrame* frame, const WebString& classname) +{ + WebAccessibilityObject::enableAccessibility(); + CppBoundClass::bindToJavascript(frame, classname); +} + +void AccessibilityController::reset() +{ + m_rootElement = WebAccessibilityObject(); + m_focusedElement = WebAccessibilityObject(); + m_elements.clear(); + + m_logAccessibilityEvents = 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.getOrCreate(m_focusedElement); +} + +AccessibilityUIElement* AccessibilityController::getRootElement() +{ + if (m_rootElement.isNull()) + m_rootElement = m_shell->webView()->accessibilityObject(); + return m_elements.createRoot(m_rootElement); +} + +bool AccessibilityController::shouldLogAccessibilityEvents() +{ + return m_logAccessibilityEvents; +} + +void AccessibilityController::notificationReceived(const WebKit::WebAccessibilityObject& target, const char* notificationName) +{ + // Call notification listeners on the element. + AccessibilityUIElement* element = m_elements.getOrCreate(target); + element->notificationReceived(notificationName); + + // Call global notification listeners. + size_t callbackCount = m_notificationCallbacks.size(); + for (size_t i = 0; i < callbackCount; i++) { + CppVariant arguments[2]; + arguments[0].set(*element->getAsCppVariant()); + arguments[1].set(notificationName); + CppVariant invokeResult; + m_notificationCallbacks[i].invokeDefault(arguments, 2, invokeResult); + } +} + +void AccessibilityController::logAccessibilityEventsCallback(const CppArgumentList&, CppVariant* result) +{ + m_logAccessibilityEvents = true; + result->setNull(); +} + +void AccessibilityController::addNotificationListenerCallback(const CppArgumentList& arguments, CppVariant* result) +{ + if (arguments.size() < 1 || !arguments[0].isObject()) { + result->setNull(); + return; + } + + m_notificationCallbacks.push_back(arguments[0]); + result->setNull(); +} + +void AccessibilityController::removeNotificationListenerCallback(const CppArgumentList&, CppVariant* result) +{ + // FIXME: Implement this. + 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 000000000..954edb9ca --- /dev/null +++ b/Tools/DumpRenderTree/chromium/AccessibilityController.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 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(); + + bool shouldLogAccessibilityEvents(); + + void notificationReceived(const WebKit::WebAccessibilityObject& target, const char* notificationName); + +private: + // If true, will log all accessibility notifications. + bool m_logAccessibilityEvents; + + // Bound methods and properties + void logAccessibilityEventsCallback(const CppArgumentList&, CppVariant*); + void fallbackCallback(const CppArgumentList&, CppVariant*); + void addNotificationListenerCallback(const CppArgumentList&, CppVariant*); + void removeNotificationListenerCallback(const CppArgumentList&, CppVariant*); + + void focusedElementGetterCallback(CppVariant*); + void rootElementGetterCallback(CppVariant*); + + WebKit::WebAccessibilityObject m_focusedElement; + WebKit::WebAccessibilityObject m_rootElement; + + AccessibilityUIElementList m_elements; + + std::vector<CppVariant> m_notificationCallbacks; + + 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 000000000..3ca1ee72a --- /dev/null +++ b/Tools/DumpRenderTree/chromium/AccessibilityUIElement.cpp @@ -0,0 +1,817 @@ +/* + * 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 "platform/WebCString.h" +#include "platform/WebRect.h" +#include "platform/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 getHelpText(const WebAccessibilityObject& object) +{ + string helpText = object.helpText().utf8(); + return helpText.insert(0, "AXHelp: "); +} + +string getStringValue(const WebAccessibilityObject& object) +{ + string value = object.stringValue().utf8(); + return value.insert(0, "AXValue: "); +} + +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 getOrientation(const WebAccessibilityObject& object) +{ + if (object.isVertical()) + return "AXOrientation: AXVerticalOrientation"; + + return "AXOrientation: AXHorizontalOrientation"; +} + +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); + + // + // Properties + // + + bindProperty("role", &AccessibilityUIElement::roleGetterCallback); + bindProperty("title", &AccessibilityUIElement::titleGetterCallback); + bindProperty("description", &AccessibilityUIElement::descriptionGetterCallback); + bindProperty("helpText", &AccessibilityUIElement::helpTextGetterCallback); + bindProperty("stringValue", &AccessibilityUIElement::stringValueGetterCallback); + bindProperty("x", &AccessibilityUIElement::xGetterCallback); + bindProperty("y", &AccessibilityUIElement::yGetterCallback); + bindProperty("width", &AccessibilityUIElement::widthGetterCallback); + bindProperty("height", &AccessibilityUIElement::heightGetterCallback); + bindProperty("intValue", &AccessibilityUIElement::intValueGetterCallback); + bindProperty("minValue", &AccessibilityUIElement::minValueGetterCallback); + bindProperty("maxValue", &AccessibilityUIElement::maxValueGetterCallback); + bindProperty("childrenCount", &AccessibilityUIElement::childrenCountGetterCallback); + bindProperty("insertionPointLineNumber", &AccessibilityUIElement::insertionPointLineNumberGetterCallback); + bindProperty("selectedTextRange", &AccessibilityUIElement::selectedTextRangeGetterCallback); + bindProperty("isEnabled", &AccessibilityUIElement::isEnabledGetterCallback); + bindProperty("isRequired", &AccessibilityUIElement::isRequiredGetterCallback); + bindProperty("isFocused", &AccessibilityUIElement::isFocusedGetterCallback); + bindProperty("isFocusable", &AccessibilityUIElement::isFocusableGetterCallback); + bindProperty("isSelected", &AccessibilityUIElement::isSelectedGetterCallback); + bindProperty("isSelectable", &AccessibilityUIElement::isSelectableGetterCallback); + bindProperty("isMultiSelectable", &AccessibilityUIElement::isMultiSelectableGetterCallback); + bindProperty("isSelectedOptionActive", &AccessibilityUIElement::isSelectedOptionActiveGetterCallback); + bindProperty("isExpanded", &AccessibilityUIElement::isExpandedGetterCallback); + bindProperty("isChecked", &AccessibilityUIElement::isCheckedGetterCallback); + bindProperty("isVisible", &AccessibilityUIElement::isVisibleGetterCallback); + bindProperty("isOffScreen", &AccessibilityUIElement::isOffScreenGetterCallback); + bindProperty("isCollapsed", &AccessibilityUIElement::isCollapsedGetterCallback); + bindProperty("hasPopup", &AccessibilityUIElement::hasPopupGetterCallback); + bindProperty("isValid", &AccessibilityUIElement::isValidGetterCallback); + bindProperty("orientation", &AccessibilityUIElement::orientationGetterCallback); + + // + // Methods + // + + bindMethod("allAttributes", &AccessibilityUIElement::allAttributesCallback); + bindMethod("attributesOfLinkedUIElements", &AccessibilityUIElement::attributesOfLinkedUIElementsCallback); + bindMethod("attributesOfDocumentLinks", &AccessibilityUIElement::attributesOfDocumentLinksCallback); + bindMethod("attributesOfChildren", &AccessibilityUIElement::attributesOfChildrenCallback); + 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); + bindMethod("showMenu", &AccessibilityUIElement::showMenuCallback); + bindMethod("press", &AccessibilityUIElement::pressCallback); + bindMethod("isEqual", &AccessibilityUIElement::isEqualCallback); + bindMethod("addNotificationListener", &AccessibilityUIElement::addNotificationListenerCallback); + bindMethod("removeNotificationListener", &AccessibilityUIElement::removeNotificationListenerCallback); + bindMethod("takeFocus", &AccessibilityUIElement::takeFocusCallback); + + bindFallbackMethod(&AccessibilityUIElement::fallbackCallback); +} + +AccessibilityUIElement* AccessibilityUIElement::getChildAtIndex(unsigned index) +{ + return m_factory->getOrCreate(accessibilityObject().childAt(index)); +} + +bool AccessibilityUIElement::isEqual(const WebKit::WebAccessibilityObject& other) +{ + return accessibilityObject().equals(other); +} + +void AccessibilityUIElement::notificationReceived(const char* notificationName) +{ + size_t callbackCount = m_notificationCallbacks.size(); + for (size_t i = 0; i < callbackCount; i++) { + CppVariant notificationNameArgument; + notificationNameArgument.set(notificationName); + CppVariant invokeResult; + m_notificationCallbacks[i].invokeDefault(¬ificationNameArgument, 1, invokeResult); + } +} + +// +// Properties +// + +void AccessibilityUIElement::roleGetterCallback(CppVariant* result) +{ + result->set(getRole(accessibilityObject())); +} + +void AccessibilityUIElement::titleGetterCallback(CppVariant* result) +{ + result->set(getTitle(accessibilityObject())); +} + +void AccessibilityUIElement::descriptionGetterCallback(CppVariant* result) +{ + result->set(getDescription(accessibilityObject())); +} + +void AccessibilityUIElement::helpTextGetterCallback(CppVariant* result) +{ + result->set(getHelpText(accessibilityObject())); +} + +void AccessibilityUIElement::stringValueGetterCallback(CppVariant* result) +{ + result->set(getStringValue(accessibilityObject())); +} + +void AccessibilityUIElement::xGetterCallback(CppVariant* result) +{ + result->set(accessibilityObject().boundingBoxRect().x); +} + +void AccessibilityUIElement::yGetterCallback(CppVariant* result) +{ + result->set(accessibilityObject().boundingBoxRect().y); +} + +void AccessibilityUIElement::widthGetterCallback(CppVariant* result) +{ + result->set(accessibilityObject().boundingBoxRect().width); +} + +void AccessibilityUIElement::heightGetterCallback(CppVariant* result) +{ + result->set(accessibilityObject().boundingBoxRect().height); +} + +void AccessibilityUIElement::intValueGetterCallback(CppVariant* result) +{ + result->set(accessibilityObject().valueForRange()); +} + +void AccessibilityUIElement::minValueGetterCallback(CppVariant* result) +{ + result->set(accessibilityObject().minValueForRange()); +} + +void AccessibilityUIElement::maxValueGetterCallback(CppVariant* result) +{ + result->set(accessibilityObject().maxValueForRange()); +} + +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::insertionPointLineNumberGetterCallback(CppVariant* result) +{ + WebVector<int> lineBreaks; + accessibilityObject().lineBreaks(lineBreaks); + int cursor = accessibilityObject().selectionEnd(); + int line = 0; + while (line < static_cast<int>(lineBreaks.size()) && lineBreaks[line] <= cursor) + line++; + result->set(line); +} + +void AccessibilityUIElement::selectedTextRangeGetterCallback(CppVariant* result) +{ + // FIXME: Implement this. + result->set(std::string()); +} + +void AccessibilityUIElement::isEnabledGetterCallback(CppVariant* result) +{ + result->set(accessibilityObject().isEnabled()); +} + +void AccessibilityUIElement::isRequiredGetterCallback(CppVariant* result) +{ + result->set(accessibilityObject().isRequired()); +} + +void AccessibilityUIElement::isFocusedGetterCallback(CppVariant* result) +{ + result->set(accessibilityObject().isFocused()); +} + +void AccessibilityUIElement::isFocusableGetterCallback(CppVariant* result) +{ + result->set(accessibilityObject().canSetFocusAttribute()); +} + +void AccessibilityUIElement::isSelectedGetterCallback(CppVariant* result) +{ + result->set(accessibilityObject().isSelected()); +} + +void AccessibilityUIElement::isSelectableGetterCallback(CppVariant* result) +{ + result->set(accessibilityObject().canSetSelectedAttribute()); +} + +void AccessibilityUIElement::isMultiSelectableGetterCallback(CppVariant* result) +{ + result->set(accessibilityObject().isMultiSelectable()); +} + +void AccessibilityUIElement::isSelectedOptionActiveGetterCallback(CppVariant* result) +{ + result->set(accessibilityObject().isSelectedOptionActive()); +} + +void AccessibilityUIElement::isExpandedGetterCallback(CppVariant* result) +{ + result->set(!accessibilityObject().isCollapsed()); +} + +void AccessibilityUIElement::isCheckedGetterCallback(CppVariant* result) +{ + result->set(accessibilityObject().isChecked()); +} + +void AccessibilityUIElement::isVisibleGetterCallback(CppVariant* result) +{ + result->set(accessibilityObject().isVisible()); +} + +void AccessibilityUIElement::isOffScreenGetterCallback(CppVariant* result) +{ + result->set(accessibilityObject().isOffScreen()); +} + +void AccessibilityUIElement::isCollapsedGetterCallback(CppVariant* result) +{ + result->set(accessibilityObject().isCollapsed()); +} + +void AccessibilityUIElement::hasPopupGetterCallback(CppVariant* result) +{ + result->set(accessibilityObject().ariaHasPopup()); +} + +void AccessibilityUIElement::isValidGetterCallback(CppVariant* result) +{ + result->set(accessibilityObject().isValid()); +} + +void AccessibilityUIElement::orientationGetterCallback(CppVariant* result) +{ + result->set(getOrientation(accessibilityObject())); +} + +// +// Methods +// + +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::showMenuCallback(const CppArgumentList&, CppVariant* result) +{ + result->setNull(); +} + +void AccessibilityUIElement::pressCallback(const CppArgumentList&, CppVariant* result) +{ + accessibilityObject().performDefaultAction(); + result->setNull(); +} + +void AccessibilityUIElement::isEqualCallback(const CppArgumentList& arguments, CppVariant* result) +{ + if (arguments.size() < 1 || !arguments[0].isObject()) { + result->setNull(); + return; + } + + result->set(arguments[0].isEqual(*getAsCppVariant())); +} + +void AccessibilityUIElement::addNotificationListenerCallback(const CppArgumentList& arguments, CppVariant* result) +{ + if (arguments.size() < 1 || !arguments[0].isObject()) { + result->setNull(); + return; + } + + m_notificationCallbacks.push_back(arguments[0]); + result->setNull(); +} + +void AccessibilityUIElement::removeNotificationListenerCallback(const CppArgumentList&, CppVariant* result) +{ + // FIXME: Implement this. + result->setNull(); +} + +void AccessibilityUIElement::takeFocusCallback(const CppArgumentList&, CppVariant* result) +{ + accessibilityObject().setFocused(true); + result->setNull(); +} + +void AccessibilityUIElement::fallbackCallback(const CppArgumentList &, CppVariant* result) +{ + // FIXME: Implement this. + result->setNull(); +} + +RootAccessibilityUIElement::RootAccessibilityUIElement(const WebAccessibilityObject &object, Factory *factory) + : AccessibilityUIElement(object, factory) { } + +AccessibilityUIElement* RootAccessibilityUIElement::getChildAtIndex(unsigned index) +{ + if (index) + return 0; + + return factory()->getOrCreate(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::getOrCreate(const WebAccessibilityObject& object) +{ + if (object.isNull()) + return 0; + + size_t elementCount = m_elements.size(); + for (size_t i = 0; i < elementCount; i++) { + if (m_elements[i]->isEqual(object)) + return m_elements[i]; + } + + 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 000000000..b5fce86f0 --- /dev/null +++ b/Tools/DumpRenderTree/chromium/AccessibilityUIElement.h @@ -0,0 +1,164 @@ +/* + * 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 <vector> +#include <wtf/Vector.h> + +class AccessibilityUIElement : public CppBoundClass { +public: + class Factory { + public: + virtual ~Factory() { } + virtual AccessibilityUIElement* getOrCreate(const WebKit::WebAccessibilityObject&) = 0; + }; + + AccessibilityUIElement(const WebKit::WebAccessibilityObject&, Factory*); + + virtual AccessibilityUIElement* getChildAtIndex(unsigned); + virtual bool isRoot() const { return false; } + virtual bool isEqual(const WebKit::WebAccessibilityObject&); + + virtual void notificationReceived(const char *notificationName); + +protected: + const WebKit::WebAccessibilityObject& accessibilityObject() const { return m_accessibilityObject; } + + Factory* factory() const { return m_factory; } + +private: + // Bound properties. + void roleGetterCallback(CppVariant*); + void titleGetterCallback(CppVariant*); + void descriptionGetterCallback(CppVariant*); + void helpTextGetterCallback(CppVariant*); + void stringValueGetterCallback(CppVariant*); + void xGetterCallback(CppVariant*); + void yGetterCallback(CppVariant*); + void widthGetterCallback(CppVariant*); + void heightGetterCallback(CppVariant*); + void intValueGetterCallback(CppVariant*); + void minValueGetterCallback(CppVariant*); + void maxValueGetterCallback(CppVariant*); + void childrenCountGetterCallback(CppVariant*); + void insertionPointLineNumberGetterCallback(CppVariant*); + void selectedTextRangeGetterCallback(CppVariant*); + void isEnabledGetterCallback(CppVariant*); + void isRequiredGetterCallback(CppVariant*); + void isFocusedGetterCallback(CppVariant*); + void isFocusableGetterCallback(CppVariant*); + void isSelectedGetterCallback(CppVariant*); + void isSelectableGetterCallback(CppVariant*); + void isMultiSelectableGetterCallback(CppVariant*); + void isSelectedOptionActiveGetterCallback(CppVariant*); + void isExpandedGetterCallback(CppVariant*); + void isCheckedGetterCallback(CppVariant*); + void isVisibleGetterCallback(CppVariant*); + void isOffScreenGetterCallback(CppVariant*); + void isCollapsedGetterCallback(CppVariant*); + void hasPopupGetterCallback(CppVariant*); + void isValidGetterCallback(CppVariant*); + void orientationGetterCallback(CppVariant*); + + // Bound methods. + 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 showMenuCallback(const CppArgumentList&, CppVariant*); + void pressCallback(const CppArgumentList&, CppVariant*); + void isEqualCallback(const CppArgumentList&, CppVariant*); + void addNotificationListenerCallback(const CppArgumentList&, CppVariant*); + void removeNotificationListenerCallback(const CppArgumentList&, CppVariant*); + void takeFocusCallback(const CppArgumentList&, CppVariant*); + + void fallbackCallback(const CppArgumentList&, CppVariant*); + + WebKit::WebAccessibilityObject m_accessibilityObject; + Factory* m_factory; + std::vector<CppVariant> m_notificationCallbacks; +}; + + +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* getOrCreate(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 000000000..9b4a3b646 --- /dev/null +++ b/Tools/DumpRenderTree/chromium/CppBoundClass.cpp @@ -0,0 +1,352 @@ +/* + * 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 "platform/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(PassOwnPtr<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, PassOwnPtr<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 000000000..4fb5361e1 --- /dev/null +++ b/Tools/DumpRenderTree/chromium/CppBoundClass.h @@ -0,0 +1,245 @@ +/* + * 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/PassOwnPtr.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 { + WTF_MAKE_NONCOPYABLE(CppBoundClass); +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 the specified 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, the method parameter + // 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&, PassOwnPtr<GetterCallback>); + + // A wrapper for BindGetterCallback, to simplify the common case of binding a + // property on the current object. Though not verified here, the method parameter + // must be a method of this CppBoundClass subclass. + template<class T> + void bindProperty(const std::string& name, void (T::*method)(CppVariant*)) + { + OwnPtr<GetterCallback> callback = adoptPtr(new MemberGetterCallback<T>(static_cast<T*>(this), method)); + bindGetterCallback(name, callback.release()); + } + + // 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(PassOwnPtr<Callback> fallbackCallback) + { + m_fallbackCallback = 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) { + OwnPtr<Callback> callback = adoptPtr(new MemberCallback<T>(static_cast<T*>(this), method)); + bindFallbackCallback(callback.release()); + } else + bindFallbackCallback(nullptr); + } + + // 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 000000000..b587d75aa --- /dev/null +++ b/Tools/DumpRenderTree/chromium/CppVariant.cpp @@ -0,0 +1,321 @@ +/* + * 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; +} + +bool CppVariant::invokeDefault(const CppVariant* arguments, uint32_t argumentCount, + CppVariant& result) const +{ + ASSERT(isObject()); + NPObject* npObject = value.objectValue; + NPVariant r; + bool status = WebBindings::invokeDefault(0, npObject, 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 000000000..60cc6271e --- /dev/null +++ b/Tools/DumpRenderTree/chromium/CppVariant.h @@ -0,0 +1,141 @@ +/* + * 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; + + // Invoke an object's default method 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 invokeDefault(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 000000000..21aed1b9d --- /dev/null +++ b/Tools/DumpRenderTree/chromium/DRTDevToolsAgent.cpp @@ -0,0 +1,131 @@ +/* + * 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 "DRTDevToolsClient.h" + +#include "platform/WebCString.h" +#include "WebDevToolsAgent.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; +} + +void DRTDevToolsAgent::reset() +{ + m_taskList.revokeAll(); +} + +void DRTDevToolsAgent::setWebView(WebView* webView) +{ + m_webView = webView; +} + +void DRTDevToolsAgent::sendMessageToInspectorFrontend(const WebString& data) +{ + if (m_drtDevToolsClient) + m_drtDevToolsClient->asyncCall(data); +} + +void DRTDevToolsAgent::runtimePropertyChanged(const WebString& name, const WebString& value) +{ + // FIXME: Implement. +} + +WebDevToolsAgentClient::WebKitClientMessageLoop* DRTDevToolsAgent::createClientMessageLoop() +{ + return webkit_support::CreateDevToolsMessageLoop(); +} + +void DRTDevToolsAgent::asyncCall(const WebString& args) +{ + postTask(new AsyncCallTask(this, args)); +} + +void DRTDevToolsAgent::call(const WebString& args) +{ + WebDevToolsAgent* agent = webDevToolsAgent(); + if (agent) + agent->dispatchOnInspectorBackend(args); +} + +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; +} + +bool DRTDevToolsAgent::setJavaScriptProfilingEnabled(bool enabled) +{ + WebDevToolsAgent* agent = webDevToolsAgent(); + if (!agent) + return false; + agent->setJavaScriptProfilingEnabled(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; +} diff --git a/Tools/DumpRenderTree/chromium/DRTDevToolsAgent.h b/Tools/DumpRenderTree/chromium/DRTDevToolsAgent.h new file mode 100644 index 000000000..6e491ad53 --- /dev/null +++ b/Tools/DumpRenderTree/chromium/DRTDevToolsAgent.h @@ -0,0 +1,96 @@ +/* + * 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 "Task.h" +#include "WebDevToolsAgentClient.h" +#include "platform/WebString.h" +#include <wtf/HashMap.h> +#include <wtf/Noncopyable.h> + +namespace WebKit { + +class WebCString; +class WebDevToolsAgent; +class WebView; +struct WebDevToolsMessageData; + +} // namespace WebKit + +class DRTDevToolsClient; + +class DRTDevToolsAgent : public WebKit::WebDevToolsAgentClient { + WTF_MAKE_NONCOPYABLE(DRTDevToolsAgent); +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 WebKitClientMessageLoop* createClientMessageLoop(); + + void asyncCall(const WebKit::WebString& args); + + void attach(DRTDevToolsClient*); + void detach(); + + bool evaluateInWebInspector(long callID, const std::string& script); + bool setJavaScriptProfilingEnabled(bool); + TaskList* taskList() { return &m_taskList; } + +private: + void call(const WebKit::WebString& args); + WebKit::WebDevToolsAgent* webDevToolsAgent(); + + class AsyncCallTask: public MethodTask<DRTDevToolsAgent> { + public: + AsyncCallTask(DRTDevToolsAgent* object, const WebKit::WebString& args) + : MethodTask<DRTDevToolsAgent>(object), m_args(args) { } + virtual void runIfValid() { m_object->call(m_args); } + + private: + WebKit::WebString m_args; + }; + + 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/DRTDevToolsClient.cpp b/Tools/DumpRenderTree/chromium/DRTDevToolsClient.cpp new file mode 100644 index 000000000..a7fc4d048 --- /dev/null +++ b/Tools/DumpRenderTree/chromium/DRTDevToolsClient.cpp @@ -0,0 +1,102 @@ +/* + * 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 "WebDevToolsAgent.h" +#include "WebDevToolsFrontend.h" +#include "WebFrame.h" +#include "WebScriptSource.h" +#include "WebView.h" +#include "webkit/support/webkit_support.h" +#include <wtf/PassOwnPtr.h> + +using namespace WebKit; + +DRTDevToolsClient::DRTDevToolsClient(DRTDevToolsAgent* agent, WebView* webView) + : m_webView(webView) + , m_drtDevToolsAgent(agent) +{ + m_webDevToolsFrontend = adoptPtr(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::sendMessageToBackend(const WebString& data) +{ + if (m_drtDevToolsAgent) + m_drtDevToolsAgent->asyncCall(data); +} + +void DRTDevToolsClient::activateWindow() +{ + // Not implemented. +} + +void DRTDevToolsClient::closeWindow() +{ + // Not implemented. +} + +void DRTDevToolsClient::dockWindow() +{ + // Not implemented. +} + +void DRTDevToolsClient::undockWindow() +{ + // Not implemented. +} + +void DRTDevToolsClient::asyncCall(const WebString& args) +{ + postTask(new AsyncCallTask(this, args)); +} + +void DRTDevToolsClient::call(const WebString& args) +{ + m_webDevToolsFrontend->dispatchOnInspectorFrontend(args); +} + diff --git a/Tools/DumpRenderTree/chromium/DRTDevToolsClient.h b/Tools/DumpRenderTree/chromium/DRTDevToolsClient.h new file mode 100644 index 000000000..97a1edc0b --- /dev/null +++ b/Tools/DumpRenderTree/chromium/DRTDevToolsClient.h @@ -0,0 +1,87 @@ +/* + * 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 "Task.h" +#include "WebDevToolsFrontendClient.h" +#include "platform/WebString.h" +#include <wtf/Noncopyable.h> +#include <wtf/OwnPtr.h> +namespace WebKit { + +class WebDevToolsFrontend; +struct WebDevToolsMessageData; +class WebView; + +} // namespace WebKit + +class DRTDevToolsAgent; + +class DRTDevToolsClient : public WebKit::WebDevToolsFrontendClient { + WTF_MAKE_NONCOPYABLE(DRTDevToolsClient); +public: + DRTDevToolsClient(DRTDevToolsAgent*, WebKit::WebView*); + virtual ~DRTDevToolsClient(); + void reset(); + + // WebDevToolsFrontendClient implementation + virtual void sendMessageToBackend(const WebKit::WebString&); + + virtual void activateWindow(); + virtual void closeWindow(); + virtual void dockWindow(); + virtual void undockWindow(); + + void asyncCall(const WebKit::WebString& args); + + void allMessagesProcessed(); + TaskList* taskList() { return &m_taskList; } + + private: + void call(const WebKit::WebString& args); + class AsyncCallTask: public MethodTask<DRTDevToolsClient> { + public: + AsyncCallTask(DRTDevToolsClient* object, const WebKit::WebString& args) + : MethodTask<DRTDevToolsClient>(object), m_args(args) { } + virtual void runIfValid() { m_object->call(m_args); } + + private: + WebKit::WebString 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 000000000..f9f6f5a69 --- /dev/null +++ b/Tools/DumpRenderTree/chromium/DumpRenderTree.cpp @@ -0,0 +1,290 @@ +/* + * Copyright (C) 2011 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-testing.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 optionDebugRenderTree[] = "--debug-render-tree"; +static const char optionDebugLayerTree[] = "--debug-layer-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 optionHardwareAcceleratedGL[] = "--enable-hardware-gpu"; +static const char optionEnableThreadedCompositing[] = "--enable-threaded-compositing"; +static const char optionForceCompositingMode[] = "--force-compositing-mode"; +static const char optionEnableAccelerated2DCanvas[] = "--enable-accelerated-2d-canvas"; +static const char optionEnableLegacyAccelerated2DCanvas[] = "--enable-legacy-accelerated-2d-canvas"; +static const char optionEnableAcceleratedPainting[] = "--enable-accelerated-painting"; +static const char optionEnableAcceleratedCompositingForVideo[] = "--enable-accelerated-video"; +static const char optionEnableCompositeToTexture[] = "--enable-composite-to-texture"; +static const char optionUseGraphicsContext3DImplementation[] = "--use-graphics-context-3d-implementation="; +static const char optionEnablePerTilePainting[] = "--enable-per-tile-painting"; + +static const char optionStressOpt[] = "--stress-opt"; +static const char optionStressDeopt[] = "--stress-deopt"; +static const char optionJavaScriptFlags[] = "--js-flags="; +static const char optionNoTimeout[] = "--no-timeout"; +static const char optionWebCoreLogChannels[] = "--webcore-log-channels="; + +class WebKitSupportTestEnvironment { +public: + WebKitSupportTestEnvironment() + { + webkit_support::SetUpTestEnvironment(); + } + ~WebKitSupportTestEnvironment() + { + webkit_support::TearDownTestEnvironment(); + } +}; + +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); + v8::V8::SetFlagsFromString(shell.javaScriptFlags().c_str(), shell.javaScriptFlags().length()); + if (shell.stressOpt() || shell.stressDeopt()) { + if (shell.stressOpt()) + v8::Testing::SetStressRunType(v8::Testing::kStressTypeOpt); + else + v8::Testing::SetStressRunType(v8::Testing::kStressTypeDeopt); + for (int i = 0; i < v8::Testing::GetStressRuns(); i++) { + v8::Testing::PrepareStressRun(i); + bool isLastLoad = (i == (v8::Testing::GetStressRuns() - 1)); + shell.setDumpWhenFinished(isLastLoad); + shell.resetTestController(); + shell.runFileTest(params); + } + } else { + shell.resetTestController(); + shell.runFileTest(params); + } + shell.setLayoutTestTimeout(oldTimeoutMsec); +} + +int main(int argc, char* argv[]) +{ + WebKitSupportTestEnvironment testEnvironment; + platformInit(&argc, &argv); + + TestParams params; + Vector<string> tests; + bool serverMode = false; + bool testShellMode = false; + bool allowExternalPages = false; + bool startupDialog = false; + bool acceleratedCompositingForVideoEnabled = false; + bool threadedCompositingEnabled = false; + bool compositeToTexture = false; + bool forceCompositingMode = false; + bool accelerated2DCanvasEnabled = false; + bool legacyAccelerated2DCanvasEnabled = false; + bool acceleratedPaintingEnabled = false; + bool perTilePaintingEnabled = false; + bool stressOpt = false; + bool stressDeopt = false; + bool hardwareAcceleratedGL = false; + string javaScriptFlags; + bool noTimeout = false; + for (int i = 1; i < argc; ++i) { + string argument(argv[i]); + if (argument == "-") + serverMode = true; + else if (argument == optionNotree) + params.dumpTree = false; + else if (argument == optionPixelTests) + params.dumpPixels = true; + else if (!argument.find(optionPixelTestsWithName)) { + params.dumpPixels = true; + params.pixelFileName = argument.substr(strlen(optionPixelTestsWithName)); + } else if (argument == optionDebugRenderTree) + params.debugRenderTree = true; + else if (argument == optionDebugLayerTree) + params.debugLayerTree = true; + else if (argument == optionTestShell) { + testShellMode = true; + serverMode = true; + } else if (argument == optionAllowExternalPages) + allowExternalPages = true; + else if (argument == optionStartupDialog) + startupDialog = true; + else if (argument == optionCheckLayoutTestSystemDeps) + return checkLayoutTestSystemDependencies() ? EXIT_SUCCESS : EXIT_FAILURE; + else if (argument == optionHardwareAcceleratedGL) + hardwareAcceleratedGL = true; + else if (argument == optionEnableAcceleratedCompositingForVideo) + acceleratedCompositingForVideoEnabled = true; + else if (argument == optionEnableThreadedCompositing) + threadedCompositingEnabled = true; + else if (argument == optionEnableCompositeToTexture) + compositeToTexture = true; + else if (argument == optionForceCompositingMode) + forceCompositingMode = true; + else if (argument == optionEnableAccelerated2DCanvas) + accelerated2DCanvasEnabled = true; + else if (argument == optionEnableLegacyAccelerated2DCanvas) + legacyAccelerated2DCanvasEnabled = true; + else if (argument == optionEnableAcceleratedPainting) + acceleratedPaintingEnabled = true; + else if (!argument.find(optionUseGraphicsContext3DImplementation)) { + string implementation = argument.substr(strlen(optionUseGraphicsContext3DImplementation)); + if (!implementation.compare("IN_PROCESS")) + webkit_support::SetGraphicsContext3DImplementation(webkit_support::IN_PROCESS); + else if (!implementation.compare("IN_PROCESS_COMMAND_BUFFER")) + webkit_support::SetGraphicsContext3DImplementation(webkit_support::IN_PROCESS_COMMAND_BUFFER); + else + fprintf(stderr, "Unknown GraphicContext3D implementation %s\n", implementation.c_str()); + } else if (argument == optionEnablePerTilePainting) + perTilePaintingEnabled = true; + else if (argument == optionStressOpt) + stressOpt = true; + else if (argument == optionStressDeopt) + stressDeopt = true; + else if (!argument.find(optionJavaScriptFlags)) + javaScriptFlags = argument.substr(strlen(optionJavaScriptFlags)); + else if (argument == optionNoTimeout) + noTimeout = true; + else if (!argument.find(optionWebCoreLogChannels)) { + string channels = argument.substr(strlen(optionWebCoreLogChannels)); + webkit_support::EnableWebCoreLogChannels(channels); + } 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 (stressOpt && stressDeopt) { + fprintf(stderr, "--stress-opt and --stress-deopt are mutually exclusive.\n"); + return EXIT_FAILURE; + } + + webkit_support::SetUpGLBindings(hardwareAcceleratedGL ? webkit_support::GL_BINDING_DEFAULT : webkit_support::GL_BINDING_SOFTWARE_RENDERER); + + if (startupDialog) + openStartupDialog(); + + { // Explicit scope for the TestShell instance. + TestShell shell(testShellMode); + shell.setAllowExternalPages(allowExternalPages); + shell.setAcceleratedCompositingForVideoEnabled(acceleratedCompositingForVideoEnabled); + shell.setThreadedCompositingEnabled(threadedCompositingEnabled); + shell.setCompositeToTexture(compositeToTexture); + shell.setForceCompositingMode(forceCompositingMode); + shell.setAccelerated2dCanvasEnabled(accelerated2DCanvasEnabled); + shell.setLegacyAccelerated2dCanvasEnabled(legacyAccelerated2DCanvasEnabled); + shell.setAcceleratedPaintingEnabled(acceleratedPaintingEnabled); + shell.setPerTilePaintingEnabled(perTilePaintingEnabled); + shell.setJavaScriptFlags(javaScriptFlags); + shell.setStressOpt(stressOpt); + shell.setStressDeopt(stressDeopt); + if (noTimeout) { + // 0x20000000ms is big enough for the purpose to avoid timeout in debugging. + shell.setLayoutTestTimeout(0x20000000); + } + if (serverMode && !tests.size()) { +#if OS(ANDROID) + // Send a signal to host to indicate DRT is ready to process commands. + puts("#READY"); + fflush(stdout); +#endif + 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; + // Explicitly quit on platforms where EOF is not reliable. + if (!strcmp(testString, "QUIT")) + break; + runTest(shell, params, testString, testShellMode); + } + } else if (!tests.size()) + puts("#EOF"); + 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(); + } + + return EXIT_SUCCESS; +} diff --git a/Tools/DumpRenderTree/chromium/EventSender.cpp b/Tools/DumpRenderTree/chromium/EventSender.cpp new file mode 100644 index 000000000..9acc42dfb --- /dev/null +++ b/Tools/DumpRenderTree/chromium/EventSender.cpp @@ -0,0 +1,1130 @@ +/* + * 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 "platform/WebDragData.h" +#include "WebDragOperation.h" +#include "platform/WebPoint.h" +#include "platform/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 WebPoint& 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("dumpFilenameBeingDragged", &EventSender::dumpFilenameBeingDragged); + 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("scheduleAsynchronousKeyDown", &EventSender::scheduleAsynchronousKeyDown); + 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("gestureScrollBegin", &EventSender::gestureScrollBegin); + bindMethod("gestureScrollEnd", &EventSender::gestureScrollEnd); + bindMethod("gestureScrollUpdate", &EventSender::gestureScrollUpdate); + bindMethod("gestureTap", &EventSender::gestureTap); + bindMethod("zoomPageIn", &EventSender::zoomPageIn); + bindMethod("zoomPageOut", &EventSender::zoomPageOut); + bindMethod("scalePageBy", &EventSender::scalePageBy); + + // 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(); + m_gestureStartLocation = WebPoint(0, 0); +} + +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, clientPoint, screenPoint, currentDragEffectsAllowed); + + // Finish processing events. + replaySavedEvents(); +} + +void EventSender::dumpFilenameBeingDragged(const CppArgumentList&, CppVariant*) +{ + printf("Filename being dragged: %s\n", currentDragData.fileContentFilename().utf8().data()); +} + +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) +{ + if (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 if ("menu" == codeStr) + // FIXME: Change this to webkit_support::VKEY_APPS. + code = 0x5D; + 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 Tools/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 OS(LINUX) && USE(GTK) + eventDown.nativeKeyCode = webkit_support::NativeKeyCodeForWindowsKeyCode(code, needsShiftKeyModifier); +#endif + + 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/Source/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 + // Tools/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::scalePageBy(const CppArgumentList& arguments, CppVariant* result) +{ + if (arguments.size() < 3 || !arguments[0].isNumber() || !arguments[1].isNumber() || !arguments[2].isNumber()) + return; + + float scaleFactor = static_cast<float>(arguments[0].toDouble()); + int x = arguments[1].toInt32(); + int y = arguments[2].toInt32(); + webview()->setPageScaleFactor(scaleFactor, WebPoint(x, y)); + 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)); +} + +class KeyDownTask : public MethodTask<EventSender> { +public: + KeyDownTask(EventSender* obj, const CppArgumentList& arg) + : MethodTask<EventSender>(obj), m_arguments(arg) { } + virtual void runIfValid() { m_object->keyDown(m_arguments, 0); } + +private: + CppArgumentList m_arguments; +}; + +void EventSender::scheduleAsynchronousKeyDown(const CppArgumentList& arguments, CppVariant* result) +{ + result->setNull(); + postTask(new KeyDownTask(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, 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; + + int lowestId = 0; + for (size_t i = 0; i < touchPoints.size(); i++) { + if (touchPoints[i].id == lowestId) + lowestId++; + } + touchPoint.id = lowestId; + 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::touchesLengthCap) > touchPoints.size()); + webview()->layout(); + + WebTouchEvent touchEvent; + touchEvent.type = type; + touchEvent.modifiers = touchModifiers; + touchEvent.timeStampSeconds = getCurrentEventTimeSec(); + touchEvent.touchesLength = touchPoints.size(); + for (unsigned i = 0; i < touchPoints.size(); ++i) + touchEvent.touches[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(); + int paged = false; + + if (arguments.size() > 2 && arguments[2].isBool()) + paged = arguments[2].toBoolean(); + + 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; + event.scrollByPage = paged; + 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); +} + +void EventSender::gestureScrollBegin(const CppArgumentList& arguments, CppVariant* result) +{ + result->setNull(); + gestureEvent(WebInputEvent::GestureScrollBegin, arguments); +} + +void EventSender::gestureScrollEnd(const CppArgumentList& arguments, CppVariant* result) +{ + result->setNull(); + gestureEvent(WebInputEvent::GestureScrollEnd, arguments); +} + +void EventSender::gestureScrollUpdate(const CppArgumentList& arguments, CppVariant* result) +{ + result->setNull(); + gestureEvent(WebInputEvent::GestureScrollUpdate, arguments); +} + +void EventSender::gestureTap(const CppArgumentList& arguments, CppVariant* result) +{ + result->setNull(); + gestureEvent(WebInputEvent::GestureTap, arguments); +} + +void EventSender::gestureEvent(WebInputEvent::Type type, const CppArgumentList& arguments) +{ + if (arguments.size() < 2 || !arguments[0].isNumber() || !arguments[1].isNumber()) + return; + + WebPoint point(arguments[0].toInt32(), arguments[1].toInt32()); + + WebGestureEvent event; + event.type = type; + + switch (type) { + case WebInputEvent::GestureScrollUpdate: + event.deltaX = static_cast<float>(arguments[0].toDouble()); + event.deltaY = static_cast<float>(arguments[1].toDouble()); + event.x = m_gestureStartLocation.x + event.deltaX; + event.y = m_gestureStartLocation.y + event.deltaY; + break; + + case WebInputEvent::GestureScrollBegin: + m_gestureStartLocation = WebPoint(point.x, point.y); + // Fallthrough + case WebInputEvent::GestureScrollEnd: + case WebInputEvent::GestureTap: + event.x = point.x; + event.y = point.y; + break; + default: + ASSERT_NOT_REACHED(); + } + + event.globalX = event.x; + event.globalY = event.y; + event.timeStampSeconds = getCurrentEventTimeSec(); + webview()->handleInputEvent(event); +} + +// +// 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 000000000..bbecc5488 --- /dev/null +++ b/Tools/DumpRenderTree/chromium/EventSender.h @@ -0,0 +1,179 @@ +/* + * 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 "platform/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); + + // Test helper for dragging out images. + void dumpFilenameBeingDragged(const CppArgumentList&, CppVariant*); + + // 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*); + // FIXME: These aren't really events. They should be moved to layout controller. + void textZoomIn(const CppArgumentList&, CppVariant*); + void textZoomOut(const CppArgumentList&, CppVariant*); + void zoomPageIn(const CppArgumentList&, CppVariant*); + void zoomPageOut(const CppArgumentList&, CppVariant*); + void scalePageBy(const CppArgumentList&, CppVariant*); + + void mouseScrollBy(const CppArgumentList&, CppVariant*); + void continuousMouseScrollBy(const CppArgumentList&, CppVariant*); + void scheduleAsynchronousClick(const CppArgumentList&, CppVariant*); + void scheduleAsynchronousKeyDown(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*); + + void gestureScrollBegin(const CppArgumentList&, CppVariant*); + void gestureScrollEnd(const CppArgumentList&, CppVariant*); + void gestureScrollUpdate(const CppArgumentList&, CppVariant*); + void gestureTap(const CppArgumentList&, CppVariant*); + void gestureEvent(WebKit::WebInputEvent::Type, const CppArgumentList&); + + // 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 the touch point that initiated a gesture. + WebKit::WebPoint m_gestureStartLocation; + + // 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/GamepadController.cpp b/Tools/DumpRenderTree/chromium/GamepadController.cpp new file mode 100644 index 000000000..2974d3bcd --- /dev/null +++ b/Tools/DumpRenderTree/chromium/GamepadController.cpp @@ -0,0 +1,171 @@ +/* + * Copyright (C) 2011 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 "GamepadController.h" + +#include "TestShell.h" + +using namespace WebKit; + +GamepadController::GamepadController(TestShell* shell) + : m_shell(shell) +{ + bindMethod("connect", &GamepadController::connect); + bindMethod("disconnect", &GamepadController::disconnect); + bindMethod("setId", &GamepadController::setId); + bindMethod("setButtonCount", &GamepadController::setButtonCount); + bindMethod("setButtonData", &GamepadController::setButtonData); + bindMethod("setAxisCount", &GamepadController::setAxisCount); + bindMethod("setAxisData", &GamepadController::setAxisData); + + bindFallbackMethod(&GamepadController::fallbackCallback); + + reset(); +} + +void GamepadController::bindToJavascript(WebFrame* frame, const WebString& classname) +{ + CppBoundClass::bindToJavascript(frame, classname); +} + +void GamepadController::reset() +{ + memset(&internalData, 0, sizeof(internalData)); +} + +void GamepadController::connect(const CppArgumentList& args, CppVariant* result) +{ + if (args.size() < 1) { + printf("Invalid args"); + return; + } + int index = args[0].toInt32(); + internalData.items[index].connected = true; + internalData.length = 0; + for (unsigned i = 0; i < WebKit::WebGamepads::itemsLengthCap; ++i) + if (internalData.items[i].connected) + internalData.length = i + 1; + webkit_support::SetGamepadData(internalData); + result->setNull(); +} + +void GamepadController::disconnect(const CppArgumentList& args, CppVariant* result) +{ + if (args.size() < 1) { + printf("Invalid args"); + return; + } + int index = args[0].toInt32(); + internalData.items[index].connected = false; + internalData.length = 0; + for (unsigned i = 0; i < WebKit::WebGamepads::itemsLengthCap; ++i) + if (internalData.items[i].connected) + internalData.length = i + 1; + webkit_support::SetGamepadData(internalData); + result->setNull(); +} + +void GamepadController::setId(const CppArgumentList& args, CppVariant* result) +{ + if (args.size() < 2) { + printf("Invalid args"); + return; + } + int index = args[0].toInt32(); + std::string src = args[1].toString(); + const char* p = src.c_str(); + memset(internalData.items[index].id, 0, sizeof(internalData.items[index].id)); + for (unsigned i = 0; *p && i < WebKit::WebGamepad::idLengthCap - 1; ++i) + internalData.items[index].id[i] = *p++; + webkit_support::SetGamepadData(internalData); + result->setNull(); +} + +void GamepadController::setButtonCount(const CppArgumentList& args, CppVariant* result) +{ + if (args.size() < 2) { + printf("Invalid args"); + return; + } + int index = args[0].toInt32(); + int buttons = args[1].toInt32(); + internalData.items[index].buttonsLength = buttons; + webkit_support::SetGamepadData(internalData); + result->setNull(); +} + +void GamepadController::setButtonData(const CppArgumentList& args, CppVariant* result) +{ + if (args.size() < 3) { + printf("Invalid args"); + return; + } + int index = args[0].toInt32(); + int button = args[1].toInt32(); + double data = args[2].toDouble(); + internalData.items[index].buttons[button] = data; + webkit_support::SetGamepadData(internalData); + result->setNull(); +} + +void GamepadController::setAxisCount(const CppArgumentList& args, CppVariant* result) +{ + if (args.size() < 2) { + printf("Invalid args"); + return; + } + int index = args[0].toInt32(); + int axes = args[1].toInt32(); + internalData.items[index].axesLength = axes; + webkit_support::SetGamepadData(internalData); + result->setNull(); +} + +void GamepadController::setAxisData(const CppArgumentList& args, CppVariant* result) +{ + if (args.size() < 3) { + printf("Invalid args"); + return; + } + int index = args[0].toInt32(); + int axis = args[1].toInt32(); + double data = args[2].toDouble(); + internalData.items[index].axes[axis] = data; + webkit_support::SetGamepadData(internalData); + result->setNull(); +} + +void GamepadController::fallbackCallback(const CppArgumentList&, CppVariant* result) +{ + printf("CONSOLE MESSAGE: JavaScript ERROR: unknown method called on " + "GamepadController\n"); + result->setNull(); +} diff --git a/Tools/DumpRenderTree/chromium/GamepadController.h b/Tools/DumpRenderTree/chromium/GamepadController.h new file mode 100644 index 000000000..414228855 --- /dev/null +++ b/Tools/DumpRenderTree/chromium/GamepadController.h @@ -0,0 +1,67 @@ +/* + * Copyright (C) 2011 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 GamepadController_h +#define GamepadController_h + +#include "CppBoundClass.h" +#include "platform/WebGamepads.h" + +namespace WebKit { +class WebGamepads; +class WebFrame; +} + +class TestShell; + +class GamepadController : public CppBoundClass { +public: + explicit GamepadController(TestShell*); + + void bindToJavascript(WebKit::WebFrame*, const WebKit::WebString& classname); + void reset(); + +private: + // Bound methods and properties + void connect(const CppArgumentList&, CppVariant*); + void disconnect(const CppArgumentList&, CppVariant*); + void setId(const CppArgumentList&, CppVariant*); + void setButtonCount(const CppArgumentList&, CppVariant*); + void setButtonData(const CppArgumentList&, CppVariant*); + void setAxisCount(const CppArgumentList&, CppVariant*); + void setAxisData(const CppArgumentList&, CppVariant*); + void fallbackCallback(const CppArgumentList&, CppVariant*); + + TestShell* m_shell; + + WebKit::WebGamepads internalData; +}; + +#endif // GamepadController_h diff --git a/Tools/DumpRenderTree/chromium/ImageDiff.cpp b/Tools/DumpRenderTree/chromium/ImageDiff.cpp new file mode 100644 index 000000000..966554bab --- /dev/null +++ b/Tools/DumpRenderTree/chromium/ImageDiff.cpp @@ -0,0 +1,521 @@ +/* + * 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 <iterator> +#include <limits.h> +#include <stdio.h> +#include <string.h> +#include <vector> + +#if OS(WINDOWS) +#include <windows.h> +#define PATH_MAX MAX_PATH +#endif + +// Define macro here to make ImageDiff independent of JavaScriptCore. +#ifdef NDEBUG +#define ASSERT(assertion) do { } while (0) +#else +#define ASSERT(assertion) do \ + if (!(assertion)) { \ + fprintf(stderr, "ASSERT failed at %s:%d: " #assertion ".", __FILE__, __LINE__); \ + exit(1); \ + } \ +while (0) +#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"; + +// If --diff is passed, causes the app to output the image difference +// metric (percentageDifferent()) on stdout. +static const char optionWrite[] = "--write-image-diff-metrics"; + +// Use weightedPercentageDifferent() instead of the default image +// comparator proc. +static const char optionWeightedIntensity[] = "--weighted-intensity"; + +// 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 unsigned int rgbaRed = 0x000000ff; +static const unsigned int 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 createFromStdin(size_t byteLength) + { + if (!byteLength) + return false; + + unsigned char* source = new unsigned char[byteLength]; + if (fread(source, 1, byteLength, stdin) != byteLength) { + delete [] source; + return false; + } + + if (!webkit_support::DecodePNG(source, byteLength, &m_data, &m_width, &m_height)) { + delete [] source; + clear(); + return false; + } + delete [] source; + 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 unsigned int pixelAt(int x, int y) const + { + ASSERT(x >= 0 && x < m_width); + ASSERT(y >= 0 && y < m_height); + return *reinterpret_cast<const unsigned int*>(&(m_data[(y * m_width + x) * 4])); + } + + void setPixelAt(int x, int y, unsigned int 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<unsigned int*>(addr) = color; + } + +private: + // pixel dimensions of the image + int m_width, m_height; + + vector<unsigned char> m_data; +}; + +typedef float (*ImageComparisonProc) (const Image&, const Image&); + +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; +} + +inline unsigned int maxOf3(unsigned int a, unsigned int b, unsigned int c) +{ + if (a < b) + return std::max(b, c); + return std::max(a, c); +} + +inline unsigned int getRedComponent(unsigned int color) +{ + return (color << 24) >> 24; +} + +inline unsigned int getGreenComponent(unsigned int color) +{ + return (color << 16) >> 24; +} + +inline unsigned int getBlueComponent(unsigned int color) +{ + return (color << 8) >> 24; +} + +/// Rank small-pixel-count high-intensity changes as more important than +/// large-pixel-count low-intensity changes. +float weightedPercentageDifferent(const Image& baseline, const Image& actual) +{ + int w = min(baseline.width(), actual.width()); + int h = min(baseline.height(), actual.height()); + + float weightedPixelsDifferent = 0; + for (int y = 0; y < h; ++y) { + for (int x = 0; x < w; ++x) { + unsigned int actualColor = actual.pixelAt(x, y); + unsigned int baselineColor = baseline.pixelAt(x, y); + if (baselineColor != actualColor) { + unsigned int actualR = getRedComponent(actualColor); + unsigned int actualG = getGreenComponent(actualColor); + unsigned int actualB = getBlueComponent(actualColor); + unsigned int baselineR = getRedComponent(baselineColor); + unsigned int baselineG = getGreenComponent(baselineColor); + unsigned int baselineB = getBlueComponent(baselineColor); + unsigned int deltaR = std::max(actualR, baselineR) + - std::min(actualR, baselineR); + unsigned int deltaG = std::max(actualG, baselineG) + - std::min(actualG, baselineG); + unsigned int deltaB = std::max(actualB, baselineB) + - std::min(actualB, baselineB); + weightedPixelsDifferent += + static_cast<float>(maxOf3(deltaR, deltaG, deltaB)) / 255; + } + } + } + + int maxWidth = max(baseline.width(), actual.width()); + int maxHeight = max(baseline.height(), actual.height()); + + weightedPixelsDifferent += (maxWidth - w) * h; + + weightedPixelsDifferent += (maxHeight - h) * maxWidth; + + float totalPixels = static_cast<float>(actual.width()) + * static_cast<float>(actual.height()); + if (!totalPixels) + return 100.0f; + return weightedPixelsDifferent / 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" + " --write-image-diff-metrics prints a difference metric to stdout\n" + " --weighted-intensity weights the difference metric by intensity\n" + " at each pixel\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, + ImageComparisonProc comparator) +{ + 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 = (*comparator)(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(ImageComparisonProc comparator) +{ + 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.createFromStdin(imageSize)) { + fputs("Error, input image can't be decoded.\n", stderr); + return 1; + } + } else if (imageSize > 0 && !baselineImage.hasImage()) { + if (!baselineImage.createFromStdin(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 = (*comparator)(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) { + unsigned int 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. + unsigned int alpha = basePixel & rgbaAlpha; + unsigned int 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, + bool shouldWritePercentages, ImageComparisonProc comparator) +{ + 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; + + if (shouldWritePercentages) { + float percent = (*comparator)(actualImage, baselineImage); + fprintf(stdout, "%.3f\n", percent); + } + + return statusDifferent; +} + +int main(int argc, const char* argv[]) +{ + std::vector<const char*> values; + bool pollStdin = false; + bool generateDiff = false; + bool shouldWritePercentages = false; + ImageComparisonProc comparator = percentageDifferent; + for (int i = 1; i < argc; ++i) { + if (!strcmp(argv[i], optionPollStdin)) + pollStdin = true; + else if (!strcmp(argv[i], optionGenerateDiff)) + generateDiff = true; + else if (!strcmp(argv[i], optionWrite)) + shouldWritePercentages = true; + else if (!strcmp(argv[i], optionWeightedIntensity)) + comparator = weightedPercentageDifferent; + else + values.push_back(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, + comparator) == 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], + shouldWritePercentages, comparator); + } else if (values.size() == 2) + return compareImages(argv[1], argv[2], comparator); + + printHelp(); + return statusError; +} diff --git a/Tools/DumpRenderTree/chromium/LayoutTestController.cpp b/Tools/DumpRenderTree/chromium/LayoutTestController.cpp new file mode 100644 index 000000000..7fc21362f --- /dev/null +++ b/Tools/DumpRenderTree/chromium/LayoutTestController.cpp @@ -0,0 +1,2152 @@ +/* + * 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 "platform/WebData.h" +#include "WebDeviceOrientation.h" +#include "WebDeviceOrientationClientMock.h" +#include "WebDocument.h" +#include "WebElement.h" +#include "WebFindOptions.h" +#include "WebFrame.h" +#include "WebGeolocationClientMock.h" +#include "WebIDBFactory.h" +#include "WebInputElement.h" +#include "WebKit.h" +#include "WebNotificationPresenter.h" +#include "WebPermissions.h" +#include "WebScriptSource.h" +#include "WebSecurityPolicy.h" +#include "WebSettings.h" +#include "platform/WebSize.h" +#include "WebSpeechInputControllerMock.h" +#include "platform/WebURL.h" +#include "WebView.h" +#include "WebViewHost.h" +#include "webkit/support/webkit_support.h" +#include <algorithm> +#include <cctype> +#include <clocale> +#include <cstdlib> +#include <limits> +#include <sstream> +#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_showDebugLayerTree(false) + , m_workQueue(this) + , m_shouldStayOnPageAfterHandlingBeforeUnload(false) +{ + + // 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("addMockSpeechInputResult", &LayoutTestController::addMockSpeechInputResult); + 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("displayInvalidatedRegion", &LayoutTestController::displayInvalidatedRegion); + 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("dumpProgressFinishedCallback", &LayoutTestController::dumpProgressFinishedCallback); + 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("dumpPermissionClientCallbacks", &LayoutTestController::dumpPermissionClientCallbacks); + bindMethod("dumpCreateView", &LayoutTestController::dumpCreateView); + bindMethod("elementDoesAutoCompleteForElementWithId", &LayoutTestController::elementDoesAutoCompleteForElementWithId); + bindMethod("evaluateInWebInspector", &LayoutTestController::evaluateInWebInspector); + bindMethod("evaluateScriptInIsolatedWorld", &LayoutTestController::evaluateScriptInIsolatedWorld); + bindMethod("setIsolatedWorldSecurityOrigin", &LayoutTestController::setIsolatedWorldSecurityOrigin); + bindMethod("execCommand", &LayoutTestController::execCommand); + bindMethod("forceRedSelectionColors", &LayoutTestController::forceRedSelectionColors); + bindMethod("grantDesktopNotificationPermission", &LayoutTestController::grantDesktopNotificationPermission); + bindMethod("hasSpellingMarker", &LayoutTestController::hasSpellingMarker); + bindMethod("findString", &LayoutTestController::findString); + bindMethod("isCommandEnabled", &LayoutTestController::isCommandEnabled); + bindMethod("hasCustomPageSizeStyle", &LayoutTestController::hasCustomPageSizeStyle); + bindMethod("isPageBoxVisible", &LayoutTestController::isPageBoxVisible); + bindMethod("layerTreeAsText", &LayoutTestController::layerTreeAsText); + bindMethod("loseCompositorContext", &LayoutTestController::loseCompositorContext); + bindMethod("markerTextForListItem", &LayoutTestController::markerTextForListItem); + bindMethod("notifyDone", &LayoutTestController::notifyDone); + bindMethod("numberOfActiveAnimations", &LayoutTestController::numberOfActiveAnimations); + bindMethod("numberOfPages", &LayoutTestController::numberOfPages); + bindMethod("numberOfPendingGeolocationPermissionRequests", &LayoutTestController:: numberOfPendingGeolocationPermissionRequests); + bindMethod("objCIdentityIsEqual", &LayoutTestController::objCIdentityIsEqual); + bindMethod("overridePreference", &LayoutTestController::overridePreference); + bindMethod("pageNumberForElementById", &LayoutTestController::pageNumberForElementById); + bindMethod("pageProperty", &LayoutTestController::pageProperty); + bindMethod("pageSizeAndMarginsInPixels", &LayoutTestController::pageSizeAndMarginsInPixels); + 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("resetPageVisibility", &LayoutTestController::resetPageVisibility); + bindMethod("resumeAnimations", &LayoutTestController::resumeAnimations); + bindMethod("sampleSVGAnimationForElementAtTime", &LayoutTestController::sampleSVGAnimationForElementAtTime); + bindMethod("setAcceptsEditing", &LayoutTestController::setAcceptsEditing); + bindMethod("setAllowDisplayOfInsecureContent", &LayoutTestController::setAllowDisplayOfInsecureContent); + bindMethod("setAllowFileAccessFromFileURLs", &LayoutTestController::setAllowFileAccessFromFileURLs); + bindMethod("setAllowRunningOfInsecureContent", &LayoutTestController::setAllowRunningOfInsecureContent); + bindMethod("setAllowUniversalAccessFromFileURLs", &LayoutTestController::setAllowUniversalAccessFromFileURLs); + bindMethod("setAlwaysAcceptCookies", &LayoutTestController::setAlwaysAcceptCookies); + bindMethod("setAuthorAndUserStylesEnabled", &LayoutTestController::setAuthorAndUserStylesEnabled); + bindMethod("setAutofilled", &LayoutTestController::setAutofilled); + 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("setAudioData", &LayoutTestController::setAudioData); + bindMethod("setGeolocationPermission", &LayoutTestController::setGeolocationPermission); + bindMethod("setIconDatabaseEnabled", &LayoutTestController::setIconDatabaseEnabled); + bindMethod("setJavaScriptCanAccessClipboard", &LayoutTestController::setJavaScriptCanAccessClipboard); + bindMethod("setJavaScriptProfilingEnabled", &LayoutTestController::setJavaScriptProfilingEnabled); + bindMethod("setMinimumTimerInterval", &LayoutTestController::setMinimumTimerInterval); + bindMethod("setMockDeviceOrientation", &LayoutTestController::setMockDeviceOrientation); + bindMethod("setMockGeolocationError", &LayoutTestController::setMockGeolocationError); + bindMethod("setMockGeolocationPosition", &LayoutTestController::setMockGeolocationPosition); + bindMethod("setPageVisibility", &LayoutTestController::setPageVisibility); + bindMethod("setPluginsEnabled", &LayoutTestController::setPluginsEnabled); + bindMethod("setPopupBlockingEnabled", &LayoutTestController::setPopupBlockingEnabled); + bindMethod("setPOSIXLocale", &LayoutTestController::setPOSIXLocale); + bindMethod("setPrinting", &LayoutTestController::setPrinting); + bindMethod("setScrollbarPolicy", &LayoutTestController::setScrollbarPolicy); + bindMethod("setSelectTrailingWhitespaceEnabled", &LayoutTestController::setSelectTrailingWhitespaceEnabled); + bindMethod("setSmartInsertDeleteEnabled", &LayoutTestController::setSmartInsertDeleteEnabled); + bindMethod("setStopProvisionalFrameLoads", &LayoutTestController::setStopProvisionalFrameLoads); + bindMethod("setTabKeyCyclesThroughElements", &LayoutTestController::setTabKeyCyclesThroughElements); + bindMethod("setUserStyleSheetEnabled", &LayoutTestController::setUserStyleSheetEnabled); + bindMethod("setUserStyleSheetLocation", &LayoutTestController::setUserStyleSheetLocation); + bindMethod("setValueForUser", &LayoutTestController::setValueForUser); + 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("startSpeechInput", &LayoutTestController::startSpeechInput); + bindMethod("suspendAnimations", &LayoutTestController::suspendAnimations); + bindMethod("testRepaint", &LayoutTestController::testRepaint); + bindMethod("waitForPolicyDelegate", &LayoutTestController::waitForPolicyDelegate); + bindMethod("waitUntilDone", &LayoutTestController::waitUntilDone); + bindMethod("windowCount", &LayoutTestController::windowCount); + bindMethod("setTextDirection", &LayoutTestController::setTextDirection); + bindMethod("setImagesAllowed", &LayoutTestController::setImagesAllowed); + bindMethod("setScriptsAllowed", &LayoutTestController::setScriptsAllowed); + bindMethod("setStorageAllowed", &LayoutTestController::setStorageAllowed); + bindMethod("setPluginsAllowed", &LayoutTestController::setPluginsAllowed); + + // The following are stubs. + bindMethod("abortModal", &LayoutTestController::abortModal); + bindMethod("accessStoredWebScriptObject", &LayoutTestController::accessStoredWebScriptObject); + bindMethod("addDisallowedURL", &LayoutTestController::addDisallowedURL); + bindMethod("applicationCacheDiskUsageForOrigin", &LayoutTestController::applicationCacheDiskUsageForOrigin); + bindMethod("callShouldCloseOnWebView", &LayoutTestController::callShouldCloseOnWebView); + bindMethod("clearAllApplicationCaches", &LayoutTestController::clearAllApplicationCaches); + bindMethod("clearApplicationCacheForOrigin", &LayoutTestController::clearApplicationCacheForOrigin); + 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); + bindMethod("deleteAllLocalStorage", &LayoutTestController::deleteAllLocalStorage); + bindMethod("localStorageDiskUsageForOrigin", &LayoutTestController::localStorageDiskUsageForOrigin); + bindMethod("originsWithLocalStorage", &LayoutTestController::originsWithLocalStorage); + bindMethod("deleteLocalStorageForOrigin", &LayoutTestController::deleteLocalStorageForOrigin); + bindMethod("observeStorageTrackerNotifications", &LayoutTestController::observeStorageTrackerNotifications); + bindMethod("syncLocalStorage", &LayoutTestController::syncLocalStorage); + bindMethod("setShouldStayOnPageAfterHandlingBeforeUnload", &LayoutTestController::setShouldStayOnPageAfterHandlingBeforeUnload); + bindMethod("enableFixedLayoutMode", &LayoutTestController::enableFixedLayoutMode); + bindMethod("setFixedLayoutSize", &LayoutTestController::setFixedLayoutSize); + + // 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); + bindProperty("titleTextDirection", &m_titleTextDirection); + bindProperty("platformName", &m_platformName); + bindProperty("interceptPostMessage", &m_interceptPostMessage); +} + +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::dumpProgressFinishedCallback(const CppArgumentList&, CppVariant* result) +{ + m_dumpProgressFinishedCallback = 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::dumpPermissionClientCallbacks(const CppArgumentList&, CppVariant* result) +{ + m_dumpPermissionClientCallbacks = true; + result->setNull(); +} + +void LayoutTestController::dumpCreateView(const CppArgumentList&, CppVariant* result) +{ + m_dumpCreateView = 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()->document().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) { } + WorkItemLoadHTMLString(const std::string& html, const WebURL& baseURL, const WebURL& unreachableURL) + : m_html(html) + , m_baseURL(baseURL) + , m_unreachableURL(unreachableURL) { } + bool run(TestShell* shell) + { + shell->webView()->mainFrame()->loadHTMLString( + WebKit::WebData(m_html.data(), m_html.length()), m_baseURL, m_unreachableURL); + return true; + } + +private: + std::string m_html; + WebURL m_baseURL; + WebURL m_unreachableURL; +}; + +void LayoutTestController::queueLoadHTMLString(const CppArgumentList& arguments, CppVariant* result) +{ + if (arguments.size() > 0 && arguments[0].isString()) { + string html = arguments[0].toString(); + WebURL baseURL(GURL("")); + if (arguments.size() > 1 && arguments[1].isString()) + baseURL = WebURL(GURL(arguments[1].toString())); + if (arguments.size() > 2 && arguments[2].isString()) + m_workQueue.addWork(new WorkItemLoadHTMLString(html, baseURL, WebURL(GURL(arguments[2].toString())))); + else + 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_dumpAsAudio = false; + m_dumpCreateView = false; + m_dumpEditingCallbacks = false; + m_dumpFrameLoadCallbacks = false; + m_dumpProgressFinishedCallback = 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_dumpPermissionClientCallbacks = 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_titleTextDirection.set("ltr"); + m_platformName.set("chromium"); + m_interceptPostMessage.set(false); + m_userStyleSheetLocation = WebURL(); + m_isPrinting = false; + + 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(); + m_shouldStayOnPageAfterHandlingBeforeUnload = false; +} + +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& arguments, CppVariant* result) +{ + if (arguments.size() > 0 && arguments[0].isBool()) + m_shell->webView()->settings()->setAsynchronousSpellCheckingEnabled(cppVariantToBool(arguments[0])); + result->setNull(); +} + +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::setImagesAllowed(const CppArgumentList& arguments, CppVariant* result) +{ + if (arguments.size() > 0 && arguments[0].isBool()) + m_shell->webPermissions()->setImagesAllowed(arguments[0].toBoolean()); + result->setNull(); +} + +void LayoutTestController::setScriptsAllowed(const CppArgumentList& arguments, CppVariant* result) +{ + if (arguments.size() > 0 && arguments[0].isBool()) + m_shell->webPermissions()->setScriptsAllowed(arguments[0].toBoolean()); + result->setNull(); +} + +void LayoutTestController::setStorageAllowed(const CppArgumentList& arguments, CppVariant* result) +{ + if (arguments.size() > 0 && arguments[0].isBool()) + m_shell->webPermissions()->setStorageAllowed(arguments[0].toBoolean()); + result->setNull(); +} + +void LayoutTestController::setPluginsAllowed(const CppArgumentList& arguments, CppVariant* result) +{ + if (arguments.size() > 0 && arguments[0].isBool()) + m_shell->webPermissions()->setPluginsAllowed(arguments[0].toBoolean()); + 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::clearApplicationCacheForOrigin(const CppArgumentList&, CppVariant* result) +{ + // FIXME: Implement to support deleting all application cache for an origin. + result->setNull(); +} + +void LayoutTestController::setApplicationCacheOriginQuota(const CppArgumentList&, CppVariant* result) +{ + // FIXME: Implement to support application cache quotas. + result->setNull(); +} + +void LayoutTestController::originsWithApplicationCache(const CppArgumentList&, CppVariant* result) +{ + // FIXME: Implement to support getting origins that have application caches. + result->setNull(); +} + +void LayoutTestController::applicationCacheDiskUsageForOrigin(const CppArgumentList&, CppVariant* result) +{ + // FIXME: Implement to support getting disk usage by all application cache for an origin. + 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 = adoptArrayPtr(new WCHAR[bufferSize]); + DWORD tempLength = ::GetTempPathW(bufferSize, tempPath.get()); + if (tempLength + url.length() - tempPrefixLength + 1 > bufferSize) { + bufferSize = tempLength + url.length() - tempPrefixLength + 1; + tempPath = adoptArrayPtr(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; + } +#if ENABLE(NOTIFICATIONS) + m_shell->notificationPresenter()->grantPermission(cppVariantToWebString(arguments[0])); +#endif + result->set(true); +} + +void LayoutTestController::simulateDesktopNotificationClick(const CppArgumentList& arguments, CppVariant* result) +{ + if (arguments.size() != 1 || !arguments[0].isString()) { + result->set(false); + return; + } +#if ENABLE(NOTIFICATIONS) + if (m_shell->notificationPresenter()->simulateClick(cppVariantToWebString(arguments[0]))) + result->set(true); + else +#endif + 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::displayInvalidatedRegion(const CppArgumentList& arguments, CppVariant* result) +{ + WebViewHost* host = m_shell->webViewHost(); + 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::setIsolatedWorldSecurityOrigin(const CppArgumentList& arguments, CppVariant* result) +{ + result->setNull(); + + if (arguments.size() != 2 || !arguments[0].isNumber() || !arguments[1].isString()) + return; + + m_shell->webView()->focusedFrame()->setIsolatedWorldSecurityOrigin( + arguments[0].toInt32(), + WebSecurityOrigin::createFromString(cppVariantToWebString(arguments[1]))); +} + +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::setAllowDisplayOfInsecureContent(const CppArgumentList& arguments, CppVariant* result) +{ + if (arguments.size() > 0 && arguments[0].isBool()) + m_shell->webPermissions()->setDisplayingInsecureContentAllowed(arguments[0].toBoolean()); + + 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(); +} + +void LayoutTestController::setAllowRunningOfInsecureContent(const CppArgumentList& arguments, CppVariant* result) +{ + if (arguments.size() > 0 && arguments[0].isBool()) + m_shell->webPermissions()->setRunningInsecureContentAllowed(arguments[0].value.boolValue); + + result->setNull(); +} + +// Need these conversions because the format of the value for booleans +// may vary - for example, on mac "1" and "0" are used for boolean. +bool LayoutTestController::cppVariantToBool(const CppVariant& value) +{ + if (value.isBool()) + return value.toBoolean(); + if (value.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()); +} + +Vector<WebString> LayoutTestController::cppVariantToWebStringArray(const CppVariant& value) +{ + if (!value.isObject()) { + logErrorToConsole("Invalid value for preference. Expected object value."); + return Vector<WebString>(); + } + Vector<WebString> resultVector; + Vector<string> stringVector = value.toStringVector(); + for (size_t i = 0; i < stringVector.size(); ++i) + resultVector.append(WebString::fromUTF8(stringVector[i].c_str())); + return resultVector; +} + +// Sets map based on scriptFontPairs, a collapsed vector of pairs of ISO 15924 +// four-letter script code and font such as: +// { "Arab", "My Arabic Font", "Grek", "My Greek Font" } +static void setFontMap(WebPreferences::ScriptFontFamilyMap& map, const Vector<WebString>& scriptFontPairs) +{ + map.clear(); + size_t i = 0; + while (i + 1 < scriptFontPairs.size()) { + const WebString& script = scriptFontPairs[i++]; + const WebString& font = scriptFontPairs[i++]; + + int32_t code = u_getPropertyValueEnum(UCHAR_SCRIPT, script.utf8().data()); + if (code >= 0 && code < USCRIPT_CODE_LIMIT) + map.set(static_cast<int>(code), font); + } +} + +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 == "WebKitStandardFontMap") + setFontMap(prefs->standardFontMap, cppVariantToWebStringArray(value)); + else if (key == "WebKitFixedFontMap") + setFontMap(prefs->fixedFontMap, cppVariantToWebStringArray(value)); + else if (key == "WebKitSerifFontMap") + setFontMap(prefs->serifFontMap, cppVariantToWebStringArray(value)); + else if (key == "WebKitSansSerifFontMap") + setFontMap(prefs->sansSerifFontMap, cppVariantToWebStringArray(value)); + else if (key == "WebKitCursiveFontMap") + setFontMap(prefs->cursiveFontMap, cppVariantToWebStringArray(value)); + else if (key == "WebKitFantasyFontMap") + setFontMap(prefs->fantasyFontMap, cppVariantToWebStringArray(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 == "WebKitPageCacheSupportsPluginsPreferenceKey") + prefs->pageCacheSupportsPlugins = 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 if (key == "WebKitAllowDisplayingInsecureContent") + prefs->allowDisplayOfInsecureContent = cppVariantToBool(value); + else if (key == "WebKitAllowRunningInsecureContent") + prefs->allowRunningOfInsecureContent = cppVariantToBool(value); + else if (key == "WebKitHixie76WebSocketProtocolEnabled") + prefs->hixie76WebSocketProtocolEnabled = cppVariantToBool(value); + else if (key == "WebKitWebAudioEnabled") { + ASSERT(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()); +} + +// Parse a single argument. The method returns true if there is an argument that +// is a number or if there is no argument at all. It returns false only if there +// is some argument that is not a number. The value parameter is filled with the +// parsed number, or given the default if there is no argument. +static bool parseCppArgumentInt32(const CppArgumentList& arguments, int argIndex, int* value, int defaultValue) +{ + if (static_cast<int>(arguments.size()) > argIndex) { + if (!arguments[argIndex].isNumber()) + return false; + *value = arguments[argIndex].toInt32(); + return true; + } + *value = defaultValue; + return true; +} + +static bool parsePageSizeParameters(const CppArgumentList& arguments, + int argOffset, + int* pageWidthInPixels, + int* pageHeightInPixels) +{ + // WebKit is using the window width/height of DumpRenderTree as the + // default value of the page size. + // FIXME: share the default values with other ports. + int argCount = static_cast<int>(arguments.size()) - argOffset; + if (argCount && argCount != 2) + return false; + if (!parseCppArgumentInt32(arguments, argOffset, pageWidthInPixels, 800) + || !parseCppArgumentInt32(arguments, argOffset + 1, pageHeightInPixels, 600)) + return false; + return true; +} + +static bool parsePageNumber(const CppArgumentList& arguments, int argOffset, int* pageNumber) +{ + if (static_cast<int>(arguments.size()) > argOffset + 1) + return false; + if (!parseCppArgumentInt32(arguments, argOffset, pageNumber, 0)) + return false; + return true; +} + +static bool parsePageNumberSizeMargins(const CppArgumentList& arguments, int argOffset, + int* pageNumber, int* width, int* height, + int* marginTop, int* marginRight, int* marginBottom, int* marginLeft) +{ + int argCount = static_cast<int>(arguments.size()) - argOffset; + if (argCount && argCount != 7) + return false; + if (!parseCppArgumentInt32(arguments, argOffset, pageNumber, 0) + || !parseCppArgumentInt32(arguments, argOffset + 1, width, 0) + || !parseCppArgumentInt32(arguments, argOffset + 2, height, 0) + || !parseCppArgumentInt32(arguments, argOffset + 3, marginTop, 0) + || !parseCppArgumentInt32(arguments, argOffset + 4, marginRight, 0) + || !parseCppArgumentInt32(arguments, argOffset + 5, marginBottom, 0) + || !parseCppArgumentInt32(arguments, argOffset + 6, marginLeft, 0)) + return false; + return true; +} + +void LayoutTestController::setPrinting(const CppArgumentList& arguments, CppVariant* result) +{ + setIsPrinting(true); + result->setNull(); +} + +void LayoutTestController::pageNumberForElementById(const CppArgumentList& arguments, CppVariant* result) +{ + result->setNull(); + int pageWidthInPixels = 0; + int 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]), + static_cast<float>(pageWidthInPixels), + static_cast<float>(pageHeightInPixels))); +} + +void LayoutTestController::pageSizeAndMarginsInPixels(const CppArgumentList& arguments, CppVariant* result) +{ + result->set(""); + int pageNumber = 0; + int width = 0; + int height = 0; + int marginTop = 0; + int marginRight = 0; + int marginBottom = 0; + int marginLeft = 0; + if (!parsePageNumberSizeMargins(arguments, 0, &pageNumber, &width, &height, &marginTop, &marginRight, &marginBottom, + &marginLeft)) + return; + + WebFrame* frame = m_shell->webView()->mainFrame(); + if (!frame) + return; + WebSize pageSize(width, height); + frame->pageSizeAndMarginsInPixels(pageNumber, pageSize, marginTop, marginRight, marginBottom, marginLeft); + stringstream resultString; + resultString << "(" << pageSize.width << ", " << pageSize.height << ") " << marginTop << " " << marginRight << " " + << marginBottom << " " << marginLeft; + result->set(resultString.str()); +} + +void LayoutTestController::hasCustomPageSizeStyle(const CppArgumentList& arguments, CppVariant* result) +{ + result->set(false); + int pageIndex = 0; + if (!parsePageNumber(arguments, 0, &pageIndex)) + return; + WebFrame* frame = m_shell->webView()->mainFrame(); + if (!frame) + return; + result->set(frame->hasCustomPageSizeStyle(pageIndex)); +} + +void LayoutTestController::isPageBoxVisible(const CppArgumentList& arguments, CppVariant* result) +{ + result->setNull(); + int pageNumber = 0; + if (!parsePageNumber(arguments, 0, &pageNumber)) + return; + WebFrame* frame = m_shell->webView()->mainFrame(); + if (!frame) + return; + result->set(frame->isPageBoxVisible(pageNumber)); +} + +void LayoutTestController::pageProperty(const CppArgumentList& arguments, CppVariant* result) +{ + result->set(""); + int pageNumber = 0; + if (!parsePageNumber(arguments, 1, &pageNumber)) + return; + if (!arguments[0].isString()) + return; + WebFrame* frame = m_shell->webView()->mainFrame(); + if (!frame) + return; + WebSize pageSize(800, 800); + frame->printBegin(pageSize); + result->set(frame->pageProperty(cppVariantToWebString(arguments[0]), pageNumber).utf8()); + frame->printEnd(); +} + +void LayoutTestController::numberOfPages(const CppArgumentList& arguments, CppVariant* result) +{ + result->setNull(); + int pageWidthInPixels = 0; + int 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::numberOfPendingGeolocationPermissionRequests(const CppArgumentList& arguments, CppVariant* result) +{ + result->setNull(); + Vector<WebViewHost*> windowList = m_shell->windowList(); + int numberOfRequests = 0; + for (size_t i = 0; i < windowList.size(); i++) + numberOfRequests += windowList[i]->geolocationClientMock()->numberOfPendingPermissionRequests(); + result->set(numberOfRequests); +} + +void LayoutTestController::logErrorToConsole(const std::string& text) +{ + m_shell->webViewHost()->didAddMessageToConsole( + WebConsoleMessage(WebConsoleMessage::LevelError, WebString::fromUTF8(text)), + WebString(), 0); +} + +void LayoutTestController::setJavaScriptProfilingEnabled(const CppArgumentList& arguments, CppVariant* result) +{ + result->setNull(); + if (arguments.size() < 1 || !arguments[0].isBool()) + return; + m_shell->drtDevToolsAgent()->setJavaScriptProfilingEnabled(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); +} + +// FIXME: For greater test flexibility, we should be able to set each page's geolocation mock individually. +// https://bugs.webkit.org/show_bug.cgi?id=52368 +void LayoutTestController::setGeolocationPermission(const CppArgumentList& arguments, CppVariant* result) +{ + result->setNull(); + if (arguments.size() < 1 || !arguments[0].isBool()) + return; + Vector<WebViewHost*> windowList = m_shell->windowList(); + for (size_t i = 0; i < windowList.size(); i++) + windowList[i]->geolocationClientMock()->setPermission(arguments[0].toBoolean()); +} + +void LayoutTestController::setMockGeolocationPosition(const CppArgumentList& arguments, CppVariant* result) +{ + result->setNull(); + if (arguments.size() < 3 || !arguments[0].isNumber() || !arguments[1].isNumber() || !arguments[2].isNumber()) + return; + Vector<WebViewHost*> windowList = m_shell->windowList(); + for (size_t i = 0; i < windowList.size(); i++) + windowList[i]->geolocationClientMock()->setPosition(arguments[0].toDouble(), arguments[1].toDouble(), arguments[2].toDouble()); +} + +void LayoutTestController::setMockGeolocationError(const CppArgumentList& arguments, CppVariant* result) +{ + result->setNull(); + if (arguments.size() < 2 || !arguments[0].isNumber() || !arguments[1].isString()) + return; + Vector<WebViewHost*> windowList = m_shell->windowList(); + for (size_t i = 0; i < windowList.size(); i++) + windowList[i]->geolocationClientMock()->setError(arguments[0].toInt32(), cppVariantToWebString(arguments[1])); +} + +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; + + if (WebSpeechInputControllerMock* controller = m_shell->webViewHost()->speechInputControllerMock()) + controller->addMockRecognitionResult(cppVariantToWebString(arguments[0]), arguments[1].toDouble(), cppVariantToWebString(arguments[2])); +} + +void LayoutTestController::startSpeechInput(const CppArgumentList& arguments, CppVariant* result) +{ + result->setNull(); + if (arguments.size() != 1) + return; + + WebElement element; + if (!WebBindings::getElement(arguments[0].value.objectValue, &element)) + return; + + WebInputElement* input = toWebInputElement(&element); + if (!input) + return; + + if (!input->isSpeechInputEnabled()) + return; + + input->startSpeechInput(); +} + +void LayoutTestController::layerTreeAsText(const CppArgumentList& args, CppVariant* result) +{ + result->set(m_shell->webView()->mainFrame()->layerTreeAsText(m_showDebugLayerTree).utf8()); +} + +void LayoutTestController::loseCompositorContext(const CppArgumentList& args, CppVariant*) +{ + int numTimes; + if (args.size() == 1 || !args[0].isNumber()) + numTimes = 1; + else + numTimes = args[0].toInt32(); + m_shell->webView()->loseCompositorContext(numTimes); +} + +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())); +} + +void LayoutTestController::findString(const CppArgumentList& arguments, CppVariant* result) +{ + if (arguments.size() < 1 || !arguments[0].isString()) + return; + + WebFindOptions findOptions; + bool wrapAround = false; + if (arguments.size() >= 2) { + Vector<std::string> optionsArray = arguments[1].toStringVector(); + findOptions.matchCase = true; + + for (size_t i = 0; i < optionsArray.size(); ++i) { + const std::string& option = optionsArray[i]; + // FIXME: Support all the options, so we can run findString.html too. + if (option == "CaseInsensitive") + findOptions.matchCase = false; + else if (option == "Backwards") + findOptions.forward = false; + else if (option == "WrapAround") + wrapAround = true; + } + } + + WebFrame* frame = m_shell->webView()->mainFrame(); + const bool findResult = frame->find(0, cppVariantToWebString(arguments[0]), findOptions, wrapAround, 0); + result->set(findResult); +} + +void LayoutTestController::setMinimumTimerInterval(const CppArgumentList& arguments, CppVariant* result) +{ + result->setNull(); + if (arguments.size() < 1 || !arguments[0].isNumber()) + return; + m_shell->webView()->settings()->setMinimumTimerInterval(arguments[0].toDouble()); +} + +void LayoutTestController::setAutofilled(const CppArgumentList& arguments, CppVariant* result) +{ + result->setNull(); + if (arguments.size() != 2 || !arguments[1].isBool()) + return; + + WebElement element; + if (!WebBindings::getElement(arguments[0].value.objectValue, &element)) + return; + + WebInputElement* input = toWebInputElement(&element); + if (!input) + return; + + input->setAutofilled(arguments[1].value.boolValue); +} + +void LayoutTestController::setValueForUser(const CppArgumentList& arguments, CppVariant* result) +{ + result->setNull(); + if (arguments.size() != 2) + return; + + WebElement element; + if (!WebBindings::getElement(arguments[0].value.objectValue, &element)) + return; + + WebInputElement* input = toWebInputElement(&element); + if (!input) + return; + + input->setValue(cppVariantToWebString(arguments[1]), true); +} + +void LayoutTestController::deleteAllLocalStorage(const CppArgumentList& arguments, CppVariant*) +{ + // Not Implemented +} + +void LayoutTestController::localStorageDiskUsageForOrigin(const CppArgumentList& arguments, CppVariant*) +{ + // Not Implemented +} + +void LayoutTestController::originsWithLocalStorage(const CppArgumentList& arguments, CppVariant*) +{ + // Not Implemented +} + +void LayoutTestController::deleteLocalStorageForOrigin(const CppArgumentList& arguments, CppVariant*) +{ + // Not Implemented +} + +void LayoutTestController::observeStorageTrackerNotifications(const CppArgumentList&, CppVariant*) +{ + // Not Implemented +} + +void LayoutTestController::syncLocalStorage(const CppArgumentList&, CppVariant*) +{ + // Not Implemented +} + +void LayoutTestController::setShouldStayOnPageAfterHandlingBeforeUnload(const CppArgumentList& arguments, CppVariant* result) +{ + if (arguments.size() == 1 && arguments[0].isBool()) + m_shouldStayOnPageAfterHandlingBeforeUnload = arguments[0].toBoolean(); + + result->setNull(); +} + +void LayoutTestController::enableFixedLayoutMode(const CppArgumentList& arguments, CppVariant* result) +{ + result->setNull(); + if (arguments.size() < 1 || !arguments[0].isBool()) + return; + bool enableFixedLayout = arguments[0].toBoolean(); + m_shell->webView()->enableFixedLayoutMode(enableFixedLayout); +} + +void LayoutTestController::setFixedLayoutSize(const CppArgumentList& arguments, CppVariant* result) +{ + result->setNull(); + if (arguments.size() < 2 || !arguments[0].isNumber() || !arguments[1].isNumber()) + return; + int width = arguments[0].toInt32(); + int height = arguments[1].toInt32(); + m_shell->webView()->setFixedLayoutSize(WebSize(width, height)); +} + +void LayoutTestController::setPluginsEnabled(const CppArgumentList& arguments, CppVariant* result) +{ + if (arguments.size() > 0 && arguments[0].isBool()) { + m_shell->preferences()->pluginsEnabled = arguments[0].toBoolean(); + m_shell->applyPreferences(); + } + result->setNull(); +} + +void LayoutTestController::resetPageVisibility(const CppArgumentList& arguments, CppVariant* result) +{ + m_shell->webView()->setVisibilityState(WebPageVisibilityStateVisible, true); +} + +void LayoutTestController::setPageVisibility(const CppArgumentList& arguments, CppVariant* result) +{ + if (arguments.size() > 0 && arguments[0].isString()) { + string newVisibility = arguments[0].toString(); + if (newVisibility == "visible") + m_shell->webView()->setVisibilityState(WebPageVisibilityStateVisible, false); + else if (newVisibility == "hidden") + m_shell->webView()->setVisibilityState(WebPageVisibilityStateHidden, false); + else if (newVisibility == "prerender") + m_shell->webView()->setVisibilityState(WebPageVisibilityStatePrerender, false); + } +} + +void LayoutTestController::setTextDirection(const CppArgumentList& arguments, CppVariant* result) +{ + result->setNull(); + if (arguments.size() != 1 || !arguments[0].isString()) + return; + + // Map a direction name to a WebTextDirection value. + std::string directionName = arguments[0].toString(); + WebKit::WebTextDirection direction; + if (directionName == "auto") + direction = WebKit::WebTextDirectionDefault; + else if (directionName == "rtl") + direction = WebKit::WebTextDirectionRightToLeft; + else if (directionName == "ltr") + direction = WebKit::WebTextDirectionLeftToRight; + else + return; + + m_shell->webView()->setTextDirection(direction); +} + +void LayoutTestController::setAudioData(const CppArgumentList& arguments, CppVariant* result) +{ + result->setNull(); + + if (arguments.size() < 1 || !arguments[0].isObject()) + return; + + // Check that passed-in object is, in fact, an ArrayBufferView. + NPObject* npobject = NPVARIANT_TO_OBJECT(arguments[0]); + if (!npobject) + return; + if (!WebBindings::getArrayBufferView(npobject, &m_audioData)) + return; + + setShouldDumpAsAudio(true); +} diff --git a/Tools/DumpRenderTree/chromium/LayoutTestController.h b/Tools/DumpRenderTree/chromium/LayoutTestController.h new file mode 100644 index 000000000..71b1b4226 --- /dev/null +++ b/Tools/DumpRenderTree/chromium/LayoutTestController.h @@ -0,0 +1,701 @@ +/* + * 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 "platform/WebArrayBufferView.h" +#include "platform/WebString.h" +#include "WebTextDirection.h" +#include "platform/WebURL.h" +#include <wtf/Deque.h> +#include <wtf/OwnPtr.h> + +namespace WebKit { +class WebGeolocationClientMock; +class WebSpeechInputController; +class WebSpeechInputControllerMock; +class WebSpeechInputListener; +} + +namespace webkit_support { +class ScopedTempDirectory; +} + +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 + // descriptive text for the progress finished callback. It takes no + // arguments, and ignores any that may be present. + void dumpProgressFinishedCallback(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*); + + // This function sets a flag that tells the test_shell to dump all calls to + // WebViewClient::createView(). + // It takes no arguments, and ignores any that may be present. + void dumpCreateView(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*); + + // Deals with Web Audio WAV file data. + void setAudioData(const CppArgumentList&, CppVariant*); + const WebKit::WebArrayBufferView& audioData() const { return m_audioData; } + + // The following are only stubs. + // FIXME: 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 displayInvalidatedRegion(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 overridePreference(const CppArgumentList&, CppVariant*); + void setAllowUniversalAccessFromFileURLs(const CppArgumentList&, CppVariant*); + void setAllowDisplayOfInsecureContent(const CppArgumentList&, CppVariant*); + void setAllowFileAccessFromFileURLs(const CppArgumentList&, CppVariant*); + void setAllowRunningOfInsecureContent(const CppArgumentList&, CppVariant*); + + void evaluateScriptInIsolatedWorld(const CppArgumentList&, CppVariant*); + void setIsolatedWorldSecurityOrigin(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*); + // Clears an application cache for an origin. + void clearApplicationCacheForOrigin(const CppArgumentList&, CppVariant*); + // Returns origins that have application caches. + void originsWithApplicationCache(const CppArgumentList&, CppVariant*); + // Sets the application cache quota for the localhost origin. + void setApplicationCacheOriginQuota(const CppArgumentList&, CppVariant*); + // Returns disk usage by all application caches for an origin. + void applicationCacheDiskUsageForOrigin(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*); + + // Causes layout to happen as if targetted to printed pages. + void setPrinting(const CppArgumentList&, CppVariant*); + + // Gets the number of page where the specified element will be put. + void pageNumberForElementById(const CppArgumentList&, CppVariant*); + + // Gets the page size and margins for a printed page. + void pageSizeAndMarginsInPixels(const CppArgumentList&, CppVariant*); + + // Returns true if the current page box has custom page size style for + // printing. + void hasCustomPageSizeStyle(const CppArgumentList&, CppVariant*); + + // Returns the visibililty status of a page box for printing + void isPageBoxVisible(const CppArgumentList&, CppVariant*); + + // Gets the page-related property for printed content + void pageProperty(const CppArgumentList&, CppVariant*); + + // Gets the number of pages to be printed. + void numberOfPages(const CppArgumentList&, CppVariant*); + + // Gets the number of geolocation permissions requests pending. + void numberOfPendingGeolocationPermissionRequests(const CppArgumentList&, CppVariant*); + + // Allows layout tests to start JavaScript profiling. + void setJavaScriptProfilingEnabled(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 startSpeechInput(const CppArgumentList&, CppVariant*); + + void layerTreeAsText(const CppArgumentList& args, CppVariant* result); + + void loseCompositorContext(const CppArgumentList& args, CppVariant* result); + + void markerTextForListItem(const CppArgumentList&, CppVariant*); + void hasSpellingMarker(const CppArgumentList&, CppVariant*); + void findString(const CppArgumentList&, CppVariant*); + + void setMinimumTimerInterval(const CppArgumentList&, CppVariant*); + + // Expects the first argument to be an input element and the second argument to be a boolean. + // Forwards the setAutofilled() call to the element. + void setAutofilled(const CppArgumentList&, CppVariant*); + + // Expects the first argument to be an input element and the second argument to be a string value. + // Forwards the setValueForUser() call to the element. + void setValueForUser(const CppArgumentList&, CppVariant*); + + // LocalStorage origin-related + void deleteAllLocalStorage(const CppArgumentList&, CppVariant*); + void originsWithLocalStorage(const CppArgumentList&, CppVariant*); + void deleteLocalStorageForOrigin(const CppArgumentList&, CppVariant*); + void localStorageDiskUsageForOrigin(const CppArgumentList&, CppVariant*); + void observeStorageTrackerNotifications(const CppArgumentList&, CppVariant*); + void syncLocalStorage(const CppArgumentList&, CppVariant*); + + // WebPermissionClient related. + void setImagesAllowed(const CppArgumentList&, CppVariant*); + void setScriptsAllowed(const CppArgumentList&, CppVariant*); + void setStorageAllowed(const CppArgumentList&, CppVariant*); + void setPluginsAllowed(const CppArgumentList&, CppVariant*); + void dumpPermissionClientCallbacks(const CppArgumentList&, CppVariant*); + + // Enable or disable plugins. + void setPluginsEnabled(const CppArgumentList&, CppVariant*); + + // Switch the visibility of the page. + void setPageVisibility(const CppArgumentList&, CppVariant*); + void resetPageVisibility(const CppArgumentList&, CppVariant*); + + // Changes the direction of the focused element. + void setTextDirection(const CppArgumentList&, CppVariant*); + + void setShouldStayOnPageAfterHandlingBeforeUnload(const CppArgumentList&, CppVariant*); + + void enableFixedLayoutMode(const CppArgumentList&, CppVariant*); + void setFixedLayoutSize(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 shouldDumpAsAudio() const { return m_dumpAsAudio; } + void setShouldDumpAsAudio(bool dumpAsAudio) { m_dumpAsAudio = dumpAsAudio; } + bool shouldDumpAsText() { return m_dumpAsText; } + void setShouldDumpAsText(bool value) { m_dumpAsText = value; } + bool shouldDumpEditingCallbacks() { return m_dumpEditingCallbacks; } + bool shouldDumpFrameLoadCallbacks() { return m_dumpFrameLoadCallbacks; } + void setShouldDumpFrameLoadCallbacks(bool value) { m_dumpFrameLoadCallbacks = value; } + bool shouldDumpProgressFinishedCallback() { return m_dumpProgressFinishedCallback; } + void setShouldDumpProgressFinishedCallback(bool value) { m_dumpProgressFinishedCallback = 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 shouldDumpPermissionClientCallbacks() { return m_dumpPermissionClientCallbacks; } + bool shouldDumpChildFrameScrollPositions() { return m_dumpChildFrameScrollPositions; } + bool shouldDumpChildFramesAsText() { return m_dumpChildFramesAsText; } + bool shouldGeneratePixelResults() { return m_generatePixelResults; } + void setShouldGeneratePixelResults(bool value) { m_generatePixelResults = value; } + bool shouldDumpCreateView() { return m_dumpCreateView; } + 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; } + void setShowDebugLayerTree(bool value) { m_showDebugLayerTree = value; } + void setTitleTextDirection(WebKit::WebTextDirection dir) + { + m_titleTextDirection.set(dir == WebKit::WebTextDirectionLeftToRight ? "ltr" : "rtl"); + } + + bool shouldInterceptPostMessage() + { + return m_interceptPostMessage.isBool() && m_interceptPostMessage.toBoolean(); + } + + void setIsPrinting(bool value) { m_isPrinting = value; } + bool isPrinting() { return m_isPrinting; } + + 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*) = 0; + }; + + TaskList* taskList() { return &m_taskList; } + + bool shouldStayOnPageAfterHandlingBeforeUnload() const { return m_shouldStayOnPageAfterHandlingBeforeUnload; } + +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*); + + 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&); + Vector<WebKit::WebString> cppVariantToWebStringArray(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 output a base64 encoded WAVE file. + bool m_dumpAsAudio; + + // If true, the test_shell will write a descriptive line for each editing + // command. + bool m_dumpEditingCallbacks; + + // If true, the test_shell will draw the bounds of the current selection rect + // taking possible transforms of the selection rect into account. + bool m_dumpSelectionRect; + + // If true, the test_shell will output a descriptive line for each frame + // load callback. + bool m_dumpFrameLoadCallbacks; + + // If true, the test_shell will output a descriptive line for the progress + // finished callback. + bool m_dumpProgressFinishedCallback; + + // 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, output a descriptive line each time a permission client + // callback is invoked. Currently only implemented for allowImage. + bool m_dumpPermissionClientCallbacks; + + // If true, the test_shell will generate pixel results in dumpAsText mode + bool m_generatePixelResults; + + // If true, output a descriptive line each time WebViewClient::createView + // is invoked. + bool m_dumpCreateView; + + // 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; + + // If true, we will show extended information in the graphics layer tree. + bool m_showDebugLayerTree; + + // If true, layout is to target printed pages. + bool m_isPrinting; + + WorkQueue m_workQueue; + + CppVariant m_globalFlag; + + // Bound variable counting the number of top URLs visited. + CppVariant m_webHistoryItemCount; + + // Bound variable tracking the directionality of the <title> tag. + CppVariant m_titleTextDirection; + + // Bound variable to return the name of this platform (chromium). + CppVariant m_platformName; + + // Bound variable to set whether postMessages should be intercepted or not + CppVariant m_interceptPostMessage; + + WebKit::WebURL m_userStyleSheetLocation; + + OwnPtr<WebKit::WebSpeechInputControllerMock> m_speechInputControllerMock; + + // WAV audio data is stored here. + WebKit::WebArrayBufferView m_audioData; + + bool m_shouldStayOnPageAfterHandlingBeforeUnload; +}; + +#endif // LayoutTestController_h diff --git a/Tools/DumpRenderTree/chromium/LayoutTestHelper.mm b/Tools/DumpRenderTree/chromium/LayoutTestHelper.mm new file mode 100644 index 000000000..e34cf5fca --- /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 000000000..25efdcdcb --- /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 000000000..56b0f1eff --- /dev/null +++ b/Tools/DumpRenderTree/chromium/MockSpellCheck.cpp @@ -0,0 +1,164 @@ +/* + * 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 "platform/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(). + WTF::String stringText(text.data(), text.length()); + int skippedLength = 0; + + while (!stringText.isEmpty()) { + // 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 ? static_cast<int>(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)) { + *misspelledOffset = wordOffset + skippedLength; + *misspelledLength = wordLength; + break; + } + + ASSERT(0 < wordOffset + wordLength); + stringText = stringText.substring(wordOffset + wordLength); + skippedLength += wordOffset + 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 000000000..bce05c772 --- /dev/null +++ b/Tools/DumpRenderTree/chromium/MockSpellCheck.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 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 000000000..7809821aa --- /dev/null +++ b/Tools/DumpRenderTree/chromium/NotificationPresenter.cpp @@ -0,0 +1,154 @@ +/* + * 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" + +#if ENABLE(NOTIFICATIONS) + +#include "WebKit.h" +#include "platform/WebKitPlatformSupport.h" +#include "WebNotification.h" +#include "WebNotificationPermissionCallback.h" +#include "WebSecurityOrigin.h" +#include "platform/WebString.h" +#include "platform/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; +} + +NotificationPresenter::~NotificationPresenter() +{ +} + +void NotificationPresenter::grantPermission(const WebString& origin) +{ + m_allowedOrigins.add(WTF::String(origin.data(), origin.length())); +} + +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.direction() == WebTextDirectionRightToLeft ? "(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); + + webKitPlatformSupport()->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 WebSecurityOrigin& origin) +{ + // Check with the layout test controller + WebString originString = origin.toString(); + bool allowed = m_allowedOrigins.find(WTF::String(originString.data(), originString.length())) != 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(); +} + +#endif // ENABLE(NOTIFICATIONS) diff --git a/Tools/DumpRenderTree/chromium/NotificationPresenter.h b/Tools/DumpRenderTree/chromium/NotificationPresenter.h new file mode 100644 index 000000000..b33df9138 --- /dev/null +++ b/Tools/DumpRenderTree/chromium/NotificationPresenter.h @@ -0,0 +1,78 @@ +/* + * 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) { } + virtual ~NotificationPresenter(); + + // 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::WebSecurityOrigin&); + 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 000000000..7287c2c83 --- /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 "platform/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 000000000..3d3a04c61 --- /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 000000000..d80beef34 --- /dev/null +++ b/Tools/DumpRenderTree/chromium/Task.cpp @@ -0,0 +1,79 @@ +/* + * 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 "platform/WebKitPlatformSupport.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::webKitPlatformSupport()->callOnMainThread(invokeTask, static_cast<void*>(task)); +} + +void postDelayedTask(WebTask* task, int64_t ms) +{ + webkit_support::PostDelayedTask(task, ms); +} + + diff --git a/Tools/DumpRenderTree/chromium/Task.h b/Tools/DumpRenderTree/chromium/Task.h new file mode 100644 index 000000000..0b32c472b --- /dev/null +++ b/Tools/DumpRenderTree/chromium/Task.h @@ -0,0 +1,96 @@ +/* + * 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 "webkit/support/webkit_support.h" +#include <wtf/OwnPtr.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 webkit_support::TaskAdaptor { +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(); + +private: + virtual void Run() { run(); } + +protected: + TaskList* m_taskList; +}; + +class TaskList { +public: + TaskList() { } + ~TaskList() { revokeAll(); } + void registerTask(WebTask* task) { m_tasks.append(task); } + void unregisterTask(WebTask*); + 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*); +void postDelayedTask(WebTask*, 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 000000000..8246c09be --- /dev/null +++ b/Tools/DumpRenderTree/chromium/TestEventPrinter.cpp @@ -0,0 +1,184 @@ +/* + * 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 handleAudioHeader() 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 handleAudioHeader() 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() +{ +} + +PassOwnPtr<TestEventPrinter> TestEventPrinter::createDRTPrinter() +{ + return adoptPtr(new DRTPrinter); +} + +PassOwnPtr<TestEventPrinter> TestEventPrinter::createTestShellPrinter() +{ + return adoptPtr(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::handleAudioHeader() const +{ + printf("Content-Type: audio/wav\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::handleAudioHeader() const +{ + printf("Content-Type: audio/wav\n"); +} + +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 000000000..f69523c34 --- /dev/null +++ b/Tools/DumpRenderTree/chromium/TestEventPrinter.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 TestEventPrinter_h +#define TestEventPrinter_h + +#include <wtf/PassOwnPtr.h> + +class TestEventPrinter { +public: + static PassOwnPtr<TestEventPrinter> createDRTPrinter(); + static PassOwnPtr<TestEventPrinter> createTestShellPrinter(); + + virtual ~TestEventPrinter(); + 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 handleAudioHeader() 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; +}; + +#endif // TestEventPrinter_h diff --git a/Tools/DumpRenderTree/chromium/TestNavigationController.cpp b/Tools/DumpRenderTree/chromium/TestNavigationController.cpp new file mode 100644 index 000000000..ad6fcfff4 --- /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. + // FIXME: 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 000000000..f23a2ae92 --- /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 "platform/WebString.h" +#include "platform/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 { + WTF_MAKE_NONCOPYABLE(TestNavigationController); +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 000000000..9fa3fff3b --- /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 000000000..59ae666b9 --- /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 000000000..597d4ad7c --- /dev/null +++ b/Tools/DumpRenderTree/chromium/TestNetscapePlugIn/ForwardingHeaders/WebKit/npruntime.h @@ -0,0 +1 @@ +#include "bindings/npruntime.h" diff --git a/Tools/DumpRenderTree/chromium/TestShell.cpp b/Tools/DumpRenderTree/chromium/TestShell.cpp new file mode 100644 index 000000000..f15cb444a --- /dev/null +++ b/Tools/DumpRenderTree/chromium/TestShell.cpp @@ -0,0 +1,768 @@ +/* + * 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 "platform/WebArrayBufferView.h" +#include "WebCompositor.h" +#include "WebDataSource.h" +#include "WebDocument.h" +#include "WebElement.h" +#include "WebFrame.h" +#include "WebHistoryItem.h" +#include "WebIDBFactory.h" +#include "WebTestingSupport.h" +#include "platform/WebThread.h" +#include "WebKit.h" +#include "platform/WebKitPlatformSupport.h" +#include "WebPermissions.h" +#include "platform/WebPoint.h" +#include "WebRuntimeFeatures.h" +#include "WebScriptController.h" +#include "WebSettings.h" +#include "platform/WebSize.h" +#include "WebSpeechInputControllerMock.h" +#include "platform/WebString.h" +#include "platform/WebURLRequest.h" +#include "platform/WebURLResponse.h" +#include "WebView.h" +#include "WebViewHost.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; + +// FIXME: Move this to a common place so that it can be shared with +// WebCore::TransparencyWin::makeLayerOpaque(). +static void makeCanvasOpaque(SkCanvas* canvas) +{ + const SkBitmap& bitmap = canvas->getTopDevice()->accessBitmap(true); + ASSERT(bitmap.config() == SkBitmap::kARGB_8888_Config); + + SkAutoLockPixels lock(bitmap); + for (int y = 0; y < bitmap.height(); y++) { + uint32_t* row = bitmap.getAddr32(0, y); + for (int x = 0; x < bitmap.width(); x++) + row[x] |= 0xFF000000; // Set alpha bits to 1. + } +} + +TestShell::TestShell(bool testShellMode) + : m_testIsPending(false) + , m_testIsPreparing(false) + , m_focusedWidget(0) + , m_testShellMode(testShellMode) + , m_devTools(0) + , m_allowExternalPages(false) + , m_acceleratedCompositingForVideoEnabled(false) + , m_threadedCompositingEnabled(false) + , m_compositeToTexture(false) + , m_forceCompositingMode(false) + , m_accelerated2dCanvasEnabled(false) + , m_legacyAccelerated2dCanvasEnabled(false) + , m_acceleratedPaintingEnabled(false) + , m_perTilePaintingEnabled(false) + , m_stressOpt(false) + , m_stressDeopt(false) + , m_dumpWhenFinished(true) +{ + WebRuntimeFeatures::enableDataTransferItems(true); + WebRuntimeFeatures::enableGeolocation(true); + WebRuntimeFeatures::enablePointerLock(true); + WebRuntimeFeatures::enableIndexedDatabase(true); + WebRuntimeFeatures::enableFileSystem(true); + WebRuntimeFeatures::enableJavaScriptI18NAPI(true); + WebRuntimeFeatures::enableMediaStream(true); + WebRuntimeFeatures::enableWebAudio(true); + WebRuntimeFeatures::enableVideoTrack(true); + WebRuntimeFeatures::enableGamepad(true); + + m_webPermissions = adoptPtr(new WebPermissions(this)); + m_accessibilityController = adoptPtr(new AccessibilityController(this)); + m_gamepadController = adoptPtr(new GamepadController(this)); + m_layoutTestController = adoptPtr(new LayoutTestController(this)); + m_eventSender = adoptPtr(new EventSender(this)); + m_plainTextController = adoptPtr(new PlainTextController()); + m_textInputController = adoptPtr(new TextInputController(this)); +#if ENABLE(NOTIFICATIONS) + m_notificationPresenter = adoptPtr(new NotificationPresenter(this)); +#endif + m_printer = m_testShellMode ? TestEventPrinter::createTestShellPrinter() : TestEventPrinter::createDRTPrinter(); + + WTF::initializeThreading(); + + if (m_threadedCompositingEnabled) { + m_webCompositorThread = adoptPtr(WebKit::webKitPlatformSupport()->createThread("Compositor")); + WebCompositor::initialize(m_webCompositorThread.get()); + } else + WebCompositor::initialize(0); + + + // 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; + + createMainWindow(); +} + +void TestShell::createMainWindow() +{ + m_drtDevToolsAgent = adoptPtr(new DRTDevToolsAgent); + m_webViewHost = adoptPtr(createNewWindow(WebURL(), m_drtDevToolsAgent.get())); + 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); + + WebCompositor::shutdown(); +} + +void TestShell::createDRTDevToolsClient(DRTDevToolsAgent* agent) +{ + m_drtDevToolsClient = adoptPtr(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); + m_devTools->webView()->settings()->setMemoryInfoEnabled(true); + m_devTools->setLogConsoleOutput(false); + ASSERT(m_devTools); + createDRTDevToolsClient(m_drtDevToolsAgent.get()); + } + m_devTools->show(WebKit::WebNavigationPolicyNewWindow); +} + +void TestShell::closeDevTools() +{ + if (m_devTools) { + m_devTools->webView()->settings()->setMemoryInfoEnabled(false); + m_drtDevToolsAgent->reset(); + m_drtDevToolsClient.clear(); + closeWindow(m_devTools); + m_devTools = 0; + } +} + +void TestShell::resetWebSettings(WebView& webView) +{ + m_prefs.reset(); + m_prefs.acceleratedCompositingEnabled = true; + m_prefs.acceleratedCompositingForVideoEnabled = m_acceleratedCompositingForVideoEnabled; + m_prefs.compositeToTexture = m_compositeToTexture; + m_prefs.forceCompositingMode = m_forceCompositingMode; + m_prefs.accelerated2dCanvasEnabled = m_accelerated2dCanvasEnabled; + m_prefs.legacyAccelerated2dCanvasEnabled = m_legacyAccelerated2dCanvasEnabled; + m_prefs.acceleratedPaintingEnabled = m_acceleratedPaintingEnabled; + m_prefs.perTilePaintingEnabled = m_perTilePaintingEnabled; + 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(); + + if (testUrl.find("loading/") != string::npos + || testUrl.find("loading\\") != string::npos) + m_layoutTestController->setShouldDumpFrameLoadCallbacks(true); + + if (testUrl.find("compositing/") != string::npos || testUrl.find("compositing\\") != string::npos) { + m_prefs.acceleratedCompositingForVideoEnabled = true; + m_prefs.accelerated2dCanvasEnabled = true; + m_prefs.applyTo(m_webView); + } + + if (testUrl.find("/dumpAsText/") != string::npos + || testUrl.find("\\dumpAsText\\") != string::npos) { + m_layoutTestController->setShouldDumpAsText(true); + m_layoutTestController->setShouldGeneratePixelResults(false); + } + + if (testUrl.find("/inspector/") != string::npos + || testUrl.find("\\inspector\\") != string::npos) + showDevTools(); + + if (m_params.debugLayerTree) + m_layoutTestController->setShowDebugLayerTree(true); + + 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_webPermissions->reset(); + m_accessibilityController->reset(); + m_gamepadController->reset(); + m_layoutTestController->reset(); + m_eventSender->reset(); + m_webViewHost->reset(); +#if ENABLE(NOTIFICATIONS) + m_notificationPresenter->reset(); +#endif + m_drtDevToolsAgent->reset(); + if (m_drtDevToolsClient) + m_drtDevToolsClient->reset(); + webView()->setPageScaleFactor(1, WebPoint(0, 0)); + webView()->enableFixedLayoutMode(false); + webView()->setFixedLayoutSize(WebSize(0, 0)); + webView()->mainFrame()->clearOpener(); + WebTestingSupport::resetInternalsObject(webView()->mainFrame()); +} + +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 string dumpFramesAsPrintedText(WebFrame* frame, bool recursive) +{ + string result; + + // Cannot do printed format for anything other than HTML + if (!frame->document().isHTMLDocument()) + return string(); + + // 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(frame->renderTreeAsText(WebFrame::RenderAsTextPrinting).utf8()); + result.append("\n"); + + if (recursive) { + for (WebFrame* child = frame->firstChild(); child; child = child->nextSibling()) + result.append(dumpFramesAsPrintedText(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 normalizeLayoutTestURLInternal(const string& url) +{ + string result = url; + size_t pos; + if (!url.find(fileUrlPattern) && ((pos = url.find(layoutTestsPattern)) != string::npos)) { + // adjust file URLs to match upstream results. + result.replace(0, pos + layoutTestsPatternSize, fileTestPrefix); + } else if (!url.find(dataUrlPattern)) { + // URL-escape data URLs to match results upstream. + string path = url.substr(dataUrlPatternSize); + result.replace(dataUrlPatternSize, url.length(), path); + } + return result; +} + +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 = normalizeLayoutTestURLInternal(item.urlString().utf8()); + 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 shouldDumpAsAudio = m_layoutTestController->shouldDumpAsAudio(); + bool shouldGeneratePixelResults = m_layoutTestController->shouldGeneratePixelResults(); + bool shouldDumpAsPrinted = m_layoutTestController->isPrinting(); + bool dumpedAnything = false; + + if (shouldDumpAsAudio) { + m_printer->handleAudioHeader(); + + const WebKit::WebArrayBufferView& webArrayBufferView = m_layoutTestController->audioData(); + printf("Content-Length: %d\n", webArrayBufferView.byteLength()); + + if (fwrite(webArrayBufferView.baseAddress(), 1, webArrayBufferView.byteLength(), stdout) != webArrayBufferView.byteLength()) + FATAL("Short write to stdout, disk full?\n"); + printf("\n"); + + m_printer->handleTestFooter(true); + + fflush(stdout); + fflush(stderr); + return; + } + + 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 = shouldDumpAsPrinted ? dumpFramesAsPrintedText(frame, recursive) : dumpFramesAsText(frame, recursive); + if (fwrite(dataUtf8.c_str(), 1, dataUtf8.size(), stdout) != dataUtf8.size()) + FATAL("Short write to stdout, disk full?\n"); + } else { + WebFrame::RenderAsTextControls renderTextBehavior = WebFrame::RenderAsTextNormal; + if (shouldDumpAsPrinted) + renderTextBehavior |= WebFrame::RenderAsTextPrinting; + if (m_params.debugRenderTree) + renderTextBehavior |= WebFrame::RenderAsTextDebug; + printf("%s", frame->renderTreeAsText(renderTextBehavior).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 if (m_layoutTestController->isPrinting()) + m_webViewHost->paintPagesWithBoundaries(); + 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(SkCanvas* canvas) const +{ + // 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; + makeCanvasOpaque(canvas); +#endif + + const SkBitmap& sourceBitmap = canvas->getTopDevice()->accessBitmap(false); + SkAutoLockPixels sourceBitmapLock(sourceBitmap); + + // 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::EncodeBGRAPNGWithChecksum(reinterpret_cast<const unsigned char*>(sourceBitmap.getPixels()), sourceBitmap.width(), + sourceBitmap.height(), static_cast<int>(sourceBitmap.rowBytes()), discardTransparency, md5hash, &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) +{ + WebTestingSupport::injectInternalsObject(frame); + m_accessibilityController->bindToJavascript(frame, WebString::fromUTF8("accessibilityController")); + m_gamepadController->bindToJavascript(frame, WebString::fromUTF8("gamepadController")); + 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::createNewWindow(const WebKit::WebURL& url) +{ + return createNewWindow(url, 0); +} + +WebViewHost* TestShell::createNewWindow(const WebKit::WebURL& url, DRTDevToolsAgent* devToolsAgent) +{ + WebViewHost* host = new WebViewHost(this); + WebView* view = WebView::create(host); + view->setPermissionClient(webPermissions()); + view->setDevToolsAgentClient(devToolsAgent); + 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(); +} + +string TestShell::normalizeLayoutTestURL(const string& url) +{ + return normalizeLayoutTestURLInternal(url); +} diff --git a/Tools/DumpRenderTree/chromium/TestShell.h b/Tools/DumpRenderTree/chromium/TestShell.h new file mode 100644 index 000000000..a095ef2d0 --- /dev/null +++ b/Tools/DumpRenderTree/chromium/TestShell.h @@ -0,0 +1,253 @@ +/* + * 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 "GamepadController.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 WebThread; +class WebView; +class WebURL; +} + +class DRTDevToolsAgent; +class DRTDevToolsCallArgs; +class DRTDevToolsClient; +class WebPermissions; + +struct TestParams { + bool dumpTree; + bool dumpPixels; + bool debugRenderTree; + bool debugLayerTree; + 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) + , debugRenderTree(false) + , debugLayerTree(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.get(); } + LayoutTestController* layoutTestController() const { return m_layoutTestController.get(); } + EventSender* eventSender() const { return m_eventSender.get(); } + AccessibilityController* accessibilityController() const { return m_accessibilityController.get(); } + GamepadController* gamepadController() const { return m_gamepadController.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); } + + WebPermissions* webPermissions() { return m_webPermissions.get(); } + + void bindJSObjectsToWindow(WebKit::WebFrame*); + void runFileTest(const TestParams&); + void callJSGC(); + void resetTestController(); + void waitTestFinished(); + + // Operations to the main window. + void loadURL(const WebKit::WebURL&); + 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 setAcceleratedCompositingForVideoEnabled(bool enabled) { m_acceleratedCompositingForVideoEnabled = enabled; } + void setThreadedCompositingEnabled(bool enabled) { m_threadedCompositingEnabled = enabled; } + void setCompositeToTexture(bool enabled) { m_compositeToTexture = enabled; } + void setForceCompositingMode(bool enabled) { m_forceCompositingMode = enabled; } + void setAccelerated2dCanvasEnabled(bool enabled) { m_accelerated2dCanvasEnabled = enabled; } + void setLegacyAccelerated2dCanvasEnabled(bool enabled) { m_legacyAccelerated2dCanvasEnabled = enabled; } + void setAcceleratedPaintingEnabled(bool enabled) { m_acceleratedPaintingEnabled = enabled; } + void setPerTilePaintingEnabled(bool enabled) { m_perTilePaintingEnabled = 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; } + + // V8 JavaScript stress test options. + int stressOpt() { return m_stressOpt; } + void setStressOpt(bool stressOpt) { m_stressOpt = stressOpt; } + int stressDeopt() { return m_stressDeopt; } + void setStressDeopt(int stressDeopt) { m_stressDeopt = stressDeopt; } + + // The JavaScript flags specified as a strings. + std::string javaScriptFlags() { return m_javaScriptFlags; } + void setJavaScriptFlags(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* 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; + + typedef Vector<WebViewHost*> WindowList; + WindowList windowList() const { return m_windowList; } + + // Returns a string representation of an URL's spec that does not depend on + // the location of the layout test in the file system. + std::string normalizeLayoutTestURL(const std::string&); + +private: + WebViewHost* createNewWindow(const WebKit::WebURL&, DRTDevToolsAgent*); + void createMainWindow(); + void createDRTDevToolsClient(DRTDevToolsAgent*); + + void resetWebSettings(WebKit::WebView&); + void dump(); + std::string dumpAllBackForwardLists(); + void dumpImage(SkCanvas*) const; + + bool m_testIsPending; + bool m_testIsPreparing; + bool m_isLoading; + WebKit::WebView* m_webView; + WebKit::WebWidget* m_focusedWidget; + bool m_testShellMode; + WebViewHost* m_devTools; + + // Be careful of the destruction order of the following objects. + OwnPtr<TestEventPrinter> m_printer; + OwnPtr<WebPermissions> m_webPermissions; + OwnPtr<DRTDevToolsAgent> m_drtDevToolsAgent; + OwnPtr<DRTDevToolsClient> m_drtDevToolsClient; + OwnPtr<AccessibilityController> m_accessibilityController; + OwnPtr<GamepadController> m_gamepadController; + OwnPtr<EventSender> m_eventSender; + OwnPtr<LayoutTestController> m_layoutTestController; + OwnPtr<PlainTextController> m_plainTextController; + OwnPtr<TextInputController> m_textInputController; + OwnPtr<NotificationPresenter> m_notificationPresenter; + OwnPtr<WebViewHost> m_webViewHost; + OwnPtr<WebKit::WebThread> m_webCompositorThread; + + TestParams m_params; + int m_timeout; // timeout value in millisecond + bool m_allowExternalPages; + bool m_acceleratedCompositingForVideoEnabled; + bool m_threadedCompositingEnabled; + bool m_compositeToTexture; + bool m_forceCompositingMode; + bool m_accelerated2dCanvasEnabled; + bool m_legacyAccelerated2dCanvasEnabled; + bool m_acceleratedPaintingEnabled; + bool m_perTilePaintingEnabled; + WebPreferences m_prefs; + bool m_stressOpt; + bool m_stressDeopt; + std::string m_javaScriptFlags; + bool m_dumpWhenFinished; + + + // List of all windows in this process. + // The main window should be put into windowList[0]. + 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 000000000..4228f2afd --- /dev/null +++ b/Tools/DumpRenderTree/chromium/TestShellGtk.cpp @@ -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. + */ + +#include "config.h" +#include "TestShell.h" + +#include "webkit/support/webkit_support.h" +#include <fontconfig/fontconfig.h> +#include <gtk/gtk.h> +#include <signal.h> + + +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/TestShellLinux.cpp b/Tools/DumpRenderTree/chromium/TestShellLinux.cpp new file mode 100644 index 000000000..82db69a2f --- /dev/null +++ b/Tools/DumpRenderTree/chromium/TestShellLinux.cpp @@ -0,0 +1,219 @@ +/* + * Copyright (C) 2011 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 "SkTypeface.h" +#include "WebView.h" +#include "webkit/support/webkit_support.h" + +#if !OS(ANDROID) +#include <fontconfig/fontconfig.h> +#endif + +#if USE(GTK) +#include <gtk/gtk.h> +#endif +#include <signal.h> +#include <unistd.h> + +static void AlarmHandler(int) +{ + // If the alarm alarmed, kill the process since we have a really bad hang. + puts("\n#TEST_TIMED_OUT\n"); + puts("#EOF\n"); + fflush(stdout); + exit(0); +} + +void TestShell::waitTestFinished() +{ + ASSERT(!m_testIsPending); + m_testIsPending = true; + + // Install an alarm signal handler that will kill us if we time out. + struct sigaction alarmAction; + alarmAction.sa_handler = AlarmHandler; + sigemptyset(&alarmAction.sa_mask); + alarmAction.sa_flags = 0; + + struct sigaction oldAction; + sigaction(SIGALRM, &alarmAction, &oldAction); + 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); + sigaction(SIGALRM, &oldAction, 0); +} + +#if !OS(ANDROID) +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", + "/usr/share/fonts/truetype/thai/Garuda.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_hi.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 re-running build/install-build-deps.sh. Also see " + "http://code.google.com/p/chromium/wiki/LayoutTestsLinux", + 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-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/LayoutTestsLinux " + "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); + } +} +#endif // !OS(ANDROID) + +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# +#if USE(GTK) + gtk_init(argc, argv); +#endif + +#if !OS(ANDROID) + setupFontconfig(); +#endif +} + diff --git a/Tools/DumpRenderTree/chromium/TestShellMac.mm b/Tools/DumpRenderTree/chromium/TestShellMac.mm new file mode 100644 index 000000000..d79a8c835 --- /dev/null +++ b/Tools/DumpRenderTree/chromium/TestShellMac.mm @@ -0,0 +1,149 @@ +/* + * Copyright (C) 2010 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" + +#include "TestShell.h" +#include "WebThemeEngineDRTMac.h" +#include "webkit/support/webkit_support.h" +#import <AppKit/AppKit.h> + +static WebThemeEngineDRTMac themeEngine; + +// 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***) +{ + webkit_support::SetThemeEngine(&themeEngine); +} + +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/TestShellStub.cpp b/Tools/DumpRenderTree/chromium/TestShellStub.cpp new file mode 100644 index 000000000..6c34f84b3 --- /dev/null +++ b/Tools/DumpRenderTree/chromium/TestShellStub.cpp @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2011 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" + +bool checkLayoutTestSystemDependencies() +{ + return true; +} + +void openStartupDialog() +{ + // FIXME: Not implemented. +} + diff --git a/Tools/DumpRenderTree/chromium/TestShellWin.cpp b/Tools/DumpRenderTree/chromium/TestShellWin.cpp new file mode 100644 index 000000000..850e5de81 --- /dev/null +++ b/Tools/DumpRenderTree/chromium/TestShellWin.cpp @@ -0,0 +1,217 @@ +/* + * 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() +{ + // This metric will be 17 when font size is "Normal". + // The size of drop-down menus depends on it. + int verticalScrollSize = ::GetSystemMetrics(SM_CXVSCROLL); + int requiredVScrollSize = 17; + std::list<std::string> errors; + 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. + OSVERSIONINFO versionInfo = {0}; + versionInfo.dwOSVersionInfoSize = sizeof(versionInfo); + ::GetVersionEx(&versionInfo); + const bool isVistaOrLater = (versionInfo.dwMajorVersion >= 6); + NONCLIENTMETRICS metrics = {0}; + metrics.cbSize = isVistaOrLater ? (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}; + const wchar_t* const requiredFont = isVistaOrLater ? L"Segoe UI" : L"Tahoma"; + const int requiredFontSize = isVistaOrLater ? -12 : -11; + for (size_t i = 0; i < arraysize(systemFonts); ++i) { + if (systemFonts[i]->lfHeight != requiredFontSize || wcscmp(requiredFont, systemFonts[i]->lfFaceName)) { + errors.push_back(isVistaOrLater ? "Must use either the Aero or Basic theme." : "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/TestWebPlugin.cpp b/Tools/DumpRenderTree/chromium/TestWebPlugin.cpp new file mode 100644 index 000000000..ac2336664 --- /dev/null +++ b/Tools/DumpRenderTree/chromium/TestWebPlugin.cpp @@ -0,0 +1,326 @@ +/* + * Copyright (C) 2011 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: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS 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 APPLE OR ITS 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 "TestWebPlugin.h" + +#include "WebFrame.h" +#include "platform/WebGraphicsContext3D.h" +#include "WebKit.h" +#include "platform/WebKitPlatformSupport.h" +#include "WebPluginContainer.h" +#include "WebPluginParams.h" +#include <wtf/Assertions.h> +#include <wtf/text/CString.h> + +using namespace WebKit; + +// GLenum values copied from gl2.h. +#define GL_FALSE 0 +#define GL_TRUE 1 +#define GL_ONE 1 +#define GL_TRIANGLES 0x0004 +#define GL_ONE_MINUS_SRC_ALPHA 0x0303 +#define GL_BLEND 0x0BE2 +#define GL_FLOAT 0x1406 +#define GL_COLOR_BUFFER_BIT 0x4000 +#define GL_ARRAY_BUFFER 0x8892 +#define GL_STATIC_DRAW 0x88E4 +#define GL_FRAGMENT_SHADER 0x8B30 +#define GL_VERTEX_SHADER 0x8B31 +#define GL_COMPILE_STATUS 0x8B81 +#define GL_LINK_STATUS 0x8B82 + +static void premultiplyAlpha(const unsigned colorIn[3], float alpha, float colorOut[4]) +{ + for (int i = 0; i < 3; ++i) + colorOut[i] = (colorIn[i] / 255.0f) * alpha; + + colorOut[3] = alpha; +} + +TestWebPlugin::TestWebPlugin(WebKit::WebFrame* frame, + const WebKit::WebPluginParams& params) + : m_frame(frame) + , m_container(0) + , m_context(0) +{ + static const WebString kAttributePrimitive = WebString::fromUTF8("primitive"); + static const WebString kAttributeBackgroundColor = WebString::fromUTF8("background-color"); + static const WebString kAttributePrimitiveColor = WebString::fromUTF8("primitive-color"); + static const WebString kAttributeOpacity = WebString::fromUTF8("opacity"); + + ASSERT(params.attributeNames.size() == params.attributeValues.size()); + size_t size = params.attributeNames.size(); + for (size_t i = 0; i < size; ++i) { + const WebString& attributeName = params.attributeNames[i]; + const WebString& attributeValue = params.attributeValues[i]; + + if (attributeName == kAttributePrimitive) + m_scene.primitive = parsePrimitive(attributeValue); + else if (attributeName == kAttributeBackgroundColor) + parseColor(attributeValue, m_scene.backgroundColor); + else if (attributeName == kAttributePrimitiveColor) + parseColor(attributeValue, m_scene.primitiveColor); + else if (attributeName == kAttributeOpacity) + m_scene.opacity = parseOpacity(attributeValue); + } +} + +TestWebPlugin::~TestWebPlugin() +{ +} + +const WebString& TestWebPlugin::mimeType() +{ + static const WebString kMimeType = WebString::fromUTF8("application/x-webkit-test-webplugin"); + return kMimeType; +} + +bool TestWebPlugin::initialize(WebPluginContainer* container) +{ + m_context = webKitPlatformSupport()->createGraphicsContext3D(); + if (!m_context) + return false; + + WebGraphicsContext3D::Attributes attrs; + if (!m_context->initialize(attrs, m_frame->view(), false)) + return false; + + if (!m_context->makeContextCurrent()) + return false; + + if (!initScene()) + return false; + + m_container = container; + m_container->setBackingTextureId(m_context->getPlatformTextureId()); + return true; +} + +void TestWebPlugin::destroy() +{ + destroyScene(); + + delete m_context; + m_context = 0; + + m_container = 0; + m_frame = 0; +} + +void TestWebPlugin::updateGeometry(const WebRect& frameRect, + const WebRect& clipRect, + const WebVector<WebRect>& cutOutsRects, + bool isVisible) +{ + if (clipRect == m_rect) + return; + m_rect = clipRect; + + m_context->reshape(m_rect.width, m_rect.height); + drawScene(); + m_context->prepareTexture(); + + m_container->commitBackingTexture(); +} + +TestWebPlugin::Primitive TestWebPlugin::parsePrimitive(const WebString& string) +{ + static const WebString kPrimitiveNone = WebString::fromUTF8("none"); + static const WebString kPrimitiveTriangle = WebString::fromUTF8("triangle"); + + Primitive primitive = PrimitiveNone; + if (string == kPrimitiveNone) + primitive = PrimitiveNone; + else if (string == kPrimitiveTriangle) + primitive = PrimitiveTriangle; + else + ASSERT_NOT_REACHED(); + return primitive; +} + +// FIXME: This method should already exist. Use it. +// For now just parse primary colors. +void TestWebPlugin::parseColor(const WebString& string, unsigned color[3]) +{ + color[0] = color[1] = color[2] = 0; + if (string == "black") + return; + + if (string == "red") + color[0] = 255; + else if (string == "green") + color[1] = 255; + else if (string == "blue") + color[2] = 255; + else + ASSERT_NOT_REACHED(); +} + +float TestWebPlugin::parseOpacity(const WebString& string) +{ + return static_cast<float>(atof(string.utf8().data())); +} + +bool TestWebPlugin::initScene() +{ + float color[4]; + premultiplyAlpha(m_scene.backgroundColor, m_scene.opacity, color); + m_context->clearColor(color[0], color[1], color[2], color[3]); + + m_context->enable(GL_BLEND); + m_context->blendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); + + return m_scene.primitive != PrimitiveNone ? initProgram() && initPrimitive() : true; +} + +void TestWebPlugin::drawScene() +{ + m_context->viewport(0, 0, m_rect.width, m_rect.height); + m_context->clear(GL_COLOR_BUFFER_BIT); + + if (m_scene.primitive != PrimitiveNone) + drawPrimitive(); +} + +void TestWebPlugin::destroyScene() +{ + if (m_scene.program) { + m_context->deleteProgram(m_scene.program); + m_scene.program = 0; + } + if (m_scene.vbo) { + m_context->deleteBuffer(m_scene.vbo); + m_scene.vbo = 0; + } +} + +bool TestWebPlugin::initProgram() +{ + const CString vertexSource( + "attribute vec4 position; \n" + "void main() { \n" + " gl_Position = position; \n" + "} \n" + ); + + const CString fragmentSource( + "precision mediump float; \n" + "uniform vec4 color; \n" + "void main() { \n" + " gl_FragColor = color; \n" + "} \n" + ); + + m_scene.program = loadProgram(vertexSource, fragmentSource); + if (!m_scene.program) + return false; + + m_scene.colorLocation = m_context->getUniformLocation(m_scene.program, "color"); + m_scene.positionLocation = m_context->getAttribLocation(m_scene.program, "position"); + return true; +} + +bool TestWebPlugin::initPrimitive() +{ + ASSERT(m_scene.primitive == PrimitiveTriangle); + + m_scene.vbo = m_context->createBuffer(); + if (!m_scene.vbo) + return false; + + const float vertices[] = { 0.0f, 0.8f, 0.0f, + -0.8f, -0.8f, 0.0f, + 0.8f, -0.8f, 0.0f }; + m_context->bindBuffer(GL_ARRAY_BUFFER, m_scene.vbo); + m_context->bufferData(GL_ARRAY_BUFFER, sizeof(vertices), 0, GL_STATIC_DRAW); + m_context->bufferSubData(GL_ARRAY_BUFFER, 0, sizeof(vertices), vertices); + return true; +} + +void TestWebPlugin::drawPrimitive() +{ + ASSERT(m_scene.primitive == PrimitiveTriangle); + ASSERT(m_scene.vbo); + ASSERT(m_scene.program); + + m_context->useProgram(m_scene.program); + + // Bind primitive color. + float color[4]; + premultiplyAlpha(m_scene.primitiveColor, m_scene.opacity, color); + m_context->uniform4f(m_scene.colorLocation, color[0], color[1], color[2], color[3]); + + // Bind primitive vertices. + m_context->bindBuffer(GL_ARRAY_BUFFER, m_scene.vbo); + m_context->enableVertexAttribArray(m_scene.positionLocation); + m_context->vertexAttribPointer(m_scene.positionLocation, 3, GL_FLOAT, GL_FALSE, 0, 0); + m_context->drawArrays(GL_TRIANGLES, 0, 3); +} + +unsigned TestWebPlugin::loadShader(unsigned type, const CString& source) +{ + unsigned shader = m_context->createShader(type); + if (shader) { + m_context->shaderSource(shader, source.data()); + m_context->compileShader(shader); + + int compiled = 0; + m_context->getShaderiv(shader, GL_COMPILE_STATUS, &compiled); + if (!compiled) { + m_context->deleteShader(shader); + shader = 0; + } + } + return shader; +} + +unsigned TestWebPlugin::loadProgram(const CString& vertexSource, + const CString& fragmentSource) +{ + unsigned vertexShader = loadShader(GL_VERTEX_SHADER, vertexSource); + unsigned fragmentShader = loadShader(GL_FRAGMENT_SHADER, fragmentSource); + unsigned program = m_context->createProgram(); + if (vertexShader && fragmentShader && program) { + m_context->attachShader(program, vertexShader); + m_context->attachShader(program, fragmentShader); + m_context->linkProgram(program); + + int linked = 0; + m_context->getProgramiv(program, GL_LINK_STATUS, &linked); + if (!linked) { + m_context->deleteProgram(program); + program = 0; + } + } + if (vertexShader) + m_context->deleteShader(vertexShader); + if (fragmentShader) + m_context->deleteShader(fragmentShader); + + return program; +} + diff --git a/Tools/DumpRenderTree/chromium/TestWebPlugin.h b/Tools/DumpRenderTree/chromium/TestWebPlugin.h new file mode 100644 index 000000000..cef472884 --- /dev/null +++ b/Tools/DumpRenderTree/chromium/TestWebPlugin.h @@ -0,0 +1,126 @@ +/* + * Copyright (C) 2011 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: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS 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 APPLE OR ITS 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 TestWebPlugin_h +#define TestWebPlugin_h + +#include "WebPlugin.h" +#include "platform/WebRect.h" + +namespace WebKit { +class WebGraphicsContext3D; +} + +// A fake implemention of WebKit::WebPlugin for testing purposes. +// +// It uses WebGraphicsContext3D to paint a scene consisiting of a primitive +// over a background. The primitive and background can be customized using +// the following plugin parameters: +// primitive: none (default), triangle. +// background-color: black (default), red, green, blue. +// primitive-color: black (default), red, green, blue. +// opacity: [0.0 - 1.0]. Default is 1.0. +class TestWebPlugin : public WebKit::WebPlugin { +public: + TestWebPlugin(WebKit::WebFrame*, const WebKit::WebPluginParams&); + virtual ~TestWebPlugin(); + + static const WebKit::WebString& mimeType(); + + // WebPlugin methods: + virtual bool initialize(WebKit::WebPluginContainer*); + virtual void destroy(); + virtual NPObject* scriptableObject() { return 0; } + virtual void paint(WebKit::WebCanvas*, const WebKit::WebRect&) { } + virtual void updateGeometry(const WebKit::WebRect& frameRect, + const WebKit::WebRect& clipRect, + const WebKit::WebVector<WebKit::WebRect>& cutOutsRects, + bool isVisible); + virtual void updateFocus(bool) { } + virtual void updateVisibility(bool) { } + virtual bool acceptsInputEvents() { return false; } + virtual bool handleInputEvent(const WebKit::WebInputEvent&, WebKit::WebCursorInfo&) { return false; } + virtual void didReceiveResponse(const WebKit::WebURLResponse&) { } + virtual void didReceiveData(const char* data, int dataLength) { } + virtual void didFinishLoading() { } + virtual void didFailLoading(const WebKit::WebURLError&) { } + virtual void didFinishLoadingFrameRequest(const WebKit::WebURL&, void* notifyData) { } + virtual void didFailLoadingFrameRequest(const WebKit::WebURL&, void* notifyData, const WebKit::WebURLError&) { } + +private: + enum Primitive { + PrimitiveNone, + PrimitiveTriangle + }; + + struct Scene { + Primitive primitive; + unsigned backgroundColor[3]; + unsigned primitiveColor[3]; + float opacity; + + unsigned vbo; + unsigned program; + int colorLocation; + int positionLocation; + + Scene() + : primitive(PrimitiveNone) + , opacity(1.0f) // Fully opaque. + , vbo(0) + , program(0) + , colorLocation(-1) + , positionLocation(-1) + { + backgroundColor[0] = backgroundColor[1] = backgroundColor[2] = 0; + primitiveColor[0] = primitiveColor[1] = primitiveColor[2] = 0; + } + }; + + // Functions for parsing plugin parameters. + Primitive parsePrimitive(const WebKit::WebString&); + void parseColor(const WebKit::WebString&, unsigned color[3]); + float parseOpacity(const WebKit::WebString&); + + // Functions for loading and drawing scene. + bool initScene(); + void drawScene(); + void destroyScene(); + bool initProgram(); + bool initPrimitive(); + void drawPrimitive(); + unsigned loadShader(unsigned type, const WTF::CString& source); + unsigned loadProgram(const WTF::CString& vertexSource, + const WTF::CString& fragmentSource); + + WebKit::WebFrame* m_frame; + WebKit::WebPluginContainer* m_container; + + WebKit::WebRect m_rect; + WebKit::WebGraphicsContext3D* m_context; + Scene m_scene; +}; + +#endif // TestPepperPlugin_h diff --git a/Tools/DumpRenderTree/chromium/TestWebWorker.h b/Tools/DumpRenderTree/chromium/TestWebWorker.h new file mode 100644 index 000000000..ad82a87e9 --- /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 "WebSharedWorkerClient.h" +#include "WebWorker.h" +#include <wtf/RefCounted.h> + +namespace WebKit { +class WebNotificationPresenter; +class WebString; +class WebURL; +} + +class TestWebWorker : public WebKit::WebWorker, + public WebKit::WebSharedWorkerClient, + 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::WebSharedWorkerClient*) { 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 000000000..a3637067a --- /dev/null +++ b/Tools/DumpRenderTree/chromium/TextInputController.cpp @@ -0,0 +1,249 @@ +/* + * 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 "platform/WebString.h" +#include "platform/WebVector.h" +#include "WebView.h" +#include <string> +#include <wtf/StringExtras.h> + +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(); + + if (arguments.size() < 1 || !arguments[0].isString()) + return; + + testShell->webView()->confirmComposition(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(); + + if (arguments.size() >= 3 && arguments[0].isString() + && arguments[1].isNumber() && arguments[2].isNumber()) { + WebVector<WebCompositionUnderline> underlines; + testShell->webView()->setComposition(WebString::fromUTF8(arguments[0].toString()), + underlines, + arguments[1].toInt32(), + arguments[1].toInt32() + arguments[2].toInt32()); + } +} + +void TextInputController::unmarkText(const CppArgumentList&, CppVariant* result) +{ + result->setNull(); + + testShell->webView()->confirmComposition(); +} + +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* frame = testShell->webView()->focusedFrame(); + if (!frame) + return; + + if (arguments.size() < 2 || !arguments[0].isNumber() || !arguments[1].isNumber()) + return; + + WebRect rect; + if (!frame->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 000000000..3a3907fda --- /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/WebPermissions.cpp b/Tools/DumpRenderTree/chromium/WebPermissions.cpp new file mode 100644 index 000000000..ee90a7b2c --- /dev/null +++ b/Tools/DumpRenderTree/chromium/WebPermissions.cpp @@ -0,0 +1,132 @@ +/* + * Copyright (C) 2011 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 "WebPermissions.h" + +#include "LayoutTestController.h" +#include "TestShell.h" +#include "platform/WebCString.h" +#include "platform/WebURL.h" + +WebPermissions::WebPermissions(TestShell* shell) + : m_shell(shell) +{ + reset(); +} + +WebPermissions::~WebPermissions() +{ +} + +bool WebPermissions::allowImage(WebKit::WebFrame*, bool enabledPerSettings, const WebKit::WebURL& imageURL) +{ + bool allowed = enabledPerSettings && m_imagesAllowed; + if (layoutTestController()->shouldDumpPermissionClientCallbacks()) + fprintf(stdout, "PERMISSION CLIENT: allowImage(%s): %s\n", m_shell->normalizeLayoutTestURL(imageURL.spec()).c_str(), allowed ? "true" : "false"); + return allowed; +} + +bool WebPermissions::allowScriptFromSource(WebKit::WebFrame*, bool enabledPerSettings, const WebKit::WebURL& scriptURL) +{ + bool allowed = enabledPerSettings && m_scriptsAllowed; + if (layoutTestController()->shouldDumpPermissionClientCallbacks()) + fprintf(stdout, "PERMISSION CLIENT: allowScriptFromSource(%s): %s\n", m_shell->normalizeLayoutTestURL(scriptURL.spec()).c_str(), allowed ? "true" : "false"); + return allowed; +} + +bool WebPermissions::allowStorage(WebKit::WebFrame*, bool) +{ + return m_storageAllowed; +} + +bool WebPermissions::allowPlugins(WebKit::WebFrame*, bool enabledPerSettings) +{ + return enabledPerSettings && m_pluginsAllowed; +} + +bool WebPermissions::allowDisplayingInsecureContent(WebKit::WebFrame*, bool enabledPerSettings, + const WebKit::WebSecurityOrigin&, const WebKit::WebURL&) +{ + return enabledPerSettings || m_displayingInsecureContentAllowed; +} + +bool WebPermissions::allowRunningInsecureContent(WebKit::WebFrame*, bool enabledPerSettings, + const WebKit::WebSecurityOrigin&, const WebKit::WebURL&) +{ + return enabledPerSettings || m_runningInsecureContentAllowed; +} + +void WebPermissions::setImagesAllowed(bool imagesAllowed) +{ + m_imagesAllowed = imagesAllowed; +} + +void WebPermissions::setScriptsAllowed(bool scriptsAllowed) +{ + m_scriptsAllowed = scriptsAllowed; +} + +void WebPermissions::setStorageAllowed(bool storageAllowed) +{ + m_storageAllowed = storageAllowed; +} + +void WebPermissions::setPluginsAllowed(bool pluginsAllowed) +{ + m_pluginsAllowed = pluginsAllowed; +} + +void WebPermissions::setDisplayingInsecureContentAllowed(bool allowed) +{ + m_displayingInsecureContentAllowed = allowed; +} + +void WebPermissions::setRunningInsecureContentAllowed(bool allowed) +{ + m_runningInsecureContentAllowed = allowed; +} + +void WebPermissions::reset() +{ + m_imagesAllowed = true; + m_scriptsAllowed = true; + m_storageAllowed = true; + m_pluginsAllowed = true; + m_displayingInsecureContentAllowed = false; + m_runningInsecureContentAllowed = false; +} + +// Private functions ---------------------------------------------------------- + +LayoutTestController* WebPermissions::layoutTestController() const +{ + return m_shell->layoutTestController(); +} diff --git a/Tools/DumpRenderTree/chromium/WebPermissions.h b/Tools/DumpRenderTree/chromium/WebPermissions.h new file mode 100644 index 000000000..184046a0a --- /dev/null +++ b/Tools/DumpRenderTree/chromium/WebPermissions.h @@ -0,0 +1,79 @@ +/* + * Copyright (C) 2011 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 WebPermissions_h +#define WebPermissions_h + +#include "WebPermissionClient.h" + +class LayoutTestController; +class TestShell; + +class WebPermissions : public WebKit::WebPermissionClient { +public: + WebPermissions(TestShell*); + virtual ~WebPermissions(); + + // Override WebPermissionClient methods. + virtual bool allowImage(WebKit::WebFrame*, bool enabledPerSettings, const WebKit::WebURL& imageURL); + virtual bool allowScriptFromSource(WebKit::WebFrame*, bool enabledPerSettings, const WebKit::WebURL& scriptURL); + virtual bool allowStorage(WebKit::WebFrame*, bool local); + virtual bool allowPlugins(WebKit::WebFrame*, bool enabledPerSettings); + virtual bool allowDisplayingInsecureContent(WebKit::WebFrame*, bool enabledPerSettings, + const WebKit::WebSecurityOrigin&, const WebKit::WebURL&); + virtual bool allowRunningInsecureContent(WebKit::WebFrame*, bool enabledPerSettings, + const WebKit::WebSecurityOrigin&, const WebKit::WebURL&); + + // Hooks to set the different policies. + void setImagesAllowed(bool); + void setScriptsAllowed(bool); + void setStorageAllowed(bool); + void setPluginsAllowed(bool); + void setDisplayingInsecureContentAllowed(bool); + void setRunningInsecureContentAllowed(bool); + + // Resets the policy to allow everything, except for running insecure content. + void reset(); + +private: + LayoutTestController* layoutTestController() const; + + // Non-owning pointer. The WebPermissions instance is owned by this TestShell instance. + TestShell* m_shell; + + bool m_imagesAllowed; + bool m_scriptsAllowed; + bool m_storageAllowed; + bool m_pluginsAllowed; + bool m_displayingInsecureContentAllowed; + bool m_runningInsecureContentAllowed; +}; + +#endif diff --git a/Tools/DumpRenderTree/chromium/WebPreferences.cpp b/Tools/DumpRenderTree/chromium/WebPreferences.cpp new file mode 100644 index 000000000..01e4fcc4e --- /dev/null +++ b/Tools/DumpRenderTree/chromium/WebPreferences.cpp @@ -0,0 +1,243 @@ +/* + * 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; + // Do not disable acceleration for 2d canvas based on size. + // This makes having test expectations consistent. + minimumAccelerated2dCanvasSize = 0; + + DOMPasteAllowed = true; + XSSAuditorEnabled = false; + allowDisplayOfInsecureContent = true; + allowFileAccessFromFileURLs = true; + allowRunningOfInsecureContent = true; + authorAndUserStylesEnabled = true; + defaultTextEncodingName = WebString::fromUTF8("ISO-8859-1"); + developerExtrasEnabled = true; + 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; + pageCacheSupportsPlugins = 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; + acceleratedCompositingForVideoEnabled = false; + acceleratedCompositingEnabled = false; + compositeToTexture = false; + accelerated2dCanvasEnabled = false; + legacyAccelerated2dCanvasEnabled = false; + acceleratedPaintingEnabled = false; + forceCompositingMode = false; + hixie76WebSocketProtocolEnabled = true; + perTilePaintingEnabled = false; +} + +static void setStandardFontFamilyWrapper(WebSettings* settings, const WebKit::WebString& font, UScriptCode script) +{ + settings->setStandardFontFamily(font, script); +} + +static void setFixedFontFamilyWrapper(WebSettings* settings, const WebKit::WebString& font, UScriptCode script) +{ + settings->setFixedFontFamily(font, script); +} + +static void setSerifFontFamilyWrapper(WebSettings* settings, const WebKit::WebString& font, UScriptCode script) +{ + settings->setSerifFontFamily(font, script); +} + +static void setSansSerifFontFamilyWrapper(WebSettings* settings, const WebKit::WebString& font, UScriptCode script) +{ + settings->setSansSerifFontFamily(font, script); +} + +static void setCursiveFontFamilyWrapper(WebSettings* settings, const WebKit::WebString& font, UScriptCode script) +{ + settings->setCursiveFontFamily(font, script); +} + +static void setFantasyFontFamilyWrapper(WebSettings* settings, const WebKit::WebString& font, UScriptCode script) +{ + settings->setFantasyFontFamily(font, script); +} + +typedef void (*SetFontFamilyWrapper)(WebSettings*, const WebString&, UScriptCode); + +static void applyFontMap(WebSettings* settings, const WebPreferences::ScriptFontFamilyMap& map, SetFontFamilyWrapper setter) +{ + for (WebPreferences::ScriptFontFamilyMap::const_iterator iter = map.begin(); iter != map.end(); ++iter) { + const WebString& font = iter->second; + if (!font.isNull() && !font.isEmpty()) + (*setter)(settings, font, static_cast<UScriptCode>(iter->first)); + } +} + +void WebPreferences::applyTo(WebView* webView) +{ + WebSettings* settings = webView->settings(); + settings->setStandardFontFamily(standardFontFamily); + settings->setFixedFontFamily(fixedFontFamily); + settings->setSerifFontFamily(serifFontFamily); + settings->setSansSerifFontFamily(sansSerifFontFamily); + settings->setCursiveFontFamily(cursiveFontFamily); + settings->setFantasyFontFamily(fantasyFontFamily); + + applyFontMap(settings, standardFontMap, setStandardFontFamilyWrapper); + applyFontMap(settings, fixedFontMap, setFixedFontFamilyWrapper); + applyFontMap(settings, serifFontMap, setSerifFontFamilyWrapper); + applyFontMap(settings, sansSerifFontMap, setSansSerifFontFamilyWrapper); + applyFontMap(settings, cursiveFontMap, setCursiveFontFamilyWrapper); + applyFontMap(settings, fantasyFontMap, setFantasyFontFamilyWrapper); + + settings->setDefaultFontSize(defaultFontSize); + settings->setDefaultFixedFontSize(defaultFixedFontSize); + settings->setMinimumFontSize(minimumFontSize); + settings->setMinimumLogicalFontSize(minimumLogicalFontSize); + settings->setMinimumAccelerated2dCanvasSize(minimumAccelerated2dCanvasSize); + + settings->setDOMPasteAllowed(DOMPasteAllowed); + settings->setXSSAuditorEnabled(XSSAuditorEnabled); + settings->setAllowDisplayOfInsecureContent(allowDisplayOfInsecureContent); + settings->setAllowFileAccessFromFileURLs(allowFileAccessFromFileURLs); + settings->setAllowRunningOfInsecureContent(allowRunningOfInsecureContent); + 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->setPageCacheSupportsPlugins(pageCacheSupportsPlugins); + 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); + settings->setAcceleratedCompositingEnabled(acceleratedCompositingEnabled); + settings->setAcceleratedCompositingForVideoEnabled(acceleratedCompositingForVideoEnabled); + settings->setCompositeToTextureEnabled(compositeToTexture); + settings->setForceCompositingMode(forceCompositingMode); + settings->setAccelerated2dCanvasEnabled(accelerated2dCanvasEnabled); + settings->setLegacyAccelerated2dCanvasEnabled(legacyAccelerated2dCanvasEnabled); + settings->setAcceleratedPaintingEnabled(acceleratedPaintingEnabled); + settings->setHixie76WebSocketProtocolEnabled(hixie76WebSocketProtocolEnabled); + settings->setPerTilePaintingEnabled(perTilePaintingEnabled); + + // Fixed values. + settings->setTextDirectionSubmenuInclusionBehaviorNeverIncluded(); + settings->setDownloadableBinaryFontsEnabled(true); + settings->setAllowScriptsToCloseWindows(false); + settings->setNeedsSiteSpecificQuirks(true); + settings->setEditableLinkBehaviorNeverLive(); + settings->setEnableScrollAnimator(false); + settings->setFontRenderingModeNormal(); + settings->setMockScrollbarsEnabled(false); + settings->setTextDirectionSubmenuInclusionBehaviorNeverIncluded(); + settings->setUsesEncodingDetector(false); + settings->setImagesEnabled(true); + settings->setInteractiveFormValidationEnabled(true); + // Enable fullscreen so the fullscreen layout tests can run. + settings->setFullScreenEnabled(true); + settings->setValidationMessageTimerMagnification(-1); + settings->setVisualWordMovementEnabled(false); +} diff --git a/Tools/DumpRenderTree/chromium/WebPreferences.h b/Tools/DumpRenderTree/chromium/WebPreferences.h new file mode 100644 index 000000000..2e2135ab7 --- /dev/null +++ b/Tools/DumpRenderTree/chromium/WebPreferences.h @@ -0,0 +1,119 @@ +/* + * Copyright (C) 2010 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef WebPreferences_h +#define WebPreferences_h + +#include "WebSettings.h" +#include "platform/WebString.h" +#include "platform/WebURL.h" +#include <wtf/HashMap.h> + +namespace WebKit { +class WebView; +} + +struct WebPreferences { + WebKit::WebString standardFontFamily; + WebKit::WebString fixedFontFamily; + WebKit::WebString serifFontFamily; + WebKit::WebString sansSerifFontFamily; + WebKit::WebString cursiveFontFamily; + WebKit::WebString fantasyFontFamily; + + // UScriptCode uses -1 and 0 for UScriptInvalidCode and UScriptCommon. + // We need to use -2 and -3 for empty value and deleted value. + // (See WebCore::ScriptFontFamilyMap) + struct UScriptCodeHashTraits : WTF::GenericHashTraits<int> { + static const bool emptyValueIsZero = false; + static int emptyValue() { return -2; } + static void constructDeletedValue(int& slot) { slot = -3; } + static bool isDeletedValue(int value) { return value == -3; } + }; + + // Map of UScriptCode to font such as USCRIPT_ARABIC to "My Arabic Font". + typedef HashMap<int, WebKit::WebString, DefaultHash<int>::Hash, UScriptCodeHashTraits> ScriptFontFamilyMap; + ScriptFontFamilyMap standardFontMap; + ScriptFontFamilyMap fixedFontMap; + ScriptFontFamilyMap serifFontMap; + ScriptFontFamilyMap sansSerifFontMap; + ScriptFontFamilyMap cursiveFontMap; + ScriptFontFamilyMap fantasyFontMap; + + int defaultFontSize; + int defaultFixedFontSize; + int minimumFontSize; + int minimumLogicalFontSize; + int minimumAccelerated2dCanvasSize; + + bool DOMPasteAllowed; + bool XSSAuditorEnabled; + bool allowDisplayOfInsecureContent; + bool allowFileAccessFromFileURLs; + bool allowRunningOfInsecureContent; + 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 pageCacheSupportsPlugins; + bool webSecurityEnabled; + bool allowUniversalAccessFromFileURLs; + WebKit::WebSettings::EditingBehavior editingBehavior; + bool tabsToLinks; + bool hyperlinkAuditingEnabled; + bool caretBrowsingEnabled; + bool acceleratedCompositingForVideoEnabled; + bool acceleratedCompositingEnabled; + bool compositeToTexture; + bool forceCompositingMode; + bool accelerated2dCanvasEnabled; + bool legacyAccelerated2dCanvasEnabled; + bool acceleratedPaintingEnabled; + bool hixie76WebSocketProtocolEnabled; + bool perTilePaintingEnabled; + + 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 000000000..13b798284 --- /dev/null +++ b/Tools/DumpRenderTree/chromium/WebThemeControlDRTWin.cpp @@ -0,0 +1,527 @@ +/* + * 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/skia_utils_win.h" +#include "third_party/skia/include/core/SkCanvas.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; + +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(SkCanvas* 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; + + 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: + case VerticalSliderThumbType: + // 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 VerticalSliderTrackType: { + // Draw a narrow rect for the track plus box hatches on the ends. + SkIRect lirect; + lirect = m_irect; + lirect.inset(halfWidth - sliderIndent, noOffset); + box(lirect, m_bgColor); + line(m_left, m_top, m_right, m_top, m_edgeColor); + line(m_left, m_bottom, 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(); +} + +// 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; + + 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(); +} + +void WebThemeControlDRTWin::drawProgressBar(const SkIRect& fillRect) +{ + SkPaint paint; + + 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(); +} + diff --git a/Tools/DumpRenderTree/chromium/WebThemeControlDRTWin.h b/Tools/DumpRenderTree/chromium/WebThemeControlDRTWin.h new file mode 100644 index 000000000..ef731ab39 --- /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 "third_party/skia/include/core/SkColor.h" +#include "third_party/skia/include/core/SkRect.h" +#include <wtf/Noncopyable.h> + +// Skia forward declarations +class SkCanvas; + +class WebThemeControlDRTWin { + WTF_MAKE_NONCOPYABLE(WebThemeControlDRTWin); +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, + VerticalSliderTrackType, + VerticalSliderThumbType, + DropDownButtonType, + ProgressBarType + }; + + // Constructs a control of the given size, type and state to draw + // on to the given canvas. + WebThemeControlDRTWin(SkCanvas*, const SkIRect&, Type, State); + ~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); + + // 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); + + + // 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); + + // 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); + + // 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); + + // 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); + + // 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); + + // 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(); + + SkCanvas* 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/WebThemeEngineDRTMac.h b/Tools/DumpRenderTree/chromium/WebThemeEngineDRTMac.h new file mode 100644 index 000000000..2398be3ae --- /dev/null +++ b/Tools/DumpRenderTree/chromium/WebThemeEngineDRTMac.h @@ -0,0 +1,59 @@ +/* + * 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: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``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 APPLE INC. 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 in such a way that we match the Mac +// port rendering more than usual Chromium path, thus allowing us to share +// more pixel baselines. + +#ifndef WebThemeEngineDRTMac_h +#define WebThemeEngineDRTMac_h + +#include "third_party/WebKit/Source/WebKit/chromium/public/platform/mac/WebThemeEngine.h" + +class WebThemeEngineDRTMac : public WebKit::WebThemeEngine { +public: + virtual void paintScrollbarThumb( + WebKit::WebCanvas*, + WebKit::WebThemeEngine::State, + WebKit::WebThemeEngine::Size, + const WebKit::WebRect&, + const WebKit::WebThemeEngine::ScrollbarInfo&); + +private: + virtual void paintHIThemeScrollbarThumb( + WebKit::WebCanvas*, + WebKit::WebThemeEngine::State, + WebKit::WebThemeEngine::Size, + const WebKit::WebRect&, + const WebKit::WebThemeEngine::ScrollbarInfo&); + virtual void paintNSScrollerScrollbarThumb( + WebKit::WebCanvas*, + WebKit::WebThemeEngine::State, + WebKit::WebThemeEngine::Size, + const WebKit::WebRect&, + const WebKit::WebThemeEngine::ScrollbarInfo&); +}; + +#endif // WebThemeEngineDRTMac_h diff --git a/Tools/DumpRenderTree/chromium/WebThemeEngineDRTMac.mm b/Tools/DumpRenderTree/chromium/WebThemeEngineDRTMac.mm new file mode 100644 index 000000000..9a1c30857 --- /dev/null +++ b/Tools/DumpRenderTree/chromium/WebThemeEngineDRTMac.mm @@ -0,0 +1,202 @@ +/* + * 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 "WebThemeEngineDRTMac.h" + +#include "third_party/WebKit/Source/WebKit/chromium/public/platform/WebCanvas.h" +#include "third_party/WebKit/Source/WebKit/chromium/public/platform/WebRect.h" +#import <AppKit/NSAffineTransform.h> +#import <AppKit/NSGraphicsContext.h> +#import <AppKit/NSScroller.h> +#import <AppKit/NSWindow.h> +#include <Carbon/Carbon.h> + +#if WEBKIT_USING_SKIA +#include "skia/ext/skia_utils_mac.h" +#endif + +using WebKit::WebCanvas; +using WebKit::WebRect; +using WebKit::WebThemeEngine; + +// We can't directly tell the NSScroller to draw itself as active or inactive, +// instead we have to make it a child of an (in)active window. This class lets +// us fake that parent window. +@interface FakeActiveWindow : NSWindow { +@private + BOOL hasActiveControls; +} ++ (NSWindow*)alwaysActiveWindow; ++ (NSWindow*)alwaysInactiveWindow; +- (id)initWithActiveControls:(BOOL)_hasActiveControls; +- (BOOL)_hasActiveControls; +@end + +@implementation FakeActiveWindow + +static NSWindow* alwaysActiveWindow = nil; +static NSWindow* alwaysInactiveWindow = nil; + ++ (NSWindow*)alwaysActiveWindow +{ + if (alwaysActiveWindow == nil) + alwaysActiveWindow = [[self alloc] initWithActiveControls:YES]; + return alwaysActiveWindow; +} + ++ (NSWindow*)alwaysInactiveWindow +{ + if (alwaysInactiveWindow == nil) + alwaysInactiveWindow = [[self alloc] initWithActiveControls:NO]; + return alwaysInactiveWindow; +} + +- (id)initWithActiveControls:(BOOL)_hasActiveControls +{ + self = [super init]; + hasActiveControls = _hasActiveControls; + return self; +} + +- (BOOL)_hasActiveControls +{ + return hasActiveControls; +} + +@end + +void WebThemeEngineDRTMac::paintScrollbarThumb( + WebCanvas* canvas, + WebThemeEngine::State state, + WebThemeEngine::Size size, + const WebRect& rect, + const WebThemeEngine::ScrollbarInfo& scrollbarInfo) +{ + // To match the Mac port, we still use HITheme for inner scrollbars. + if (scrollbarInfo.parent == WebThemeEngine::ScrollbarParentRenderLayer) + paintHIThemeScrollbarThumb(canvas, state, size, rect, scrollbarInfo); + else + paintNSScrollerScrollbarThumb(canvas, state, size, rect, scrollbarInfo); +} + +static ThemeTrackEnableState stateToHIEnableState(WebThemeEngine::State state) +{ + switch (state) { + case WebThemeEngine::StateDisabled: + return kThemeTrackDisabled; + case WebThemeEngine::StateInactive: + return kThemeTrackInactive; + default: + return kThemeTrackActive; + } +} + +// Duplicated from webkit/glue/webthemeengine_impl_mac.cc in the downstream +// Chromium WebThemeEngine implementation. +void WebThemeEngineDRTMac::paintHIThemeScrollbarThumb( + WebCanvas* canvas, + WebThemeEngine::State state, + WebThemeEngine::Size size, + const WebRect& rect, + const WebThemeEngine::ScrollbarInfo& scrollbarInfo) +{ + HIThemeTrackDrawInfo trackInfo; + trackInfo.version = 0; + trackInfo.kind = size == WebThemeEngine::SizeRegular ? kThemeMediumScrollBar : kThemeSmallScrollBar; + trackInfo.bounds = CGRectMake(rect.x, rect.y, rect.width, rect.height); + trackInfo.min = 0; + trackInfo.max = scrollbarInfo.maxValue; + trackInfo.value = scrollbarInfo.currentValue; + trackInfo.trackInfo.scrollbar.viewsize = scrollbarInfo.visibleSize; + trackInfo.attributes = 0; + if (scrollbarInfo.orientation == WebThemeEngine::ScrollbarOrientationHorizontal) + trackInfo.attributes |= kThemeTrackHorizontal; + + trackInfo.enableState = stateToHIEnableState(state); + + trackInfo.trackInfo.scrollbar.pressState = + state == WebThemeEngine::StatePressed ? kThemeThumbPressed : 0; + trackInfo.attributes |= (kThemeTrackShowThumb | kThemeTrackHideTrack); +#if WEBKIT_USING_SKIA + gfx::SkiaBitLocker bitLocker(canvas); + CGContextRef cgContext = bitLocker.cgContext(); +#else + CGContextRef cgContext = canvas; +#endif + HIThemeDrawTrack(&trackInfo, 0, cgContext, kHIThemeOrientationNormal); +} + +void WebThemeEngineDRTMac::paintNSScrollerScrollbarThumb( + WebCanvas* canvas, + WebThemeEngine::State state, + WebThemeEngine::Size size, + const WebRect& rect, + const WebThemeEngine::ScrollbarInfo& scrollbarInfo) +{ + [NSGraphicsContext saveGraphicsState]; + NSScroller* scroller = [[NSScroller alloc] initWithFrame:NSMakeRect(rect.x, rect.y, rect.width, rect.height)]; + [scroller setEnabled:state != WebThemeEngine::StateDisabled]; + if (state == WebThemeEngine::StateInactive) + [[[FakeActiveWindow alwaysInactiveWindow] contentView] addSubview:scroller]; + else + [[[FakeActiveWindow alwaysActiveWindow] contentView] addSubview:scroller]; + + [scroller setControlSize:size == WebThemeEngine::SizeRegular ? NSRegularControlSize : NSSmallControlSize]; + + double value = double(scrollbarInfo.currentValue) / double(scrollbarInfo.maxValue); + [scroller setDoubleValue: value]; + + float knobProportion = float(scrollbarInfo.visibleSize) / float(scrollbarInfo.totalSize); + [scroller setKnobProportion: knobProportion]; + +#if WEBKIT_USING_SKIA + gfx::SkiaBitLocker bitLocker(canvas); + CGContextRef cgContext = bitLocker.cgContext(); +#else + CGContextRef cgContext = canvas; +#endif + NSGraphicsContext* nsGraphicsContext = [NSGraphicsContext graphicsContextWithGraphicsPort:cgContext flipped:YES]; + [NSGraphicsContext setCurrentContext:nsGraphicsContext]; + + // Despite passing in frameRect() to the scroller, it always draws at (0, 0). + // Force it to draw in the right location by translating the whole graphics + // context. + CGContextSaveGState(cgContext); + NSAffineTransform *transform = [NSAffineTransform transform]; + [transform translateXBy:rect.x yBy:rect.y]; + [transform concat]; + + [scroller drawKnob]; + CGContextRestoreGState(cgContext); + + [scroller release]; + + [NSGraphicsContext restoreGraphicsState]; +} diff --git a/Tools/DumpRenderTree/chromium/WebThemeEngineDRTWin.cpp b/Tools/DumpRenderTree/chromium/WebThemeEngineDRTWin.cpp new file mode 100755 index 000000000..789b1c816 --- /dev/null +++ b/Tools/DumpRenderTree/chromium/WebThemeEngineDRTWin.cpp @@ -0,0 +1,787 @@ +/* + * 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 "platform/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_THUMBVERT == part) { + ctype = WebThemeControlDRTWin::VerticalSliderThumbType; + 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(state == TRS_NORMAL); + ASSERT(classicState == dfcsNormal); + cstate = WebThemeControlDRTWin::NormalState; + } else if (TKP_TRACKVERT == part) { + ctype = WebThemeControlDRTWin::VerticalSliderTrackType; + ASSERT(state == TRVS_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 000000000..daa911166 --- /dev/null +++ b/Tools/DumpRenderTree/chromium/WebThemeEngineDRTWin.h @@ -0,0 +1,98 @@ +/* + * 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 "platform/win/WebThemeEngine.h" +#include <wtf/Noncopyable.h> + +class WebThemeEngineDRTWin : public WebKit::WebThemeEngine { + WTF_MAKE_NONCOPYABLE(WebThemeEngineDRTWin); +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 000000000..1b0687dd0 --- /dev/null +++ b/Tools/DumpRenderTree/chromium/WebViewHost.cpp @@ -0,0 +1,1687 @@ +/* + * Copyright (C) 2010, 2011 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 "TestWebPlugin.h" +#include "TestWebWorker.h" +#include "platform/WebCString.h" +#include "WebConsoleMessage.h" +#include "WebContextMenuData.h" +#include "WebDOMMessageEvent.h" +#include "WebDataSource.h" +#include "WebDeviceOrientationClientMock.h" +#include "platform/WebDragData.h" +#include "WebElement.h" +#include "WebFrame.h" +#include "WebGeolocationClientMock.h" +#include "WebHistoryItem.h" +#include "WebKit.h" +#include "WebNode.h" +#include "WebPluginParams.h" +#include "WebPopupMenu.h" +#include "WebPopupType.h" +#include "WebRange.h" +#include "platform/WebRect.h" +#include "WebScreenInfo.h" +#include "platform/WebSize.h" +#include "WebSpeechInputControllerMock.h" +#include "WebStorageNamespace.h" +#include "WebTextCheckingCompletion.h" +#include "WebTextCheckingResult.h" +#include "platform/WebThread.h" +#include "platform/WebURLRequest.h" +#include "platform/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> +#include <wtf/Vector.h> + +using namespace WebCore; +using namespace WebKit; +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 dragData (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 WebURLRequest& request, const WebWindowFeatures&, const WebString&) +{ + if (!layoutTestController()->canOpenWindows()) + return 0; + if (layoutTestController()->shouldDumpCreateView()) + fprintf(stdout, "createView(%s)\n", URLDescription(request.url()).c_str()); + return m_shell->createNewWindow(WebURL())->webView(); +} + +WebWidget* WebViewHost::createPopupMenu(WebPopupType type) +{ + switch (type) { + case WebKit::WebPopupTypeNone: + break; + case WebKit::WebPopupTypeSelect: + case WebKit::WebPopupTypeSuggestion: + m_popupmenus.append(WebPopupMenu::create(0)); + return m_popupmenus.last(); + } + return 0; +} + +WebWidget* WebViewHost::createPopupMenu(const WebPopupMenuInfo&) +{ + // Do not use this method. It's been replaced by createExternalPopupMenu. + ASSERT_NOT_REACHED(); + 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. + if (!m_logConsoleOutput) + return; + 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() +{ + if (layoutTestController()->shouldDumpProgressFinishedCallback()) + fputs("postProgressFinishedNotification\n", stdout); + 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, WebVector<WebString>* optionalSuggestions) +{ + // Check the spelling of the given text. + m_spellcheck.spellCheckWord(text, &misspelledOffset, &misspelledLength); +} + +void WebViewHost::requestCheckingOfText(const WebString& text, WebTextCheckingCompletion* completion) +{ + if (text.isEmpty()) { + if (completion) + completion->didFinishCheckingText(Vector<WebTextCheckingResult>()); + return; + } + + m_lastRequestedTextCheckingCompletion = completion; + m_lastRequestedTextCheckString = text; + postDelayedTask(new HostMethodTask(this, &WebViewHost::finishLastTextCheck), 0); +} + +void WebViewHost::finishLastTextCheck() +{ + Vector<WebTextCheckingResult> results; + // FIXME: Do the grammar check. + int offset = 0; + String text(m_lastRequestedTextCheckString.data(), m_lastRequestedTextCheckString.length()); + while (text.length()) { + int misspelledPosition = 0; + int misspelledLength = 0; + m_spellcheck.spellCheckWord(WebString(text.characters(), text.length()), &misspelledPosition, &misspelledLength); + if (!misspelledLength) + break; + results.append(WebTextCheckingResult(WebTextCheckingResult::ErrorSpelling, offset + misspelledPosition, misspelledLength)); + text = text.substring(misspelledPosition + misspelledLength); + offset += misspelledPosition + misspelledLength; + } + + m_lastRequestedTextCheckingCompletion->didFinishCheckingText(results); + m_lastRequestedTextCheckingCompletion = 0; +} + + +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& message) +{ + printf("CONFIRM NAVIGATION: %s\n", message.utf8().data()); + return !layoutTestController()->shouldStayOnPageAfterHandlingBeforeUnload(); +} + +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::didUpdateLayout() +{ +#if OS(MAC_OS_X) + static bool queryingPreferredSize = false; + if (queryingPreferredSize) + return; + + queryingPreferredSize = true; + // Query preferred width to emulate the same functionality in Chromium: + // see RenderView::CheckPreferredSize (src/content/renderer/render_view.cc) + // and TabContentsViewMac::RenderViewCreated (src/chrome/browser/tab_contents/tab_contents_view_mac.mm) + webView()->mainFrame()->contentsPreferredWidth(); + webView()->mainFrame()->documentElementScrollHeight(); + queryingPreferredSize = false; +#endif +} + +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); + + const char* notificationName; + switch (notification) { + case WebAccessibilityNotificationActiveDescendantChanged: + notificationName = "ActiveDescendantChanged"; + break; + case WebAccessibilityNotificationAutocorrectionOccured: + notificationName = "AutocorrectionOccured"; + break; + case WebAccessibilityNotificationCheckedStateChanged: + notificationName = "CheckedStateChanged"; + break; + case WebAccessibilityNotificationChildrenChanged: + notificationName = "ChildrenChanged"; + break; + case WebAccessibilityNotificationFocusedUIElementChanged: + notificationName = "FocusedUIElementChanged"; + break; + case WebAccessibilityNotificationLayoutComplete: + notificationName = "LayoutComplete"; + break; + case WebAccessibilityNotificationLoadComplete: + notificationName = "LoadComplete"; + break; + case WebAccessibilityNotificationSelectedChildrenChanged: + notificationName = "SelectedChildrenChanged"; + break; + case WebAccessibilityNotificationSelectedTextChanged: + notificationName = "SelectedTextChanged"; + break; + case WebAccessibilityNotificationValueChanged: + notificationName = "ValueChanged"; + break; + case WebAccessibilityNotificationScrolledToAnchor: + notificationName = "ScrolledToAnchor"; + break; + case WebAccessibilityNotificationLiveRegionChanged: + notificationName = "LiveRegionChanged"; + break; + case WebAccessibilityNotificationMenuListItemSelected: + notificationName = "MenuListItemSelected"; + break; + case WebAccessibilityNotificationMenuListValueChanged: + notificationName = "MenuListValueChanged"; + break; + case WebAccessibilityNotificationRowCountChanged: + notificationName = "RowCountChanged"; + break; + case WebAccessibilityNotificationRowCollapsed: + notificationName = "RowCollapsed"; + break; + case WebAccessibilityNotificationRowExpanded: + notificationName = "RowExpanded"; + break; + case WebAccessibilityNotificationInvalidStatusChanged: + notificationName = "InvalidStatusChanged"; + break; + default: + notificationName = "UnknownNotification"; + break; + } + + m_shell->accessibilityController()->notificationReceived(obj, notificationName); + + if (m_shell->accessibilityController()->shouldLogAccessibilityEvents()) { + printf("AccessibilityNotification - %s", notificationName); + + 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(); +} + +WebKit::WebGeolocationClient* WebViewHost::geolocationClient() +{ + return geolocationClientMock(); +} + +WebKit::WebGeolocationClientMock* WebViewHost::geolocationClientMock() +{ + if (!m_geolocationClientMock) + m_geolocationClientMock = adoptPtr(WebGeolocationClientMock::create()); + return m_geolocationClientMock.get(); +} + +WebSpeechInputController* WebViewHost::speechInputController(WebKit::WebSpeechInputListener* listener) +{ + if (!m_speechInputControllerMock) + m_speechInputControllerMock = adoptPtr(WebSpeechInputControllerMock::create(listener)); + return m_speechInputControllerMock.get(); +} + +WebDeviceOrientationClientMock* WebViewHost::deviceOrientationClientMock() +{ + if (!m_deviceOrientationClientMock.get()) + m_deviceOrientationClientMock = adoptPtr(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); +} + +#if ENABLE(REQUEST_ANIMATION_FRAME) +void WebViewHost::scheduleAnimation() +{ + postDelayedTask(new HostMethodTask(this, &WebViewHost::scheduleComposite), 0); +} +#endif + +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. +} + +void WebViewHost::closeWidgetSoon() +{ + postDelayedTask(new HostMethodTask(this, &WebViewHost::closeWidget), 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); +} + +bool WebViewHost::enterFullScreen() +{ + postDelayedTask(new HostMethodTask(this, &WebViewHost::enterFullScreenNow), 0); + return true; +} + +void WebViewHost::exitFullScreen() +{ + postDelayedTask(new HostMethodTask(this, &WebViewHost::exitFullScreenNow), 0); +} + +// WebFrameClient ------------------------------------------------------------ + +WebPlugin* WebViewHost::createPlugin(WebFrame* frame, const WebPluginParams& params) +{ + if (params.mimeType == TestWebPlugin::mimeType()) + return new TestWebPlugin(frame, params); + + return webkit_support::CreateWebPlugin(frame, params); +} + +WebWorker* WebViewHost::createWorker(WebFrame*, WebSharedWorkerClient*) +{ + 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); +} + +void WebViewHost::loadURLExternally(WebFrame* frame, const WebURLRequest& request, WebNavigationPolicy policy) +{ + loadURLExternally(frame, request, policy, WebString()); +} + +void WebViewHost::loadURLExternally(WebFrame*, const WebURLRequest& request, WebNavigationPolicy policy, const WebString& downloadName) +{ + 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()) { + printFrameDescription(frame); + printf(" - willPerformClientRedirectToURL: %s \n", to.spec().data()); + } + + if (m_shell->shouldDumpUserGestureInFrameLoadCallbacks()) + printFrameUserGestureStatus(frame, " - in willPerformClientRedirect\n"); +} + +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->shouldDumpFrameLoadCallbacks()) { + printFrameDescription(frame); + fputs(" - didStartProvisionalLoadForFrame\n", stdout); + } + + if (m_shell->shouldDumpUserGestureInFrameLoadCallbacks()) + printFrameUserGestureStatus(frame, " - in didStartProvisionalLoadForFrame\n"); + + 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, WebTextDirection direction) +{ + 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); + layoutTestController()->setTitleTextDirection(direction); +} + +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, const WebURL& insecureURL) +{ + if (m_shell->shouldDumpFrameLoadCallbacks()) + fputs("didRunInsecureContent\n", stdout); +} + +void WebViewHost::didDetectXSS(WebFrame*, const WebURL&, bool) +{ + if (m_shell->shouldDumpFrameLoadCallbacks()) + fputs("didDetectXSS\n", stdout); +} + +void WebViewHost::openFileSystem(WebFrame* frame, WebFileSystem::Type type, long long size, bool create, WebFileSystemCallbacks* callbacks) +{ + webkit_support::OpenFileSystem(frame, type, size, create, callbacks); +} + +bool WebViewHost::willCheckAndDispatchMessageEvent(WebFrame* source, WebSecurityOrigin target, WebDOMMessageEvent event) +{ + if (m_shell->layoutTestController()->shouldInterceptPostMessage()) { + fputs("intercepted postMessage\n", stdout); + return true; + } + + return false; +} + +// Public functions ----------------------------------------------------------- + +WebViewHost::WebViewHost(TestShell* shell) + : m_shell(shell) + , m_webWidget(0) + , m_lastRequestedTextCheckingCompletion(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()); + } + + for (Vector<WebKit::WebWidget*>::iterator it = m_popupmenus.begin(); + it < m_popupmenus.end(); ++it) + (*it)->close(); + + webWidget()->close(); + if (m_inModalLoop) + webkit_support::QuitMessageLoop(); +} + +void WebViewHost::setWebWidget(WebKit::WebWidget* widget) +{ + m_webWidget = widget; + webView()->setSpellCheckClient(this); +} + +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; + m_logConsoleOutput = 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 = adoptPtr(new TestNavigationController(this)); + + m_pendingExtraData.clear(); + m_resourceIdentifierMap.clear(); + m_clearHeaders.clear(); + m_editCommandName.clear(); + m_editCommandValue.clear(); + + if (m_geolocationClientMock.get()) + m_geolocationClientMock->resetMock(); + + if (m_speechInputControllerMock.get()) + m_speechInputControllerMock->clearResults(); + + m_currentCursor = WebCursorInfo(); + m_windowRect = WebRect(); + m_paintRect = WebRect(); + + if (m_webWidget) { + webView()->mainFrame()->setName(WebString()); + webView()->settings()->setMinimumTimerInterval(webkit_support::GetForegroundTabTimerInterval()); + } +} + +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::setLogConsoleOutput(bool enabled) +{ + m_logConsoleOutput = enabled; +} + +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(adoptPtr(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(nullptr); + + // 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(PassOwnPtr<TestShellExtraData> extraData) +{ + m_pendingExtraData = extraData; +} + +void WebViewHost::setPageTitle(const WebString&) +{ + // Nothing to do in layout test. +} + +void WebViewHost::setAddressBarURL(const WebURL&) +{ + // Nothing to do in layout test. +} + +void WebViewHost::enterFullScreenNow() +{ + webView()->willEnterFullScreen(); + webView()->didEnterFullScreen(); +} + +void WebViewHost::exitFullScreenNow() +{ + webView()->willExitFullScreen(); + webView()->didExitFullScreen(); +} + +// 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 USE(CG) + webWidget()->paint(skia::BeginPlatformPaint(canvas()), rect); + skia::EndPlatformPaint(canvas()); +#else + webWidget()->paint(canvas(), rect); +#endif + m_isPainting = false; +} + +void WebViewHost::paintInvalidatedRegion() +{ +#if ENABLE(REQUEST_ANIMATION_FRAME) + webWidget()->animate(0.0); +#endif + 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()); +} + +void WebViewHost::paintPagesWithBoundaries() +{ + ASSERT(!m_isPainting); + ASSERT(canvas()); + m_isPainting = true; + + WebSize pageSizeInPixels = webWidget()->size(); + WebFrame* webFrame = webView()->mainFrame(); + + int pageCount = webFrame->printBegin(pageSizeInPixels); + int totalHeight = pageCount * (pageSizeInPixels.height + 1) - 1; + + SkCanvas* testCanvas = skia::TryCreateBitmapCanvas(pageSizeInPixels.width, totalHeight, true); + if (testCanvas) { + discardBackingStore(); + m_canvas = adoptPtr(testCanvas); + } else { + webFrame->printEnd(); + return; + } + +#if WEBKIT_USING_SKIA + WebCanvas* webCanvas = canvas(); +#elif WEBKIT_USING_CG + const SkBitmap& canvasBitmap = canvas()->getDevice()->accessBitmap(false); + WebCanvas* webCanvas = CGBitmapContextCreate(canvasBitmap.getPixels(), + pageSizeInPixels.width, totalHeight, + 8, pageSizeInPixels.width * 4, + CGColorSpaceCreateDeviceRGB(), + kCGImageAlphaPremultipliedFirst | + kCGBitmapByteOrder32Host); + CGContextTranslateCTM(webCanvas, 0.0, totalHeight); + CGContextScaleCTM(webCanvas, 1.0, -1.0f); +#endif + + webFrame->printPagesWithBoundaries(webCanvas, pageSizeInPixels); + webFrame->printEnd(); + + m_isPainting = false; +} + +SkCanvas* WebViewHost::canvas() +{ + if (m_canvas) + return m_canvas.get(); + WebSize widgetSize = webWidget()->size(); + resetScrollRect(); + m_canvas = adoptPtr(skia::CreateBitmapCanvas(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 000000000..0bc4c38e6 --- /dev/null +++ b/Tools/DumpRenderTree/chromium/WebViewHost.h @@ -0,0 +1,368 @@ +/* + * 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 "Task.h" +#include "TestNavigationController.h" +#include "WebAccessibilityNotification.h" +#include "WebCursorInfo.h" +#include "WebFrameClient.h" +#include "WebSpellCheckClient.h" +#include "WebViewClient.h" +#include <wtf/HashMap.h> +#include <wtf/HashSet.h> +#include <wtf/Vector.h> +#include <wtf/text/WTFString.h> + +class LayoutTestController; +class SkCanvas; +class TestShell; + +namespace WebKit { +class WebFrame; +class WebDeviceOrientationClient; +class WebDeviceOrientationClientMock; +class WebGeolocationClient; +class WebGeolocationClientMock; +class WebGeolocationServiceMock; +class WebSharedWorkerClient; +class WebSpeechInputController; +class WebSpeechInputControllerMock; +class WebSpeechInputListener; +class WebURL; +struct WebRect; +struct WebURLError; +struct WebWindowFeatures; +} + +class WebViewHost : public WebKit::WebSpellCheckClient, public WebKit::WebViewClient, public WebKit::WebFrameClient, public NavigationHost { + public: + WebViewHost(TestShell*); + virtual ~WebViewHost(); + void setWebWidget(WebKit::WebWidget*); + WebKit::WebView* webView() const; + WebKit::WebWidget* webWidget() const; + void reset(); + void setSelectTrailingWhitespaceEnabled(bool); + void setSmartInsertDeleteEnabled(bool); + void setLogConsoleOutput(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(PassOwnPtr<TestShellExtraData>); + + void paintRect(const WebKit::WebRect&); + void updatePaintRect(const WebKit::WebRect&); + void paintInvalidatedRegion(); + void paintPagesWithBoundaries(); + SkCanvas* 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::WebSpellCheckClient + virtual void spellCheck(const WebKit::WebString&, int& offset, int& length, WebKit::WebVector<WebKit::WebString>* optionalSuggestions); + virtual void requestCheckingOfText(const WebKit::WebString&, WebKit::WebTextCheckingCompletion*); + virtual WebKit::WebString autoCorrectWord(const WebKit::WebString&); + + // WebKit::WebViewClient + virtual WebKit::WebView* createView(WebKit::WebFrame*, const WebKit::WebURLRequest&, 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 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 didUpdateLayout(); + virtual void navigateBackForwardSoon(int offset); + virtual int historyBackListCount(); + virtual int historyForwardListCount(); + virtual void postAccessibilityNotification(const WebKit::WebAccessibilityObject&, WebKit::WebAccessibilityNotification); + virtual WebKit::WebNotificationPresenter* notificationPresenter(); + virtual WebKit::WebGeolocationClient* geolocationClient(); + 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(); +#if ENABLE(REQUEST_ANIMATION_FRAME) + virtual void scheduleAnimation(); +#endif + virtual void didFocus(); + virtual void didBlur(); + virtual void didChangeCursor(const WebKit::WebCursorInfo&); + virtual void closeWidgetSoon(); + virtual void show(WebKit::WebNavigationPolicy); + virtual void runModal(); + virtual bool enterFullScreen(); + virtual void exitFullScreen(); + 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::WebSharedWorkerClient*); + virtual WebKit::WebMediaPlayer* createMediaPlayer(WebKit::WebFrame*, WebKit::WebMediaPlayerClient*); + virtual WebKit::WebApplicationCacheHost* createApplicationCacheHost(WebKit::WebFrame*, WebKit::WebApplicationCacheHostClient*); + virtual void loadURLExternally(WebKit::WebFrame*, const WebKit::WebURLRequest&, WebKit::WebNavigationPolicy); + virtual void loadURLExternally(WebKit::WebFrame*, const WebKit::WebURLRequest&, WebKit::WebNavigationPolicy, const WebKit::WebString& downloadName); + 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&, WebKit::WebTextDirection); + 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&, const WebKit::WebURL&); + virtual void didDetectXSS(WebKit::WebFrame*, const WebKit::WebURL&, bool didBlockEntirePage); + virtual void openFileSystem(WebKit::WebFrame*, WebKit::WebFileSystem::Type, long long size, bool create, WebKit::WebFileSystemCallbacks*); + virtual bool willCheckAndDispatchMessageEvent(WebKit::WebFrame* source, WebKit::WebSecurityOrigin target, WebKit::WebDOMMessageEvent); + + WebKit::WebDeviceOrientationClientMock* deviceOrientationClientMock(); + + // Spellcheck related helper APIs + MockSpellCheck* mockSpellCheck(); + void finishLastTextCheck(); + + // Geolocation client mocks for LayoutTestController + WebKit::WebGeolocationClientMock* geolocationClientMock(); + + // Pending task list, Note taht the method is referred from MethodTask class. + TaskList* taskList() { return &m_taskList; } + +private: + + class HostMethodTask : public MethodTask<WebViewHost> { + public: + typedef void (WebViewHost::*CallbackMethodType)(); + HostMethodTask(WebViewHost* object, CallbackMethodType callback) + : MethodTask<WebViewHost>(object) + , m_callback(callback) + { } + + virtual void runIfValid() { (m_object->*m_callback)(); } + + private: + CallbackMethodType m_callback; + }; + + 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&); + + void enterFullScreenNow(); + void exitFullScreenNow(); + + // 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; + + // true if whatever is sent to the console should be logged to stdout. + bool m_logConsoleOutput; + + // 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<SkCanvas> m_canvas; + WebKit::WebRect m_paintRect; + bool m_isPainting; + + OwnPtr<WebKit::WebContextMenuData> m_lastContextMenuData; + + // Geolocation + OwnPtr<WebKit::WebGeolocationClientMock> m_geolocationClientMock; + + OwnPtr<WebKit::WebDeviceOrientationClientMock> m_deviceOrientationClientMock; + OwnPtr<WebKit::WebSpeechInputControllerMock> m_speechInputControllerMock; + + OwnPtr<TestNavigationController> m_navigationController; + + WebKit::WebString m_lastRequestedTextCheckString; + WebKit::WebTextCheckingCompletion* m_lastRequestedTextCheckingCompletion; + + TaskList m_taskList; + Vector<WebKit::WebWidget*> m_popupmenus; +}; + +#endif // WebViewHost_h diff --git a/Tools/DumpRenderTree/chromium/config.h b/Tools/DumpRenderTree/chromium/config.h new file mode 100644 index 000000000..57b3a54fc --- /dev/null +++ b/Tools/DumpRenderTree/chromium/config.h @@ -0,0 +1,53 @@ +/* + * 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> +#include <wtf/ExportMacros.h> + +#if OS(WINDOWS) && !COMPILER(GCC) +// Allow 'this' to be used in base member initializer list. +#pragma warning(disable : 4355) +#endif + +#endif // config_h diff --git a/Tools/DumpRenderTree/chromium/fonts.conf b/Tools/DumpRenderTree/chromium/fonts.conf new file mode 100644 index 000000000..b75a9322e --- /dev/null +++ b/Tools/DumpRenderTree/chromium/fonts.conf @@ -0,0 +1,229 @@ +<?xml version="1.0"?> +<!DOCTYPE fontconfig SYSTEM "fonts.dtd"> +<!-- /etc/fonts/fonts.conf file to configure system font access --> +<fontconfig> + <match target="font"> + <edit name="embeddedbitmap" mode="assign"><bool>false</bool></edit> + </match> + + <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> + + <match target="pattern"> + <test name="family" compare="eq"> + <string>AutohintedSerif</string> + </test> + <edit name="family" mode="assign"> + <string>Arial</string> + </edit> + <edit name="autohint" mode="assign"> + <bool>true</bool> + </edit> + <edit name="hintstyle" mode="assign"> + <const>hintmedium</const> + </edit> + </match> + + <match target="pattern"> + <test name="family" compare="eq"> + <string>HintedSerif</string> + </test> + <edit name="family" mode="assign"> + <string>Arial</string> + </edit> + <edit name="autohint" mode="assign"> + <bool>false</bool> + </edit> + <edit name="hintstyle" mode="assign"> + <const>hintmedium</const> + </edit> + </match> + + <match target="pattern"> + <test name="family" compare="eq"> + <string>FullAndAutoHintedSerif</string> + </test> + <edit name="family" mode="assign"> + <string>Arial</string> + </edit> + <edit name="autohint" mode="assign"> + <bool>true</bool> + </edit> + <edit name="hintstyle" mode="assign"> + <const>hintfull</const> + </edit> + </match> + + <match target="pattern"> + <test name="family" compare="eq"> + <string>SubpixelEnabledArial</string> + </test> + <edit name="family" mode="assign"> + <string>Arial</string> + </edit> + <edit name="rgba" mode="assign"> + <const>rgb</const> + </edit> + </match> + + <match target="pattern"> + <test name="family" compare="eq"> + <string>SubpixelDisabledArial</string> + </test> + <edit name="family" mode="assign"> + <string>Arial</string> + </edit> + <edit name="rgba" mode="assign"> + <const>none</const> + </edit> + </match> + +</fontconfig> |