diff options
author | kkania@chromium.org <kkania@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-03-17 00:19:32 +0000 |
---|---|---|
committer | kkania@chromium.org <kkania@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-03-17 00:19:32 +0000 |
commit | c158a6699a9f5e5f56d913c0b06e0d83e8358591 (patch) | |
tree | 727e3eb0a5aac10658e35d2e389b057faed103bf /chrome/renderer | |
parent | c7eca5b2c6f9a527d18d00486d2d7cd7dca9c7b1 (diff) | |
download | chromium_src-c158a6699a9f5e5f56d913c0b06e0d83e8358591.zip chromium_src-c158a6699a9f5e5f56d913c0b06e0d83e8358591.tar.gz chromium_src-c158a6699a9f5e5f56d913c0b06e0d83e8358591.tar.bz2 |
Add support for DOM interaction in browser tests via C++.
BUG=none
TEST=none
Review URL: http://codereview.chromium.org/660046
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@41789 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/renderer')
-rw-r--r-- | chrome/renderer/automation/dom_automation_controller.cc | 31 | ||||
-rw-r--r-- | chrome/renderer/automation/dom_automation_controller.h | 5 | ||||
-rw-r--r-- | chrome/renderer/automation/dom_automation_v8_extension.cc | 19 | ||||
-rw-r--r-- | chrome/renderer/automation/dom_automation_v8_extension.h | 16 | ||||
-rw-r--r-- | chrome/renderer/render_thread.cc | 5 | ||||
-rw-r--r-- | chrome/renderer/renderer_resources.grd | 1 | ||||
-rw-r--r-- | chrome/renderer/resources/dom_automation.js | 234 |
7 files changed, 311 insertions, 0 deletions
diff --git a/chrome/renderer/automation/dom_automation_controller.cc b/chrome/renderer/automation/dom_automation_controller.cc index 3b7ac43..6c3e8a5 100644 --- a/chrome/renderer/automation/dom_automation_controller.cc +++ b/chrome/renderer/automation/dom_automation_controller.cc @@ -14,6 +14,7 @@ DomAutomationController::DomAutomationController() automation_id_(MSG_ROUTING_NONE) { BindMethod("send", &DomAutomationController::Send); BindMethod("setAutomationId", &DomAutomationController::SetAutomationId); + BindMethod("sendJSON", &DomAutomationController::SendJSON); } void DomAutomationController::Send(const CppArgumentList& args, @@ -86,6 +87,36 @@ void DomAutomationController::Send(const CppArgumentList& args, return; } +void DomAutomationController::SendJSON(const CppArgumentList& args, + CppVariant* result) { + if (args.size() != 1) { + result->SetNull(); + return; + } + + if (automation_id_ == MSG_ROUTING_NONE) { + result->SetNull(); + return; + } + + if (!sender_) { + NOTREACHED(); + result->SetNull(); + return; + } + + if (args[0].type != NPVariantType_String) { + result->SetNull(); + return; + } + + std::string json = args[0].ToString(); + result->Set(sender_->Send( + new ViewHostMsg_DomOperationResponse(routing_id_, json, automation_id_))); + + automation_id_ = MSG_ROUTING_NONE; +} + void DomAutomationController::SetAutomationId( const CppArgumentList& args, CppVariant* result) { if (args.size() != 1) { diff --git a/chrome/renderer/automation/dom_automation_controller.h b/chrome/renderer/automation/dom_automation_controller.h index bc6f5fe..c815418 100644 --- a/chrome/renderer/automation/dom_automation_controller.h +++ b/chrome/renderer/automation/dom_automation_controller.h @@ -84,6 +84,11 @@ class DomAutomationController : public CppBoundClass { // IPC. It sets the return value to null on unexpected errors or arguments. void Send(const CppArgumentList& args, CppVariant* result); + // Makes the renderer send a javascript value to the app. + // The value must be a NPString and should be properly formed JSON. + // This function does not modify/escape the returned string in any way. + void SendJSON(const CppArgumentList& args, CppVariant* result); + void SetAutomationId(const CppArgumentList& args, CppVariant* result); // TODO(vibhor): Implement later diff --git a/chrome/renderer/automation/dom_automation_v8_extension.cc b/chrome/renderer/automation/dom_automation_v8_extension.cc new file mode 100644 index 0000000..efe5ada --- /dev/null +++ b/chrome/renderer/automation/dom_automation_v8_extension.cc @@ -0,0 +1,19 @@ +// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/renderer/automation/dom_automation_v8_extension.h" + +#include "chrome/renderer/extensions/bindings_utils.h" +#include "grit/renderer_resources.h" + +using bindings_utils::GetStringResource; + +const char* DomAutomationV8Extension::kName = "chrome/domautomation"; + +v8::Extension* DomAutomationV8Extension::Get() { + static v8::Extension* extension = + new bindings_utils::ExtensionBase( + kName, GetStringResource<IDR_DOM_AUTOMATION_JS>(), 0, NULL); + return extension; +} diff --git a/chrome/renderer/automation/dom_automation_v8_extension.h b/chrome/renderer/automation/dom_automation_v8_extension.h new file mode 100644 index 0000000..94201e7 --- /dev/null +++ b/chrome/renderer/automation/dom_automation_v8_extension.h @@ -0,0 +1,16 @@ +// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROME_RENDERER_AUTOMATION_DOM_AUTOMATION_V8_EXTENSION_H_ +#define CHROME_RENDERER_AUTOMATION_DOM_AUTOMATION_V8_EXTENSION_H_ + +#include "v8/include/v8.h" + +class DomAutomationV8Extension { + public: + static const char* kName; + static v8::Extension* Get(); +}; + +#endif // CHROME_RENDERER_AUTOMATION_DOM_AUTOMATION_V8_EXTENSION_H_ diff --git a/chrome/renderer/render_thread.cc b/chrome/renderer/render_thread.cc index bc9a2a5..25ce100 100644 --- a/chrome/renderer/render_thread.cc +++ b/chrome/renderer/render_thread.cc @@ -42,6 +42,7 @@ #include "base/scoped_handle.h" #include "chrome/plugin/plugin_channel_base.h" #endif +#include "chrome/renderer/automation/dom_automation_v8_extension.h" #include "chrome/renderer/cookie_message_filter.h" #include "chrome/renderer/devtools_agent_filter.h" #include "chrome/renderer/extension_groups.h" @@ -789,6 +790,10 @@ void RenderThread::EnsureWebKitInitialized() { extensions_v8::PlaybackExtension::Get()); } + if (command_line.HasSwitch(switches::kDomAutomationController)) { + WebScriptController::registerExtension(DomAutomationV8Extension::Get()); + } + WebRuntimeFeatures::enableMediaPlayer( RenderProcess::current()->HasInitializedMediaLibrary()); diff --git a/chrome/renderer/renderer_resources.grd b/chrome/renderer/renderer_resources.grd index 15c8f90..ac2f91c 100644 --- a/chrome/renderer/renderer_resources.grd +++ b/chrome/renderer/renderer_resources.grd @@ -22,6 +22,7 @@ without changes to the corresponding grd file. fb9 --> <include name="IDR_SAD_PLUGIN" file="resources\sadplugin.png" type="BINDATA" /> <include name="IDR_EXTENSION_APITEST_JS" file="resources\extension_apitest.js" type="BINDATA" /> <include name="IDR_EXTENSION_TOOLSTRIP_CSS" file="resources\extension_toolstrip.css" flattenhtml="true" type="BINDATA" /> + <include name="IDR_DOM_AUTOMATION_JS" file="resources\dom_automation.js" type="BINDATA" /> <include name="IDR_BASE_JS" file="resources\base.js" type="BINDATA" /> </includes> </release> diff --git a/chrome/renderer/resources/dom_automation.js b/chrome/renderer/resources/dom_automation.js new file mode 100644 index 0000000..16a4dd0 --- /dev/null +++ b/chrome/renderer/resources/dom_automation.js @@ -0,0 +1,234 @@ +// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// dom_automation.js +// Methods for performing common DOM operations. Used in Chrome testing +// involving the DomAutomationController. + +var domAutomation = domAutomation || {}; + +(function() { + // |objects| is used to track objects which are sent back to the + // DomAutomationController. Since JavaScript does not have a map type, + // |objects| is simply an object in which the property name and + // property value serve as the key-value pair. The key is the handle number + // and the value is the tracked object. + domAutomation.objects = {}; + domAutomation.nextHandle = 1; + + // Returns |value| after converting it to an acceptable type for return, if + // necessary. + function getConvertedValue(value) { + if (typeof value == "undefined" || !value) { + return ""; + } + if (value instanceof Array) { + var result = []; + for (var i = 0; i < value.length; i++) { + result.push(getConvertedValue(value[i])); + } + return result; + } + if (typeof(value) == "object") { + var handle = getHandleForObject(value); + if (handle == -1) { + // Track this object. + var handle = domAutomation.nextHandle++; + domAutomation.objects[handle] = value; + } + return handle; + } + return value; + } + + // Returns the handle for |obj|, or -1 if no handle exists. + function getHandleForObject(obj) { + for (var property in domAutomation.objects) { + if (domAutomation.objects[property] == obj) + return parseInt(property); + } + return -1; + } + + // Safely evaluates |javascript| and sends a response back via the + // DomAutomationController. See javascript_execution_controller.cc + // for more details. + domAutomation.evaluateJavaScript = function(javascript) { + try { + var result = [true, "", getConvertedValue(eval(javascript))]; + } + catch (exception) { + var message = exception.message; + if (typeof message != "string") + message = JSON.stringify(message); + var result = [false, message, exception]; + } + domAutomationController.sendJSON(JSON.stringify(result)); + } + + // Stops tracking the object associated with |handle|. + domAutomation.removeObject = function(handle) { + delete domAutomation.objects[handle]; + } + + // Stops tracking all objects. + domAutomation.removeAll = function() { + domAutomation.objects = {}; + domAutomation.nextHandle = 1; + } + + // Gets the object associated with this |handle|. + domAutomation.getObject = function(handle) { + return domAutomation.objects[handle]; + } + + // Converts an indexable list with a length property to an array. + function getArray(list) { + var arr = []; + for (var i = 0; i < list.length; i++) { + arr.push(list[i]); + } + return arr; + } + + // Removes whitespace at the beginning and end of |text|. + function trim(text) { + return text.replace(/^\s+|\s+$/g, ""); + } + + // Returns the window (which is a sub window of |win|) which + // directly contains |doc|. May return null. + function findWindowForDocument(win, doc) { + if (win.document == doc) + return win; + for (var i = 0; i < win.frames.length; i++) { + if (findWindowForDocument(win.frames[i], doc)) + return win.frames[i]; + } + return null; + } + + //// DOM Element automation methods + //// See dom_element_proxy.h for method details. + + domAutomation.getDocumentFromFrame = function(element, frameNames) { + // Find the window this element is in. + var containingDocument = element.ownerDocument || element; + var frame = findWindowForDocument(window, containingDocument); + + for (var i = 0; i < frameNames.length; i++) { + frame = frame.frames[frameNames[i]]; + if (typeof frame == "undefined" || !frame) { + return null; + } + } + return frame.document; + } + + domAutomation.findByXPath = function(context, xpath) { + var xpathResult = + document.evaluate(xpath, context, null, + XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null); + var elements = []; + for (var i = 0; i < xpathResult.snapshotLength; i++) { + elements.push(xpathResult.snapshotItem(i)); + } + return elements; + } + + domAutomation.find1ByXPath = function(context, xpath) { + var xpathResult = + document.evaluate(xpath, context, null, + XPathResult.FIRST_ORDERED_NODE_TYPE, null); + return xpathResult.singleNodeValue; + } + + domAutomation.findBySelectors = function(context, selectors) { + return getArray(context.querySelectorAll(selectors)); + } + + domAutomation.find1BySelectors = function(context, selectors) { + return context.querySelector(selectors); + } + + domAutomation.findByText = function(context, text) { + // Find all elements containing this text and all inputs containing + // this text. + var xpath = ".//*[contains(text(), '" + text + "')] | " + + ".//input[contains(@value, '" + text + "')]"; + var elements = domAutomation.findByXPath(context, xpath); + + // Limit to what is visible. + var final_list = []; + for (var i = 0; i < elements.length; i++) { + if (domAutomation.isVisible(elements[i])) + final_list.push(elements[i]); + } + return final_list; + } + + domAutomation.find1ByText = function(context, text) { + return domAutomation.findByText(context, text)[0]; + } + + domAutomation.click = function(element) { + var evt = document.createEvent('MouseEvents'); + evt.initMouseEvent('click', true, true, window, + 0, 0, 0, 0, 0, false, false, + false, false, 0, null); + while (element) { + element.dispatchEvent(evt); + element = element.parentNode; + } + } + + domAutomation.type = function(element, text) { + if (element instanceof HTMLTextAreaElement || + (element instanceof HTMLInputElement && element.type == "text")) { + element.value += text; + return true; + } + return false; + } + + domAutomation.setText = function(element, text) { + if (element instanceof HTMLTextAreaElement || + (element instanceof HTMLInputElement && element.type == "text")) { + element.value = text; + return true; + } + return false; + } + + domAutomation.getText = function(element) { + if (element instanceof Text) { + return trim(element.textContent); + } + else if (element instanceof HTMLTextAreaElement || + (element instanceof HTMLInputElement)) { + return element.value || ""; + } + var childrenText = ""; + for (var i = 0; i < element.childNodes.length; i++) { + childrenText += domAutomation.getText(element.childNodes[i]); + } + return childrenText; + } + + domAutomation.getInnerHTML = function(element) { + return trim(element.innerHTML); + } + + domAutomation.isVisible = function(element) { + while (element.style) { + if (element.style.display == 'none' || + element.style.visibility == 'hidden' || + element.style.visibility == 'collapse') { + return false; + } + element = element.parentNode; + } + return true; + } +})(); |