summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--chrome/browser/automation/automation_provider.cc2
-rw-r--r--chrome/browser/automation/automation_provider_observers.cc13
-rw-r--r--chrome/browser/automation/automation_provider_observers.h3
-rw-r--r--chrome/browser/automation/testing_automation_provider.cc134
-rw-r--r--chrome/browser/automation/testing_automation_provider.h16
-rw-r--r--chrome/test/data/extensions/js_injection_background/bg.html10
-rw-r--r--chrome/test/data/extensions/js_injection_background/manifest.json10
-rw-r--r--chrome/test/functional/execute_javascript.py42
-rw-r--r--chrome/test/functional/memory.py2
-rw-r--r--chrome/test/functional/process_count.py3
-rw-r--r--chrome/test/pyautolib/pyauto.py51
11 files changed, 249 insertions, 37 deletions
diff --git a/chrome/browser/automation/automation_provider.cc b/chrome/browser/automation/automation_provider.cc
index 80530abd..707c75f 100644
--- a/chrome/browser/automation/automation_provider.cc
+++ b/chrome/browser/automation/automation_provider.cc
@@ -781,6 +781,7 @@ void AutomationProvider::InstallExtension(
// The observer will delete itself when done.
new ExtensionReadyNotificationObserver(
manager,
+ service,
this,
AutomationMsg_InstallExtension::ID,
reply_message);
@@ -826,6 +827,7 @@ void AutomationProvider::EnableExtension(int extension_handle,
// The observer will delete itself when done.
new ExtensionReadyNotificationObserver(
manager,
+ service,
this,
AutomationMsg_EnableExtension::ID,
reply_message);
diff --git a/chrome/browser/automation/automation_provider_observers.cc b/chrome/browser/automation/automation_provider_observers.cc
index 3f2a306..c62dd8a 100644
--- a/chrome/browser/automation/automation_provider_observers.cc
+++ b/chrome/browser/automation/automation_provider_observers.cc
@@ -28,6 +28,7 @@
#include "chrome/browser/extensions/crx_installer.h"
#include "chrome/browser/extensions/extension_host.h"
#include "chrome/browser/extensions/extension_process_manager.h"
+#include "chrome/browser/extensions/extension_service.h"
#include "chrome/browser/extensions/extension_tabs_module.h"
#include "chrome/browser/extensions/extension_updater.h"
#include "chrome/browser/history/history_types.h"
@@ -67,6 +68,7 @@
#include "content/browser/renderer_host/render_view_host.h"
#include "content/browser/tab_contents/navigation_controller.h"
#include "content/browser/tab_contents/tab_contents.h"
+#include "content/common/json_value_serializer.h"
#include "content/common/notification_service.h"
#include "googleurl/src/gurl.h"
#include "ui/gfx/codec/png_codec.h"
@@ -523,9 +525,10 @@ void ExtensionUninstallObserver::Observe(
}
ExtensionReadyNotificationObserver::ExtensionReadyNotificationObserver(
- ExtensionProcessManager* manager, AutomationProvider* automation, int id,
- IPC::Message* reply_message)
+ ExtensionProcessManager* manager, ExtensionService* service,
+ AutomationProvider* automation, int id, IPC::Message* reply_message)
: manager_(manager),
+ service_(service),
automation_(automation->AsWeakPtr()),
id_(id),
reply_message_(reply_message),
@@ -562,6 +565,12 @@ void ExtensionReadyNotificationObserver::Observe(
extension_ = Details<const Extension>(details).ptr();
if (!DidExtensionHostsStopLoading(manager_))
return;
+ // For some reason, the background ExtensionHost is not yet
+ // created at this point so just checking whether all ExtensionHosts
+ // are loaded is not sufficient. If background page is not ready,
+ // we wait for NOTIFICATION_EXTENSION_HOST_DID_STOP_LOADING.
+ if(!service_->IsBackgroundPageReady(extension_))
+ return;
break;
case chrome::NOTIFICATION_EXTENSION_INSTALL_ERROR:
case chrome::NOTIFICATION_EXTENSION_UPDATE_DISABLED:
diff --git a/chrome/browser/automation/automation_provider_observers.h b/chrome/browser/automation/automation_provider_observers.h
index d6054b8..f7358d7 100644
--- a/chrome/browser/automation/automation_provider_observers.h
+++ b/chrome/browser/automation/automation_provider_observers.h
@@ -57,6 +57,7 @@ class BalloonCollection;
class Browser;
class Extension;
class ExtensionProcessManager;
+class ExtensionService;
class NavigationController;
class Profile;
class RenderViewHost;
@@ -313,6 +314,7 @@ class ExtensionUninstallObserver : public NotificationObserver {
class ExtensionReadyNotificationObserver : public NotificationObserver {
public:
ExtensionReadyNotificationObserver(ExtensionProcessManager* manager,
+ ExtensionService* service,
AutomationProvider* automation,
int id,
IPC::Message* reply_message);
@@ -326,6 +328,7 @@ class ExtensionReadyNotificationObserver : public NotificationObserver {
private:
NotificationRegistrar registrar_;
ExtensionProcessManager* manager_;
+ ExtensionService* service_;
base::WeakPtr<AutomationProvider> automation_;
int id_;
scoped_ptr<IPC::Message> reply_message_;
diff --git a/chrome/browser/automation/testing_automation_provider.cc b/chrome/browser/automation/testing_automation_provider.cc
index 48cc884..233cc0e 100644
--- a/chrome/browser/automation/testing_automation_provider.cc
+++ b/chrome/browser/automation/testing_automation_provider.cc
@@ -45,7 +45,9 @@
#include "chrome/browser/debugger/devtools_window.h"
#include "chrome/browser/download/download_prefs.h"
#include "chrome/browser/download/save_package_file_picker.h"
+#include "chrome/browser/extensions/extension_browser_event_router.h"
#include "chrome/browser/extensions/extension_host.h"
+#include "chrome/browser/extensions/extension_process_manager.h"
#include "chrome/browser/extensions/extension_service.h"
#include "chrome/browser/extensions/extension_updater.h"
#include "chrome/browser/history/top_sites.h"
@@ -104,6 +106,7 @@
#include "content/browser/tab_contents/interstitial_page.h"
#include "content/common/common_param_traits.h"
#include "content/common/notification_service.h"
+#include "content/common/view_types.h"
#include "net/base/cookie_store.h"
#include "third_party/WebKit/Source/WebKit/chromium/public/WebInputEvent.h"
#include "ui/base/events.h"
@@ -1291,6 +1294,30 @@ void TestingAutomationProvider::GetFullscreenBubbleVisibility(int handle,
}
}
+namespace {
+
+void ExecuteJavascriptInRenderViewFrame(
+ const string16& frame_xpath,
+ const string16& script,
+ IPC::Message* reply_message,
+ RenderViewHost* render_view_host) {
+ // Set the routing id of this message with the controller.
+ // This routing id needs to be remembered for the reverse
+ // communication while sending back the response of
+ // this javascript execution.
+ std::string set_automation_id;
+ base::SStringPrintf(&set_automation_id,
+ "window.domAutomationController.setAutomationId(%d);",
+ reply_message->routing_id());
+
+ render_view_host->ExecuteJavascriptInWebFrame(
+ frame_xpath, UTF8ToUTF16(set_automation_id));
+ render_view_host->ExecuteJavascriptInWebFrame(
+ frame_xpath, script);
+}
+
+} // namespace
+
void TestingAutomationProvider::ExecuteJavascript(
int handle,
const std::wstring& frame_xpath,
@@ -1303,20 +1330,10 @@ void TestingAutomationProvider::ExecuteJavascript(
return;
}
- // Set the routing id of this message with the controller.
- // This routing id needs to be remembered for the reverse
- // communication while sending back the response of
- // this javascript execution.
- std::string set_automation_id;
- base::SStringPrintf(&set_automation_id,
- "window.domAutomationController.setAutomationId(%d);",
- reply_message->routing_id());
-
new DomOperationMessageSender(this, reply_message, false);
- tab_contents->render_view_host()->ExecuteJavascriptInWebFrame(
- WideToUTF16Hack(frame_xpath), UTF8ToUTF16(set_automation_id));
- tab_contents->render_view_host()->ExecuteJavascriptInWebFrame(
- WideToUTF16Hack(frame_xpath), WideToUTF16Hack(script));
+ ExecuteJavascriptInRenderViewFrame(WideToUTF16Hack(frame_xpath),
+ WideToUTF16Hack(script), reply_message,
+ tab_contents->render_view_host());
}
void TestingAutomationProvider::GetConstrainedWindowCount(int handle,
@@ -2164,6 +2181,8 @@ void TestingAutomationProvider::SendJSONRequest(int handle,
&TestingAutomationProvider::NavigateToURL;
handler_map["ExecuteJavascript"] =
&TestingAutomationProvider::ExecuteJavascriptJSON;
+ handler_map["ExecuteJavascriptInRenderView"] =
+ &TestingAutomationProvider::ExecuteJavascriptInRenderView;
handler_map["GoForward"] =
&TestingAutomationProvider::GoForward;
handler_map["GoBack"] =
@@ -2771,7 +2790,7 @@ void TestingAutomationProvider::GetBrowserInfo(
// Add all extension processes in a list of dictionaries, one dictionary
// item per extension process.
- ListValue* extension_processes = new ListValue;
+ ListValue* extension_views = new ListValue;
ProfileManager* profile_manager = g_browser_process->profile_manager();
std::vector<Profile*> profiles(profile_manager->GetLoadedProfiles());
for (size_t i = 0; i < profiles.size(); ++i) {
@@ -2787,13 +2806,43 @@ void TestingAutomationProvider::GetBrowserInfo(
continue;
DictionaryValue* item = new DictionaryValue;
item->SetString("name", ex_host->extension()->name());
+ item->SetString("extension_id", ex_host->extension()->id());
item->SetInteger(
"pid",
base::GetProcId(ex_host->render_process_host()->GetHandle()));
- extension_processes->Append(item);
+ DictionaryValue* view = new DictionaryValue;
+ view->SetInteger(
+ "render_process_id",
+ ex_host->render_process_host()->id());
+ view->SetInteger(
+ "render_view_id",
+ ex_host->render_view_host()->routing_id());
+ item->Set("view", view);
+ std::string type;
+ switch (ex_host->extension_host_type()) {
+ case ViewType::EXTENSION_BACKGROUND_PAGE:
+ type = "EXTENSION_BACKGROUND_PAGE";
+ break;
+ case ViewType::EXTENSION_POPUP:
+ type = "EXTENSION_POPUP";
+ break;
+ case ViewType::EXTENSION_INFOBAR:
+ type = "EXTENSION_INFOBAR";
+ break;
+ case ViewType::EXTENSION_DIALOG:
+ type = "EXTENSION_DIALOG";
+ break;
+ default:
+ type = "unknown";
+ break;
+ }
+ item->SetString("view_type", type);
+ item->SetString("url", ex_host->GetURL().spec());
+ item->SetBoolean("loaded", ex_host->did_stop_loading());
+ extension_views->Append(item);
}
}
- return_value->Set("extension_processes", extension_processes);
+ return_value->Set("extension_views", extension_views);
AutomationJSONReply(this, reply_message).SendSuccess(return_value.get());
}
@@ -5735,20 +5784,49 @@ void TestingAutomationProvider::ExecuteJavascriptJSON(
return;
}
- // Set the routing id of this message with the controller.
- // This routing id needs to be remembered for the reverse
- // communication while sending back the response of
- // this javascript execution.
- std::string set_automation_id;
- base::SStringPrintf(&set_automation_id,
- "window.domAutomationController.setAutomationId(%d);",
- reply_message->routing_id());
+ new DomOperationMessageSender(this, reply_message, true);
+ ExecuteJavascriptInRenderViewFrame(frame_xpath, javascript, reply_message,
+ tab_contents->render_view_host());
+}
+
+void TestingAutomationProvider::ExecuteJavascriptInRenderView(
+ DictionaryValue* args,
+ IPC::Message* reply_message) {
+ string16 frame_xpath, javascript, extension_id, url_text;
+ std::string error;
+ int render_process_id, render_view_id;
+ if (!args->GetString("frame_xpath", &frame_xpath)) {
+ AutomationJSONReply(this, reply_message)
+ .SendError("'frame_xpath' missing or invalid");
+ return;
+ }
+ if (!args->GetString("javascript", &javascript)) {
+ AutomationJSONReply(this, reply_message)
+ .SendError("'javascript' missing or invalid");
+ return;
+ }
+ if (!args->GetInteger("view.render_process_id", &render_process_id)) {
+ AutomationJSONReply(this, reply_message)
+ .SendError("'view.render_process_id' missing or invalid");
+ return;
+ }
+ if (!args->GetInteger("view.render_view_id", &render_view_id)) {
+ AutomationJSONReply(this, reply_message)
+ .SendError("'view.render_view_id' missing or invalid");
+ return;
+ }
+
+ RenderViewHost* rvh = RenderViewHost::FromID(render_process_id,
+ render_view_id);
+ if(!rvh) {
+ AutomationJSONReply(this, reply_message).SendError(
+ "A RenderViewHost object was not found with the given view ID.");
+ return;
+ }
new DomOperationMessageSender(this, reply_message, true);
- tab_contents->render_view_host()->ExecuteJavascriptInWebFrame(
- frame_xpath, UTF8ToUTF16(set_automation_id));
- tab_contents->render_view_host()->ExecuteJavascriptInWebFrame(
- frame_xpath, javascript);
+ ExecuteJavascriptInRenderViewFrame(frame_xpath, javascript, reply_message,
+ rvh);
}
void TestingAutomationProvider::GoForward(
diff --git a/chrome/browser/automation/testing_automation_provider.h b/chrome/browser/automation/testing_automation_provider.h
index fbd486c..653e1a4 100644
--- a/chrome/browser/automation/testing_automation_provider.h
+++ b/chrome/browser/automation/testing_automation_provider.h
@@ -884,6 +884,22 @@ class TestingAutomationProvider : public AutomationProvider,
void ExecuteJavascriptJSON(
base::DictionaryValue* args, IPC::Message* reply_message);
+ // Executes javascript in the specified frame of a render view.
+ // Uses the JSON interface. Waits for a result from the
+ // |DOMAutomationController|. The javascript must send a string.
+ // Example:
+ // input: { "view": {
+ // "render_process_id": 1,
+ // "render_view_id": 2,
+ // }
+ // "frame_xpath": "//frames[1]",
+ // "javascript":
+ // "window.domAutomationController.send(window.name)",
+ // }
+ // output: { "result": "My Window Name" }
+ void ExecuteJavascriptInRenderView(
+ base::DictionaryValue* args, IPC::Message* reply_message);
+
// Goes forward in the specified tab. Uses the JSON interface.
// Example:
// input: { "windex": 1, "tab_index": 1 }
diff --git a/chrome/test/data/extensions/js_injection_background/bg.html b/chrome/test/data/extensions/js_injection_background/bg.html
new file mode 100644
index 0000000..b629c2a
--- /dev/null
+++ b/chrome/test/data/extensions/js_injection_background/bg.html
@@ -0,0 +1,10 @@
+<html>
+ <script>
+ var bool_var = true;
+ var int_var = 42;
+ var str_var = "foo";
+ </script>
+ <body>
+ <input id="myinput"></input>
+ </body>
+</html>
diff --git a/chrome/test/data/extensions/js_injection_background/manifest.json b/chrome/test/data/extensions/js_injection_background/manifest.json
new file mode 100644
index 0000000..a6d3076
--- /dev/null
+++ b/chrome/test/data/extensions/js_injection_background/manifest.json
@@ -0,0 +1,10 @@
+{
+ "name": "js_injection_background",
+ "description": "Tests JS injection into an extension's background page.
+ The name of a DOM node in the background page is returned and verified.",
+ "version": "0.1",
+ "background_page": "bg.html",
+ "browser_action": {
+ "default_title": "Browser Action"
+ }
+}
diff --git a/chrome/test/functional/execute_javascript.py b/chrome/test/functional/execute_javascript.py
index 1173d4a..af7a9d0 100644
--- a/chrome/test/functional/execute_javascript.py
+++ b/chrome/test/functional/execute_javascript.py
@@ -12,6 +12,11 @@ from pyauto import PyUITest
class ExecuteJavascriptTest(PyUITest):
+ def _GetExtensionInfoById(self, extensions, id):
+ for x in extensions:
+ if x['id'] == id:
+ return x
+ return None
def testExecuteJavascript(self):
self.NavigateToURL(self.GetFileURLForDataPath(
@@ -28,6 +33,43 @@ class ExecuteJavascriptTest(PyUITest):
v = self.GetDOMValue('document.getElementById("myinput").nodeName')
self.assertEqual(v, 'INPUT')
+ def testExecuteJavascriptInExtension(self):
+ """Test we can inject JavaScript into an extension."""
+ dir_path = os.path.abspath(
+ os.path.join(self.DataDir(), 'extensions', 'js_injection_background'))
+ ext_id = self.InstallExtension(dir_path, False);
+ self.assertTrue(ext_id, msg='Failed to install extension: %s.' % dir_path)
+
+ # Verify extension is enabled.
+ extension = self._GetExtensionInfoById(self.GetExtensionsInfo(), ext_id)
+ self.assertTrue(extension['is_enabled'],
+ msg='Extension was disabled by default')
+
+ # Get the background page's view.
+ info = self.GetBrowserInfo()['extension_views']
+ view = [x for x in info if
+ x['extension_id'] == ext_id and
+ x['view_type'] == 'EXTENSION_BACKGROUND_PAGE']
+ self.assertEqual(1, len(view),
+ msg='problematic background view: view = %s.' % view)
+ background_view = view[0]
+
+ # Get values from background page's DOM
+ v = self.ExecuteJavascriptInRenderView(
+ 'window.domAutomationController.send('
+ 'document.getElementById("myinput").nodeName)', background_view['view'])
+ self.assertEqual(v, 'INPUT',
+ msg='Incorrect value returned (v = %s).' % v)
+ v = self.ExecuteJavascriptInRenderView(
+ 'window.domAutomationController.send(bool_var)', background_view['view'])
+ self.assertEqual(v, True, msg='Incorrect value returned (v = %s).' % v)
+ v = self.ExecuteJavascriptInRenderView(
+ 'window.domAutomationController.send(int_var)', background_view['view'])
+ self.assertEqual(v, 42, msg='Incorrect value returned (v = %s).' % v)
+ v = self.ExecuteJavascriptInRenderView(
+ 'window.domAutomationController.send(str_var)', background_view['view'])
+ self.assertEqual(v, 'foo', msg='Incorrect value returned (v = %s).' % v)
+
if __name__ == '__main__':
pyauto_functional.Main()
diff --git a/chrome/test/functional/memory.py b/chrome/test/functional/memory.py
index a06eb23..bf7e044 100644
--- a/chrome/test/functional/memory.py
+++ b/chrome/test/functional/memory.py
@@ -56,7 +56,7 @@ class MemoryTest(pyauto.PyUITest):
The integer process identifier (PID) for the specified process, or
None if the PID cannot be identified.
"""
- info = self.GetBrowserInfo()['extension_processes']
+ info = self.GetBrowserInfo()['extension_views']
pid = [x['pid'] for x in info if x['name'] == '%s' % name]
if pid:
return pid[0]
diff --git a/chrome/test/functional/process_count.py b/chrome/test/functional/process_count.py
index f870e10..f45464e 100644
--- a/chrome/test/functional/process_count.py
+++ b/chrome/test/functional/process_count.py
@@ -27,8 +27,7 @@ class ProcessCountTest(pyauto.PyUITest):
actual_count = (
1 + # Browser process.
sum([len(tab_info['tabs']) for tab_info in info['windows']]) +
- len(info['child_processes']) +
- len(info['extension_processes']))
+ len(info['child_processes']) + len(info['extension_views']))
self.assertEqual(actual_count, expected_count,
msg='Number of processes should be %d, but was %d.' %
diff --git a/chrome/test/pyautolib/pyauto.py b/chrome/test/pyautolib/pyauto.py
index 8a2e3d4..fe2143a 100644
--- a/chrome/test/pyautolib/pyauto.py
+++ b/chrome/test/pyautolib/pyauto.py
@@ -1253,10 +1253,16 @@ class PyUITest(pyautolib.PyUITestBase, unittest.TestCase):
u'child_processes': [ { u'name': u'Shockwave Flash',
u'pid': 93766,
u'type': u'Plug-in'}],
- # There's one extension process per extension.
- u'extension_processes': [
- { u'name': u'Webpage Screenshot', u'pid': 93938},
- { u'name': u'Google Voice (by Google)', u'pid': 93852}],
+ u'extension_views': [ {
+ u'name': u'Webpage Screenshot',
+ u'pid': 93938,
+ u'extension_id': u'dgcoklnmbeljaehamekjpeidmbicddfj',
+ u'url': u'chrome-extension://dgcoklnmbeljaehamekjpeidmbicddfj/'
+ 'bg.html',
+ u'view': {
+ u'render_process_id': 2,
+ u'render_view_id': 1},
+ u'view_type': u'EXTENSION_BACKGROUND_PAGE'}]
u'properties': {
u'BrowserProcessExecutableName': u'Chromium',
u'BrowserProcessExecutablePath': u'Chromium.app/Contents/MacOS/'
@@ -2234,6 +2240,43 @@ class PyUITest(pyautolib.PyUITestBase, unittest.TestCase):
}
return self._GetResultFromJSONRequest(cmd_dict, windex=windex)
+ def ExecuteJavascriptInRenderView(self, js, view, frame_xpath=''):
+ """Executes a script in the specified frame of an render view.
+
+ The invoked javascript function must send a result back via the
+ domAutomationController.send function, or this function will never return.
+
+ Args:
+ js: script to be executed
+ view: A dictionary representing a unique id for the render view as
+ returned for example by
+ self.GetBrowserInfo()['extension_views'][]['view'].
+ Example:
+ { 'render_process_id': 1,
+ 'render_view_id' : 2}
+
+ frame_xpath: XPath of the frame to execute the script. Default is no
+ frame. Example:
+ '//frames[1]'
+
+ Returns:
+ a value that was sent back via the domAutomationController.send method
+
+ Raises:
+ pyauto_errors.JSONInterfaceError if the automation call returns an error.
+ """
+ cmd_dict = {
+ 'command': 'ExecuteJavascriptInRenderView',
+ 'javascript' : js,
+ 'view' : view,
+ 'frame_xpath' : frame_xpath,
+ }
+ result = self._GetResultFromJSONRequest(cmd_dict)['result']
+ # Wrap result in an array before deserializing because valid JSON has an
+ # array or an object as the root.
+ json_string = '[' + result + ']'
+ return json.loads(json_string)[0]
+
def CallJavascriptFunc(self, function, args=[], tab_index=0, windex=0):
"""Executes a script which calls a given javascript function.