diff options
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 }); +}); |