summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authoreaugusti@chromium.org <eaugusti@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-04-13 19:00:24 +0000
committereaugusti@chromium.org <eaugusti@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-04-13 19:00:24 +0000
commite7d6ec8594d224d2967de6edc94ddbd58419f218 (patch)
treee3b294198b1b56f6d0133888a8aa5712a14eb094
parent009266504f0b83aa61cbf91c4ed29c429f7674c2 (diff)
downloadchromium_src-e7d6ec8594d224d2967de6edc94ddbd58419f218.zip
chromium_src-e7d6ec8594d224d2967de6edc94ddbd58419f218.tar.gz
chromium_src-e7d6ec8594d224d2967de6edc94ddbd58419f218.tar.bz2
Adding run_at to chrome.tabs.executeScript/insertCss.
As per Aaron's suggestion I extended the functionality of UserScriptIdleScheduler to run different scripts at different times depending on when the script requested to be run. chrome.tabs.executeScript/insertCss now accept a 'runAt' parameter that follows the semantics specified here: http://code.google.com/chrome/extensions/content_scripts.html The script will be injected as earliest as the 'runAt' parameter. So I guess it is now more of a UserScriptScheduler. BUG=107286 Review URL: http://codereview.chromium.org/9456037 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@132230 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r--chrome/browser/chromeos/accessibility/accessibility_util.cc7
-rw-r--r--chrome/browser/extensions/execute_code_in_tab_function.cc20
-rw-r--r--chrome/browser/extensions/execute_code_in_tab_function.h6
-rw-r--r--chrome/browser/extensions/execute_script_apitest.cc6
-rw-r--r--chrome/browser/extensions/extension_tabs_module.cc2
-rw-r--r--chrome/browser/extensions/extension_tabs_module_constants.cc1
-rw-r--r--chrome/browser/extensions/extension_tabs_module_constants.h1
-rw-r--r--chrome/chrome_renderer.gypi4
-rw-r--r--chrome/common/extensions/api/tabs.json16
-rw-r--r--chrome/common/extensions/docs/tabs.html56
-rw-r--r--chrome/common/extensions/extension_messages.h3
-rw-r--r--chrome/common/extensions/user_script.h2
-rw-r--r--chrome/renderer/extensions/extension_helper.cc11
-rw-r--r--chrome/renderer/extensions/user_script_scheduler.cc (renamed from chrome/renderer/extensions/user_script_idle_scheduler.cc)95
-rw-r--r--chrome/renderer/extensions/user_script_scheduler.h (renamed from chrome/renderer/extensions/user_script_idle_scheduler.h)47
-rw-r--r--chrome/test/data/extensions/api_test/executescript/run_at/manifest.json10
-rw-r--r--chrome/test/data/extensions/api_test/executescript/run_at/test.html3
-rw-r--r--chrome/test/data/extensions/api_test/executescript/run_at/test.js29
18 files changed, 257 insertions, 62 deletions
diff --git a/chrome/browser/chromeos/accessibility/accessibility_util.cc b/chrome/browser/chromeos/accessibility/accessibility_util.cc
index 3207321..7ff99f2 100644
--- a/chrome/browser/chromeos/accessibility/accessibility_util.cc
+++ b/chrome/browser/chromeos/accessibility/accessibility_util.cc
@@ -17,6 +17,7 @@
#include "chrome/browser/speech/extension_api/tts_extension_api_platform.h"
#include "chrome/common/extensions/extension_messages.h"
#include "chrome/common/extensions/extension_resource.h"
+#include "chrome/common/extensions/user_script.h"
#include "chrome/common/pref_names.h"
#include "content/public/browser/render_view_host.h"
#include "content/public/browser/web_contents.h"
@@ -71,6 +72,7 @@ class ContentScriptLoader {
params.extension_id = extension_id_;
params.is_javascript = true;
params.code = data;
+ params.run_at = UserScript::DOCUMENT_IDLE;
params.all_frames = true;
params.in_main_world = false;
render_view_host_->Send(new ExtensionMsg_ExecuteCode(
@@ -110,7 +112,7 @@ void EnableSpokenFeedback(bool enabled, content::WebUI* login_web_ui) {
profile->GetExtensionService();
FilePath path = FilePath(extension_misc::kAccessExtensionPath)
.AppendASCII(extension_misc::kChromeVoxDirectoryName);
- if (enabled) { // Load ChromeVox
+ if (enabled) { // Load ChromeVox
const Extension* extension =
extension_service->component_loader()->Add(IDR_CHROMEVOX_MANIFEST,
path);
@@ -125,6 +127,7 @@ void EnableSpokenFeedback(bool enabled, content::WebUI* login_web_ui) {
params.extension_id = extension->id();
params.is_javascript = true;
params.code = "window.INJECTED_AFTER_LOAD = true;";
+ params.run_at = UserScript::DOCUMENT_IDLE;
params.all_frames = true;
params.in_main_world = false;
render_view_host->Send(new ExtensionMsg_ExecuteCode(
@@ -147,7 +150,7 @@ void EnableSpokenFeedback(bool enabled, content::WebUI* login_web_ui) {
}
DLOG(INFO) << "ChromeVox was Loaded.";
- } else { // Unload ChromeVox
+ } else { // Unload ChromeVox
extension_service->component_loader()->Remove(path);
DLOG(INFO) << "ChromeVox was Unloaded.";
}
diff --git a/chrome/browser/extensions/execute_code_in_tab_function.cc b/chrome/browser/extensions/execute_code_in_tab_function.cc
index d17de24..ee3f5be 100644
--- a/chrome/browser/extensions/execute_code_in_tab_function.cc
+++ b/chrome/browser/extensions/execute_code_in_tab_function.cc
@@ -20,6 +20,7 @@
#include "chrome/common/extensions/extension_error_utils.h"
#include "chrome/common/extensions/extension_file_util.h"
#include "chrome/common/extensions/extension_l10n_util.h"
+#include "chrome/common/extensions/extension_manifest_constants.h"
#include "chrome/common/extensions/extension_message_bundle.h"
#include "chrome/common/extensions/extension_messages.h"
#include "content/public/browser/render_view_host.h"
@@ -31,7 +32,8 @@ namespace keys = extension_tabs_module_constants;
ExecuteCodeInTabFunction::ExecuteCodeInTabFunction()
: execute_tab_id_(-1),
- all_frames_(false) {
+ all_frames_(false),
+ run_at_(UserScript::DOCUMENT_IDLE) {
}
ExecuteCodeInTabFunction::~ExecuteCodeInTabFunction() {
@@ -95,6 +97,21 @@ bool ExecuteCodeInTabFunction::RunImpl() {
return false;
}
+ if (script_info->HasKey(keys::kRunAtKey)) {
+ std::string run_string;
+ EXTENSION_FUNCTION_VALIDATE(script_info->GetString(
+ keys::kRunAtKey, &run_string));
+
+ if (run_string == extension_manifest_values::kRunAtDocumentStart)
+ run_at_ = UserScript::DOCUMENT_START;
+ else if (run_string == extension_manifest_values::kRunAtDocumentEnd)
+ run_at_ = UserScript::DOCUMENT_END;
+ else if (run_string == extension_manifest_values::kRunAtDocumentIdle)
+ run_at_ = UserScript::DOCUMENT_IDLE;
+ else
+ EXTENSION_FUNCTION_VALIDATE(false);
+ }
+
std::string code_string;
if (script_info->HasKey(keys::kCodeKey)) {
if (!script_info->GetString(keys::kCodeKey, &code_string))
@@ -222,6 +239,7 @@ bool ExecuteCodeInTabFunction::Execute(const std::string& code_string) {
params.is_javascript = is_js_code;
params.code = code_string;
params.all_frames = all_frames_;
+ params.run_at = run_at_;
params.in_main_world = false;
contents->web_contents()->GetRenderViewHost()->Send(
new ExtensionMsg_ExecuteCode(
diff --git a/chrome/browser/extensions/execute_code_in_tab_function.h b/chrome/browser/extensions/execute_code_in_tab_function.h
index fb36fa9..ccdee29 100644
--- a/chrome/browser/extensions/execute_code_in_tab_function.h
+++ b/chrome/browser/extensions/execute_code_in_tab_function.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Copyright (c) 2012 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.
@@ -10,6 +10,7 @@
#include "chrome/browser/extensions/extension_function.h"
#include "chrome/common/extensions/extension_resource.h"
+#include "chrome/common/extensions/user_script.h"
#include "content/public/browser/web_contents_observer.h"
// Implement API call tabs.executeScript and tabs.insertCSS.
@@ -58,6 +59,9 @@ class ExecuteCodeInTabFunction : public AsyncExtensionFunction,
// If all_frames_ is true, script or CSS text would be injected
// to all frames; Otherwise only injected to top main frame.
bool all_frames_;
+
+ // The intended time to run the script.
+ UserScript::RunLocation run_at_;
};
class TabsExecuteScriptFunction : public ExecuteCodeInTabFunction {
diff --git a/chrome/browser/extensions/execute_script_apitest.cc b/chrome/browser/extensions/execute_script_apitest.cc
index 0a4cfaa..0594c79 100644
--- a/chrome/browser/extensions/execute_script_apitest.cc
+++ b/chrome/browser/extensions/execute_script_apitest.cc
@@ -72,3 +72,9 @@ IN_PROC_BROWSER_TEST_F(ExecuteScriptApiTest, ExecuteScriptFrameAfterLoad) {
ASSERT_TRUE(StartTestServer());
ASSERT_TRUE(RunExtensionTest("executescript/frame_after_load")) << message_;
}
+
+IN_PROC_BROWSER_TEST_F(ExecuteScriptApiTest, ExecuteScriptRunAt) {
+ SetupDelayedHostResolver();
+ ASSERT_TRUE(StartTestServer());
+ ASSERT_TRUE(RunExtensionTest("executescript/run_at")) << message_;
+}
diff --git a/chrome/browser/extensions/extension_tabs_module.cc b/chrome/browser/extensions/extension_tabs_module.cc
index ef450fd..e7bbd73 100644
--- a/chrome/browser/extensions/extension_tabs_module.cc
+++ b/chrome/browser/extensions/extension_tabs_module.cc
@@ -48,6 +48,7 @@
#include "chrome/common/extensions/extension_manifest_constants.h"
#include "chrome/common/extensions/extension_error_utils.h"
#include "chrome/common/extensions/extension_messages.h"
+#include "chrome/common/extensions/user_script.h"
#include "chrome/common/pref_names.h"
#include "chrome/common/url_constants.h"
#include "content/public/browser/navigation_controller.h"
@@ -1313,6 +1314,7 @@ bool UpdateTabFunction::UpdateURLIfPresent(DictionaryValue* update_props,
params.extension_id = extension_id();
params.is_javascript = true;
params.code = url.path();
+ params.run_at = UserScript::DOCUMENT_IDLE;
params.all_frames = false;
params.in_main_world = true;
diff --git a/chrome/browser/extensions/extension_tabs_module_constants.cc b/chrome/browser/extensions/extension_tabs_module_constants.cc
index 02cd07b..7ddd6e3 100644
--- a/chrome/browser/extensions/extension_tabs_module_constants.cc
+++ b/chrome/browser/extensions/extension_tabs_module_constants.cc
@@ -32,6 +32,7 @@ const char kOpenerTabIdKey[] = "openerTabId";
const char kPinnedKey[] = "pinned";
const char kQualityKey[] = "quality";
const char kHighlightedKey[] = "highlighted";
+const char kRunAtKey[] = "runAt";
const char kSelectedKey[] = "selected";
const char kShowStateKey[] = "state";
const char kStatusKey[] = "status";
diff --git a/chrome/browser/extensions/extension_tabs_module_constants.h b/chrome/browser/extensions/extension_tabs_module_constants.h
index 76d5f7f..daa35f4 100644
--- a/chrome/browser/extensions/extension_tabs_module_constants.h
+++ b/chrome/browser/extensions/extension_tabs_module_constants.h
@@ -36,6 +36,7 @@ extern const char kOpenerTabIdKey[];
extern const char kPinnedKey[];
extern const char kQualityKey[];
extern const char kHighlightedKey[];
+extern const char kRunAtKey[];
extern const char kSelectedKey[];
extern const char kShowStateKey[];
extern const char kStatusKey[];
diff --git a/chrome/chrome_renderer.gypi b/chrome/chrome_renderer.gypi
index 07ff39f..f9ce51d 100644
--- a/chrome/chrome_renderer.gypi
+++ b/chrome/chrome_renderer.gypi
@@ -109,8 +109,8 @@
'renderer/extensions/tabs_custom_bindings.h',
'renderer/extensions/tts_custom_bindings.cc',
'renderer/extensions/tts_custom_bindings.h',
- 'renderer/extensions/user_script_idle_scheduler.cc',
- 'renderer/extensions/user_script_idle_scheduler.h',
+ 'renderer/extensions/user_script_scheduler.cc',
+ 'renderer/extensions/user_script_scheduler.h',
'renderer/extensions/user_script_slave.cc',
'renderer/extensions/user_script_slave.h',
'renderer/extensions/v8_schema_registry.cc',
diff --git a/chrome/common/extensions/api/tabs.json b/chrome/common/extensions/api/tabs.json
index ef78c2e..848bae4 100644
--- a/chrome/common/extensions/api/tabs.json
+++ b/chrome/common/extensions/api/tabs.json
@@ -605,7 +605,13 @@
"properties": {
"code": {"type": "string", "optional": true, "description": "JavaScript code to execute."},
"file": {"type": "string", "optional": true, "description": "JavaScript file to execute."},
- "allFrames": {"type": "boolean", "optional": true, "description": "If allFrames is true, this function injects script into all frames of current page. By default, it's false and script is injected only into the top main frame."}
+ "allFrames": {"type": "boolean", "optional": true, "description": "If allFrames is true, this function injects script into all frames of current page. By default, it's false and script is injected only into the top main frame."},
+ "runAt": {
+ "type": "string",
+ "optional": true,
+ "enum": ["document_start", "document_end", "document_idle"],
+ "description": "The soonest that the script will be injected into the tab. Defaults to \"document_idle\"."
+ }
}
},
{
@@ -630,7 +636,13 @@
"properties": {
"code": {"type": "string", "optional": true, "description": "CSS code to be injected."},
"file": {"type": "string", "optional": true, "description": "CSS file to be injected."},
- "allFrames": {"type": "boolean", "optional": true, "description": "If allFrames is true, this function injects CSS text into all frames of current page. By default, it's false and CSS is injected only into the top main frame."}
+ "allFrames": {"type": "boolean", "optional": true, "description": "If allFrames is true, this function injects CSS text into all frames of current page. By default, it's false and CSS is injected only into the top main frame."},
+ "runAt": {
+ "type": "string",
+ "optional": true,
+ "enum": ["document_start", "document_end", "document_idle"],
+ "description": "The soonest that the CSS will be injected into the tab. Defaults to \"document_idle\"."
+ }
}
},
{
diff --git a/chrome/common/extensions/docs/tabs.html b/chrome/common/extensions/docs/tabs.html
index 7bb168c..058e15b8 100644
--- a/chrome/common/extensions/docs/tabs.html
+++ b/chrome/common/extensions/docs/tabs.html
@@ -1138,6 +1138,34 @@ For other examples and for help in viewing the source code, see
<!-- OBJECT EVENT FIELDS -->
<!-- FUNCTION PARAMETERS -->
</div>
+ </div><div>
+ <div>
+ <dt>
+ <var>run_at</var>
+ <em>
+ <!-- TYPE -->
+ <div style="display:inline">
+ (
+ <span class="optional">optional</span>
+ <span class="enum">enumerated</span>
+ <span id="typeTemplate">
+ <span>
+ <span>string</span>
+ <span>["document_start", "document_end", "document_idle"]</span>
+ </span>
+ </span>
+ )
+ </div>
+ </em>
+ </dt>
+ <dd class="todo">
+ Undocumented.
+ </dd>
+ <!-- OBJECT PROPERTIES -->
+ <!-- OBJECT METHODS -->
+ <!-- OBJECT EVENT FIELDS -->
+ <!-- FUNCTION PARAMETERS -->
+ </div>
</div>
</dl>
</dd>
@@ -1675,6 +1703,34 @@ For other examples and for help in viewing the source code, see
<!-- OBJECT EVENT FIELDS -->
<!-- FUNCTION PARAMETERS -->
</div>
+ </div><div>
+ <div>
+ <dt>
+ <var>run_at</var>
+ <em>
+ <!-- TYPE -->
+ <div style="display:inline">
+ (
+ <span class="optional">optional</span>
+ <span class="enum">enumerated</span>
+ <span id="typeTemplate">
+ <span>
+ <span>string</span>
+ <span>["document_start", "document_end", "document_idle"]</span>
+ </span>
+ </span>
+ )
+ </div>
+ </em>
+ </dt>
+ <dd class="todo">
+ Undocumented.
+ </dd>
+ <!-- OBJECT PROPERTIES -->
+ <!-- OBJECT METHODS -->
+ <!-- OBJECT EVENT FIELDS -->
+ <!-- FUNCTION PARAMETERS -->
+ </div>
</div>
</dl>
</dd>
diff --git a/chrome/common/extensions/extension_messages.h b/chrome/common/extensions/extension_messages.h
index 3503161..661f060 100644
--- a/chrome/common/extensions/extension_messages.h
+++ b/chrome/common/extensions/extension_messages.h
@@ -67,6 +67,9 @@ IPC_STRUCT_BEGIN(ExtensionMsg_ExecuteCode_Params)
// Whether to inject into all frames, or only the root frame.
IPC_STRUCT_MEMBER(bool, all_frames)
+ // When to inject the code.
+ IPC_STRUCT_MEMBER(int, run_at)
+
// Whether to execute code in the main world (as opposed to an isolated
// world).
IPC_STRUCT_MEMBER(bool, in_main_world)
diff --git a/chrome/common/extensions/user_script.h b/chrome/common/extensions/user_script.h
index 47b0287..a50a951 100644
--- a/chrome/common/extensions/user_script.h
+++ b/chrome/common/extensions/user_script.h
@@ -39,6 +39,7 @@ class UserScript {
// Locations that user scripts can be run inside the document.
enum RunLocation {
+ UNDEFINED,
DOCUMENT_START, // After the documentElemnet is created, but before
// anything else happens.
DOCUMENT_END, // After the entire document is parsed. Same as
@@ -47,7 +48,6 @@ class UserScript {
// is "idle". Currently this uses the simple heuristic of:
// min(DOM_CONTENT_LOADED + TIMEOUT, ONLOAD), but no
// particular injection point is guaranteed.
-
RUN_LOCATION_LAST // Leave this as the last item.
};
diff --git a/chrome/renderer/extensions/extension_helper.cc b/chrome/renderer/extensions/extension_helper.cc
index f3f0532..73027de 100644
--- a/chrome/renderer/extensions/extension_helper.cc
+++ b/chrome/renderer/extensions/extension_helper.cc
@@ -19,7 +19,7 @@
#include "chrome/renderer/extensions/chrome_v8_context.h"
#include "chrome/renderer/extensions/extension_dispatcher.h"
#include "chrome/renderer/extensions/miscellaneous_bindings.h"
-#include "chrome/renderer/extensions/user_script_idle_scheduler.h"
+#include "chrome/renderer/extensions/user_script_scheduler.h"
#include "chrome/renderer/extensions/user_script_slave.h"
#include "content/public/renderer/render_view.h"
#include "third_party/WebKit/Source/WebKit/chromium/public/WebConsoleMessage.h"
@@ -41,11 +41,11 @@ using webkit_glue::ImageResourceFetcher;
using webkit_glue::ResourceFetcher;
namespace {
-// Keeps a mapping from the frame pointer to a UserScriptIdleScheduler object.
+// Keeps a mapping from the frame pointer to a UserScriptScheduler object.
// We store this mapping per process, because a frame can jump from one
// document to another with adoptNode, and so having the object be a
// RenderViewObserver means it might miss some notifications after it moves.
-typedef std::map<WebFrame*, UserScriptIdleScheduler*> SchedulerMap;
+typedef std::map<WebFrame*, UserScriptScheduler*> SchedulerMap;
static base::LazyInstance<SchedulerMap> g_schedulers =
LAZY_INSTANCE_INITIALIZER;
}
@@ -149,6 +149,9 @@ void ExtensionHelper::DidFinishLoad(WebKit::WebFrame* frame) {
void ExtensionHelper::DidCreateDocumentElement(WebFrame* frame) {
extension_dispatcher_->user_script_slave()->InjectScripts(
frame, UserScript::DOCUMENT_START);
+ SchedulerMap::iterator i = g_schedulers.Get().find(frame);
+ if (i != g_schedulers.Get().end())
+ i->second->DidCreateDocumentElement();
}
void ExtensionHelper::DidStartProvisionalLoad(WebKit::WebFrame* frame) {
@@ -181,7 +184,7 @@ void ExtensionHelper::DidCreateDataSource(WebFrame* frame, WebDataSource* ds) {
if (g_schedulers.Get().count(frame))
return;
- g_schedulers.Get()[frame] = new UserScriptIdleScheduler(
+ g_schedulers.Get()[frame] = new UserScriptScheduler(
frame, extension_dispatcher_);
}
diff --git a/chrome/renderer/extensions/user_script_idle_scheduler.cc b/chrome/renderer/extensions/user_script_scheduler.cc
index f0c0bee..58f3104 100644
--- a/chrome/renderer/extensions/user_script_idle_scheduler.cc
+++ b/chrome/renderer/extensions/user_script_scheduler.cc
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "chrome/renderer/extensions/user_script_idle_scheduler.h"
+#include "chrome/renderer/extensions/user_script_scheduler.h"
#include "base/bind.h"
#include "base/message_loop.h"
@@ -14,9 +14,9 @@
#include "chrome/renderer/extensions/extension_helper.h"
#include "chrome/renderer/extensions/user_script_slave.h"
#include "content/public/renderer/render_view.h"
+#include "third_party/WebKit/Source/WebKit/chromium/public/platform/WebString.h"
#include "third_party/WebKit/Source/WebKit/chromium/public/WebDocument.h"
#include "third_party/WebKit/Source/WebKit/chromium/public/WebFrame.h"
-#include "third_party/WebKit/Source/WebKit/chromium/public/platform/WebString.h"
#include "third_party/WebKit/Source/WebKit/chromium/public/WebView.h"
namespace {
@@ -30,21 +30,28 @@ using WebKit::WebFrame;
using WebKit::WebString;
using WebKit::WebView;
-UserScriptIdleScheduler::UserScriptIdleScheduler(
+UserScriptScheduler::UserScriptScheduler(
WebFrame* frame, ExtensionDispatcher* extension_dispatcher)
: ALLOW_THIS_IN_INITIALIZER_LIST(weak_factory_(this)),
frame_(frame),
- has_run_(false),
+ current_location_(UserScript::UNDEFINED),
+ has_run_idle_(false),
extension_dispatcher_(extension_dispatcher) {
+ for (int i = UserScript::UNDEFINED; i < UserScript::RUN_LOCATION_LAST; ++i) {
+ pending_execution_map_[static_cast<UserScript::RunLocation>(i)] =
+ std::queue<linked_ptr<ExtensionMsg_ExecuteCode_Params> >();
+ }
}
-UserScriptIdleScheduler::~UserScriptIdleScheduler() {
+UserScriptScheduler::~UserScriptScheduler() {
}
-void UserScriptIdleScheduler::ExecuteCode(
+void UserScriptScheduler::ExecuteCode(
const ExtensionMsg_ExecuteCode_Params& params) {
- if (!has_run_) {
- pending_code_execution_queue_.push(
+ UserScript::RunLocation run_at =
+ static_cast<UserScript::RunLocation>(params.run_at);
+ if (current_location_ < run_at) {
+ pending_execution_map_[run_at].push(
linked_ptr<ExtensionMsg_ExecuteCode_Params>(
new ExtensionMsg_ExecuteCode_Params(params)));
return;
@@ -53,51 +60,73 @@ void UserScriptIdleScheduler::ExecuteCode(
ExecuteCodeImpl(params);
}
-void UserScriptIdleScheduler::DidFinishDocumentLoad() {
+void UserScriptScheduler::DidCreateDocumentElement() {
+ current_location_ = UserScript::DOCUMENT_START;
+ MaybeRun();
+}
+
+void UserScriptScheduler::DidFinishDocumentLoad() {
+ current_location_ = UserScript::DOCUMENT_END;
+ MaybeRun();
+ // Schedule a run for DOCUMENT_IDLE
MessageLoop::current()->PostDelayedTask(
- FROM_HERE, base::Bind(&UserScriptIdleScheduler::MaybeRun,
+ FROM_HERE, base::Bind(&UserScriptScheduler::IdleTimeout,
weak_factory_.GetWeakPtr()),
base::TimeDelta::FromMilliseconds(kUserScriptIdleTimeoutMs));
}
-void UserScriptIdleScheduler::DidFinishLoad() {
+void UserScriptScheduler::DidFinishLoad() {
+ current_location_ = UserScript::DOCUMENT_IDLE;
// Ensure that running scripts does not keep any progress UI running.
MessageLoop::current()->PostTask(
- FROM_HERE, base::Bind(&UserScriptIdleScheduler::MaybeRun,
+ FROM_HERE, base::Bind(&UserScriptScheduler::MaybeRun,
weak_factory_.GetWeakPtr()));
}
-void UserScriptIdleScheduler::DidStartProvisionalLoad() {
+void UserScriptScheduler::DidStartProvisionalLoad() {
// The frame is navigating, so reset the state since we'll want to inject
// scripts once the load finishes.
- has_run_ = false;
+ current_location_ = UserScript::UNDEFINED;
+ has_run_idle_ = false;
weak_factory_.InvalidateWeakPtrs();
- while (!pending_code_execution_queue_.empty())
- pending_code_execution_queue_.pop();
+ std::map<UserScript::RunLocation, ExecutionQueue>::iterator itr =
+ pending_execution_map_.begin();
+ for (itr = pending_execution_map_.begin();
+ itr != pending_execution_map_.end(); ++itr) {
+ while (!itr->second.empty())
+ itr->second.pop();
+ }
}
-void UserScriptIdleScheduler::MaybeRun() {
- if (has_run_)
- return;
+void UserScriptScheduler::IdleTimeout() {
+ current_location_ = UserScript::DOCUMENT_IDLE;
+ MaybeRun();
+}
- // Note: we must set this before calling ExecuteCodeImpl, because that may
- // result in a synchronous call back into MaybeRun if there is a pending task
- // currently in the queue.
- // http://code.google.com/p/chromium/issues/detail?id=29644
- has_run_ = true;
+void UserScriptScheduler::MaybeRun() {
+ if (current_location_ == UserScript::UNDEFINED)
+ return;
- extension_dispatcher_->user_script_slave()->InjectScripts(
- frame_, UserScript::DOCUMENT_IDLE);
+ if (!has_run_idle_ && current_location_ == UserScript::DOCUMENT_IDLE) {
+ has_run_idle_ = true;
+ extension_dispatcher_->user_script_slave()->InjectScripts(
+ frame_, UserScript::DOCUMENT_IDLE);
+ }
- while (!pending_code_execution_queue_.empty()) {
- linked_ptr<ExtensionMsg_ExecuteCode_Params>& params =
- pending_code_execution_queue_.front();
- ExecuteCodeImpl(*params);
- pending_code_execution_queue_.pop();
+ // Run all tasks from the current time and earlier.
+ for (int i = UserScript::DOCUMENT_START;
+ i <= current_location_; ++i) {
+ UserScript::RunLocation run_time = static_cast<UserScript::RunLocation>(i);
+ while (!pending_execution_map_[run_time].empty()) {
+ linked_ptr<ExtensionMsg_ExecuteCode_Params>& params =
+ pending_execution_map_[run_time].front();
+ ExecuteCodeImpl(*params);
+ pending_execution_map_[run_time].pop();
+ }
}
}
-void UserScriptIdleScheduler::ExecuteCodeImpl(
+void UserScriptScheduler::ExecuteCodeImpl(
const ExtensionMsg_ExecuteCode_Params& params) {
const Extension* extension = extension_dispatcher_->extensions()->GetByID(
params.extension_id);
@@ -168,7 +197,7 @@ void UserScriptIdleScheduler::ExecuteCodeImpl(
render_view->GetRoutingID(), params.request_id, true, ""));
}
-bool UserScriptIdleScheduler::GetAllChildFrames(
+bool UserScriptScheduler::GetAllChildFrames(
WebFrame* parent_frame,
std::vector<WebFrame*>* frames_vector) const {
if (!parent_frame)
diff --git a/chrome/renderer/extensions/user_script_idle_scheduler.h b/chrome/renderer/extensions/user_script_scheduler.h
index 782ce86..aaccc045 100644
--- a/chrome/renderer/extensions/user_script_idle_scheduler.h
+++ b/chrome/renderer/extensions/user_script_scheduler.h
@@ -1,14 +1,15 @@
-// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Copyright (c) 2012 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_EXTENSIONS_USER_SCRIPT_IDLE_SCHEDULER_H_
-#define CHROME_RENDERER_EXTENSIONS_USER_SCRIPT_IDLE_SCHEDULER_H_
+#ifndef CHROME_RENDERER_EXTENSIONS_USER_SCRIPT_SCHEDULER_H_
+#define CHROME_RENDERER_EXTENSIONS_USER_SCRIPT_SCHEDULER_H_
#pragma once
+#include <map>
#include <queue>
-#include <vector>
+#include "chrome/common/extensions/user_script.h"
#include "base/memory/linked_ptr.h"
#include "base/memory/weak_ptr.h"
@@ -20,9 +21,12 @@ namespace WebKit {
class WebFrame;
}
-// Implements support for injecting scripts at "document idle". Currently,
-// determining idleness is simple: it is whichever of the following happens
-// first:
+// Implements support for injecting scripts at different times in the document
+// loading process. The different possible time are described in
+// UserScript::RunLocation.
+//
+// Currently, determining idleness is simple: it is whichever of the following
+// happens first:
//
// a) When the initial DOM for a page is complete + kUserScriptIdleTimeout,
// b) or when the page has completely loaded including all subresources.
@@ -36,18 +40,23 @@ class WebFrame;
// RenderViews thanks to adoptNode. So we have each RenderView's
// ExtensionHelper proxy these calls to the renderer process'
// ExtensionDispatcher, which contains the mapping from WebFrame to us.
-class UserScriptIdleScheduler {
+class UserScriptScheduler {
public:
- UserScriptIdleScheduler(WebKit::WebFrame* frame,
+ UserScriptScheduler(WebKit::WebFrame* frame,
ExtensionDispatcher* extension_dispatcher);
- ~UserScriptIdleScheduler();
+ ~UserScriptScheduler();
void ExecuteCode(const ExtensionMsg_ExecuteCode_Params& params);
+ void DidCreateDocumentElement();
void DidFinishDocumentLoad();
void DidFinishLoad();
void DidStartProvisionalLoad();
private:
+ typedef
+ std::queue<linked_ptr<ExtensionMsg_ExecuteCode_Params> >
+ ExecutionQueue;
+
// Run user scripts, except if they've already run for this frame, or the
// frame has been destroyed.
void MaybeRun();
@@ -60,19 +69,25 @@ class UserScriptIdleScheduler {
bool GetAllChildFrames(WebKit::WebFrame* parent_frame,
std::vector<WebKit::WebFrame*>* frames_vector) const;
- base::WeakPtrFactory<UserScriptIdleScheduler> weak_factory_;
+ // Call to signify thet the idle timeout has expired.
+ void IdleTimeout();
+
+ base::WeakPtrFactory<UserScriptScheduler> weak_factory_;
// The Frame we will run scripts in.
WebKit::WebFrame* frame_;
- // Whether we have already run scripts.
- bool has_run_;
+ // The current location in the document loading process.
+ // Will be UserScript::UNDEFINED if it is before any scripts should be run.
+ UserScript::RunLocation current_location_;
+
+ // Whether we have already run the idle scripts.
+ bool has_run_idle_;
// This is only used if we're for the main frame.
- std::queue<linked_ptr<ExtensionMsg_ExecuteCode_Params> >
- pending_code_execution_queue_;
+ std::map<UserScript::RunLocation, ExecutionQueue> pending_execution_map_;
ExtensionDispatcher* extension_dispatcher_;
};
-#endif // CHROME_RENDERER_EXTENSIONS_USER_SCRIPT_IDLE_SCHEDULER_H_
+#endif // CHROME_RENDERER_EXTENSIONS_USER_SCRIPT_SCHEDULER_H_
diff --git a/chrome/test/data/extensions/api_test/executescript/run_at/manifest.json b/chrome/test/data/extensions/api_test/executescript/run_at/manifest.json
new file mode 100644
index 0000000..bc4cbc6
--- /dev/null
+++ b/chrome/test/data/extensions/api_test/executescript/run_at/manifest.json
@@ -0,0 +1,10 @@
+{
+ "version": "1.0.0.0",
+ "manifest_version": 2,
+ "name": "run_at test",
+ "description": "Test the run_at property of executeScript and insertCSS",
+ "background": {
+ "scripts": ["test.js"]
+ },
+ "permissions": ["tabs", "http://b.com/"]
+}
diff --git a/chrome/test/data/extensions/api_test/executescript/run_at/test.html b/chrome/test/data/extensions/api_test/executescript/run_at/test.html
new file mode 100644
index 0000000..ccd49d9
--- /dev/null
+++ b/chrome/test/data/extensions/api_test/executescript/run_at/test.html
@@ -0,0 +1,3 @@
+<html>
+ <title>Initial</title>
+</html>
diff --git a/chrome/test/data/extensions/api_test/executescript/run_at/test.js b/chrome/test/data/extensions/api_test/executescript/run_at/test.js
new file mode 100644
index 0000000..eed5b29
--- /dev/null
+++ b/chrome/test/data/extensions/api_test/executescript/run_at/test.js
@@ -0,0 +1,29 @@
+// Copyright (c) 2012 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.
+
+var relativePath =
+ '/files/extensions/api_test/executescript/run_at/test.html';
+var testUrl = 'http://b.com:PORT' + relativePath;
+
+chrome.test.getConfig(function(config) {
+ testUrl = testUrl.replace(/PORT/, config.testServer.port);
+ chrome.tabs.onUpdated.addListener(function(tabId, changeInfo, tab) {
+ if (changeInfo.status != 'complete')
+ return;
+ chrome.tabs.onUpdated.removeListener(arguments.callee);
+ chrome.test.runTests([
+ function executeAtStartShouldSucceed() {
+ var scriptDetails = {};
+ scriptDetails.code = "document.title = 'Injected';";
+ scriptDetails.runAt = "document_start";
+ chrome.tabs.executeScript(tabId, scriptDetails, function() {
+ chrome.tabs.get(tabId, chrome.test.callbackPass(function(tab) {
+ chrome.test.assertEq('Injected', tab.title);
+ }));
+ });
+ },
+ ]);
+ });
+ chrome.tabs.create({ url: testUrl });
+});