[email protected] | ac2f8937 | 2014-06-23 21:44:25 | [diff] [blame] | 1 | // Copyright 2014 The Chromium Authors. All rights reserved. |
| 2 | // Use of this source code is governed by a BSD-style license that can be |
| 3 | // found in the LICENSE file. |
| 4 | |
| 5 | #include "extensions/renderer/script_injection_manager.h" |
| 6 | |
rdevlin.cronin | 1463eeaf | 2014-12-11 18:06:56 | [diff] [blame] | 7 | #include "base/auto_reset.h" |
[email protected] | ac2f8937 | 2014-06-23 21:44:25 | [diff] [blame] | 8 | #include "base/bind.h" |
| 9 | #include "base/memory/weak_ptr.h" |
[email protected] | ac2f8937 | 2014-06-23 21:44:25 | [diff] [blame] | 10 | #include "base/values.h" |
rdevlin.cronin | 4bb32d7 | 2015-06-02 21:55:01 | [diff] [blame] | 11 | #include "content/public/renderer/render_frame.h" |
| 12 | #include "content/public/renderer/render_frame_observer.h" |
alexclarke | 5f9af564 | 2015-01-09 19:24:31 | [diff] [blame] | 13 | #include "content/public/renderer/render_thread.h" |
[email protected] | ac2f8937 | 2014-06-23 21:44:25 | [diff] [blame] | 14 | #include "extensions/common/extension.h" |
| 15 | #include "extensions/common/extension_messages.h" |
| 16 | #include "extensions/common/extension_set.h" |
rdevlin.cronin | b67a3477 | 2015-06-04 16:32:38 | [diff] [blame] | 17 | #include "extensions/renderer/extension_frame_helper.h" |
hanxi | a5c856cf | 2015-02-13 20:51:58 | [diff] [blame] | 18 | #include "extensions/renderer/extension_injection_host.h" |
[email protected] | c11e659 | 2014-06-27 17:07:34 | [diff] [blame] | 19 | #include "extensions/renderer/programmatic_script_injector.h" |
annekao | 6572d5c | 2015-08-19 16:13:36 | [diff] [blame^] | 20 | #include "extensions/renderer/renderer_extension_registry.h" |
[email protected] | c11e659 | 2014-06-27 17:07:34 | [diff] [blame] | 21 | #include "extensions/renderer/script_injection.h" |
| 22 | #include "extensions/renderer/scripts_run_info.h" |
hanxi | 79f7a57 | 2015-03-09 20:46:59 | [diff] [blame] | 23 | #include "extensions/renderer/web_ui_injection_host.h" |
[email protected] | ac2f8937 | 2014-06-23 21:44:25 | [diff] [blame] | 24 | #include "ipc/ipc_message_macros.h" |
markdittmer | 9ea140f | 2014-08-29 02:46:15 | [diff] [blame] | 25 | #include "third_party/WebKit/public/web/WebDocument.h" |
[email protected] | ac2f8937 | 2014-06-23 21:44:25 | [diff] [blame] | 26 | #include "third_party/WebKit/public/web/WebFrame.h" |
| 27 | #include "third_party/WebKit/public/web/WebLocalFrame.h" |
| 28 | #include "third_party/WebKit/public/web/WebView.h" |
| 29 | #include "url/gurl.h" |
| 30 | |
| 31 | namespace extensions { |
| 32 | |
| 33 | namespace { |
| 34 | |
| 35 | // The length of time to wait after the DOM is complete to try and run user |
| 36 | // scripts. |
| 37 | const int kScriptIdleTimeoutInMs = 200; |
| 38 | |
rdevlin.cronin | 1463eeaf | 2014-12-11 18:06:56 | [diff] [blame] | 39 | // Returns the RunLocation that follows |run_location|. |
| 40 | UserScript::RunLocation NextRunLocation(UserScript::RunLocation run_location) { |
| 41 | switch (run_location) { |
| 42 | case UserScript::DOCUMENT_START: |
| 43 | return UserScript::DOCUMENT_END; |
| 44 | case UserScript::DOCUMENT_END: |
| 45 | return UserScript::DOCUMENT_IDLE; |
| 46 | case UserScript::DOCUMENT_IDLE: |
| 47 | return UserScript::RUN_LOCATION_LAST; |
| 48 | case UserScript::UNDEFINED: |
| 49 | case UserScript::RUN_DEFERRED: |
| 50 | case UserScript::BROWSER_DRIVEN: |
| 51 | case UserScript::RUN_LOCATION_LAST: |
| 52 | break; |
| 53 | } |
| 54 | NOTREACHED(); |
| 55 | return UserScript::RUN_LOCATION_LAST; |
| 56 | } |
| 57 | |
[email protected] | ac2f8937 | 2014-06-23 21:44:25 | [diff] [blame] | 58 | } // namespace |
| 59 | |
rdevlin.cronin | 4bb32d7 | 2015-06-02 21:55:01 | [diff] [blame] | 60 | class ScriptInjectionManager::RFOHelper : public content::RenderFrameObserver { |
[email protected] | ac2f8937 | 2014-06-23 21:44:25 | [diff] [blame] | 61 | public: |
rdevlin.cronin | 4bb32d7 | 2015-06-02 21:55:01 | [diff] [blame] | 62 | RFOHelper(content::RenderFrame* render_frame, |
| 63 | ScriptInjectionManager* manager); |
| 64 | ~RFOHelper() override; |
[email protected] | ac2f8937 | 2014-06-23 21:44:25 | [diff] [blame] | 65 | |
| 66 | private: |
rdevlin.cronin | 4bb32d7 | 2015-06-02 21:55:01 | [diff] [blame] | 67 | // RenderFrameObserver implementation. |
dcheng | 9168b2f | 2014-10-21 12:38:24 | [diff] [blame] | 68 | bool OnMessageReceived(const IPC::Message& message) override; |
rdevlin.cronin | 4bb32d7 | 2015-06-02 21:55:01 | [diff] [blame] | 69 | void DidCreateNewDocument() override; |
| 70 | void DidCreateDocumentElement() override; |
| 71 | void DidFinishDocumentLoad() override; |
| 72 | void DidFinishLoad() override; |
| 73 | void FrameDetached() override; |
dcheng | 9168b2f | 2014-10-21 12:38:24 | [diff] [blame] | 74 | void OnDestruct() override; |
[email protected] | ac2f8937 | 2014-06-23 21:44:25 | [diff] [blame] | 75 | |
| 76 | virtual void OnExecuteCode(const ExtensionMsg_ExecuteCode_Params& params); |
markdittmer | 9ea140f | 2014-08-29 02:46:15 | [diff] [blame] | 77 | virtual void OnExecuteDeclarativeScript(int tab_id, |
| 78 | const ExtensionId& extension_id, |
| 79 | int script_id, |
| 80 | const GURL& url); |
[email protected] | ac2f8937 | 2014-06-23 21:44:25 | [diff] [blame] | 81 | virtual void OnPermitScriptInjection(int64 request_id); |
| 82 | |
| 83 | // Tells the ScriptInjectionManager to run tasks associated with |
| 84 | // document_idle. |
rdevlin.cronin | 4bb32d7 | 2015-06-02 21:55:01 | [diff] [blame] | 85 | void RunIdle(); |
[email protected] | ac2f8937 | 2014-06-23 21:44:25 | [diff] [blame] | 86 | |
rdevlin.cronin | 4bb32d7 | 2015-06-02 21:55:01 | [diff] [blame] | 87 | // Indicate that the frame is no longer valid because it is starting |
[email protected] | 5672620d | 2014-07-30 10:07:17 | [diff] [blame] | 88 | // a new load or closing. |
rdevlin.cronin | 3ae4a3201 | 2015-06-30 17:43:19 | [diff] [blame] | 89 | void InvalidateAndResetFrame(); |
[email protected] | 5672620d | 2014-07-30 10:07:17 | [diff] [blame] | 90 | |
| 91 | // The owning ScriptInjectionManager. |
[email protected] | ac2f8937 | 2014-06-23 21:44:25 | [diff] [blame] | 92 | ScriptInjectionManager* manager_; |
[email protected] | 5672620d | 2014-07-30 10:07:17 | [diff] [blame] | 93 | |
rdevlin.cronin | 3ae4a3201 | 2015-06-30 17:43:19 | [diff] [blame] | 94 | bool should_run_idle_; |
[email protected] | 5672620d | 2014-07-30 10:07:17 | [diff] [blame] | 95 | |
rdevlin.cronin | 4bb32d7 | 2015-06-02 21:55:01 | [diff] [blame] | 96 | base::WeakPtrFactory<RFOHelper> weak_factory_; |
[email protected] | ac2f8937 | 2014-06-23 21:44:25 | [diff] [blame] | 97 | }; |
| 98 | |
rdevlin.cronin | 3ae4a3201 | 2015-06-30 17:43:19 | [diff] [blame] | 99 | ScriptInjectionManager::RFOHelper::RFOHelper(content::RenderFrame* render_frame, |
| 100 | ScriptInjectionManager* manager) |
rdevlin.cronin | 4bb32d7 | 2015-06-02 21:55:01 | [diff] [blame] | 101 | : content::RenderFrameObserver(render_frame), |
[email protected] | ac2f8937 | 2014-06-23 21:44:25 | [diff] [blame] | 102 | manager_(manager), |
rdevlin.cronin | 3ae4a3201 | 2015-06-30 17:43:19 | [diff] [blame] | 103 | should_run_idle_(true), |
[email protected] | ac2f8937 | 2014-06-23 21:44:25 | [diff] [blame] | 104 | weak_factory_(this) { |
| 105 | } |
| 106 | |
rdevlin.cronin | 4bb32d7 | 2015-06-02 21:55:01 | [diff] [blame] | 107 | ScriptInjectionManager::RFOHelper::~RFOHelper() { |
[email protected] | ac2f8937 | 2014-06-23 21:44:25 | [diff] [blame] | 108 | } |
| 109 | |
rdevlin.cronin | 4bb32d7 | 2015-06-02 21:55:01 | [diff] [blame] | 110 | bool ScriptInjectionManager::RFOHelper::OnMessageReceived( |
[email protected] | ac2f8937 | 2014-06-23 21:44:25 | [diff] [blame] | 111 | const IPC::Message& message) { |
| 112 | bool handled = true; |
rdevlin.cronin | 4bb32d7 | 2015-06-02 21:55:01 | [diff] [blame] | 113 | IPC_BEGIN_MESSAGE_MAP(ScriptInjectionManager::RFOHelper, message) |
[email protected] | ac2f8937 | 2014-06-23 21:44:25 | [diff] [blame] | 114 | IPC_MESSAGE_HANDLER(ExtensionMsg_ExecuteCode, OnExecuteCode) |
| 115 | IPC_MESSAGE_HANDLER(ExtensionMsg_PermitScriptInjection, |
| 116 | OnPermitScriptInjection) |
markdittmer | 9ea140f | 2014-08-29 02:46:15 | [diff] [blame] | 117 | IPC_MESSAGE_HANDLER(ExtensionMsg_ExecuteDeclarativeScript, |
| 118 | OnExecuteDeclarativeScript) |
[email protected] | ac2f8937 | 2014-06-23 21:44:25 | [diff] [blame] | 119 | IPC_MESSAGE_UNHANDLED(handled = false) |
| 120 | IPC_END_MESSAGE_MAP() |
| 121 | return handled; |
| 122 | } |
| 123 | |
rdevlin.cronin | 4bb32d7 | 2015-06-02 21:55:01 | [diff] [blame] | 124 | void ScriptInjectionManager::RFOHelper::DidCreateNewDocument() { |
rob | 5ef11ff | 2014-11-17 23:56:20 | [diff] [blame] | 125 | // A new document is going to be shown, so invalidate the old document state. |
| 126 | // Check that the frame's state is known before invalidating the frame, |
| 127 | // because it is possible that a script injection was scheduled before the |
| 128 | // page was loaded, e.g. by navigating to a javascript: URL before the page |
| 129 | // has loaded. |
rdevlin.cronin | 4bb32d7 | 2015-06-02 21:55:01 | [diff] [blame] | 130 | if (manager_->frame_statuses_.count(render_frame()) != 0) |
rdevlin.cronin | 3ae4a3201 | 2015-06-30 17:43:19 | [diff] [blame] | 131 | InvalidateAndResetFrame(); |
rob | 5ef11ff | 2014-11-17 23:56:20 | [diff] [blame] | 132 | } |
| 133 | |
rdevlin.cronin | 4bb32d7 | 2015-06-02 21:55:01 | [diff] [blame] | 134 | void ScriptInjectionManager::RFOHelper::DidCreateDocumentElement() { |
| 135 | manager_->StartInjectScripts(render_frame(), UserScript::DOCUMENT_START); |
[email protected] | ac2f8937 | 2014-06-23 21:44:25 | [diff] [blame] | 136 | } |
| 137 | |
rdevlin.cronin | 4bb32d7 | 2015-06-02 21:55:01 | [diff] [blame] | 138 | void ScriptInjectionManager::RFOHelper::DidFinishDocumentLoad() { |
skyostil | ed8969c | 2015-07-20 16:57:08 | [diff] [blame] | 139 | DCHECK(content::RenderThread::Get()); |
rdevlin.cronin | 4bb32d7 | 2015-06-02 21:55:01 | [diff] [blame] | 140 | manager_->StartInjectScripts(render_frame(), UserScript::DOCUMENT_END); |
[email protected] | 5672620d | 2014-07-30 10:07:17 | [diff] [blame] | 141 | // We try to run idle in two places: here and DidFinishLoad. |
| 142 | // DidFinishDocumentLoad() corresponds to completing the document's load, |
| 143 | // whereas DidFinishLoad corresponds to completing the document and all |
| 144 | // subresources' load. We don't want to hold up script injection for a |
| 145 | // particularly slow subresource, so we set a delayed task from here - but if |
| 146 | // we finish everything before that point (i.e., DidFinishLoad() is |
| 147 | // triggered), then there's no reason to keep waiting. |
skyostil | ed8969c | 2015-07-20 16:57:08 | [diff] [blame] | 148 | base::ThreadTaskRunnerHandle::Get()->PostDelayedTask( |
[email protected] | ac2f8937 | 2014-06-23 21:44:25 | [diff] [blame] | 149 | FROM_HERE, |
rdevlin.cronin | 4bb32d7 | 2015-06-02 21:55:01 | [diff] [blame] | 150 | base::Bind(&ScriptInjectionManager::RFOHelper::RunIdle, |
| 151 | weak_factory_.GetWeakPtr()), |
[email protected] | ac2f8937 | 2014-06-23 21:44:25 | [diff] [blame] | 152 | base::TimeDelta::FromMilliseconds(kScriptIdleTimeoutInMs)); |
| 153 | } |
| 154 | |
rdevlin.cronin | 4bb32d7 | 2015-06-02 21:55:01 | [diff] [blame] | 155 | void ScriptInjectionManager::RFOHelper::DidFinishLoad() { |
skyostil | ed8969c | 2015-07-20 16:57:08 | [diff] [blame] | 156 | DCHECK(content::RenderThread::Get()); |
[email protected] | 5672620d | 2014-07-30 10:07:17 | [diff] [blame] | 157 | // Ensure that we don't block any UI progress by running scripts. |
skyostil | ed8969c | 2015-07-20 16:57:08 | [diff] [blame] | 158 | base::ThreadTaskRunnerHandle::Get()->PostTask( |
[email protected] | ac2f8937 | 2014-06-23 21:44:25 | [diff] [blame] | 159 | FROM_HERE, |
rdevlin.cronin | 4bb32d7 | 2015-06-02 21:55:01 | [diff] [blame] | 160 | base::Bind(&ScriptInjectionManager::RFOHelper::RunIdle, |
| 161 | weak_factory_.GetWeakPtr())); |
[email protected] | ac2f8937 | 2014-06-23 21:44:25 | [diff] [blame] | 162 | } |
| 163 | |
rdevlin.cronin | 4bb32d7 | 2015-06-02 21:55:01 | [diff] [blame] | 164 | void ScriptInjectionManager::RFOHelper::FrameDetached() { |
[email protected] | 5672620d | 2014-07-30 10:07:17 | [diff] [blame] | 165 | // The frame is closing - invalidate. |
rdevlin.cronin | 3ae4a3201 | 2015-06-30 17:43:19 | [diff] [blame] | 166 | InvalidateAndResetFrame(); |
[email protected] | ac2f8937 | 2014-06-23 21:44:25 | [diff] [blame] | 167 | } |
| 168 | |
rdevlin.cronin | 4bb32d7 | 2015-06-02 21:55:01 | [diff] [blame] | 169 | void ScriptInjectionManager::RFOHelper::OnDestruct() { |
[email protected] | ac2f8937 | 2014-06-23 21:44:25 | [diff] [blame] | 170 | manager_->RemoveObserver(this); |
| 171 | } |
| 172 | |
rdevlin.cronin | 4bb32d7 | 2015-06-02 21:55:01 | [diff] [blame] | 173 | void ScriptInjectionManager::RFOHelper::OnExecuteCode( |
[email protected] | ac2f8937 | 2014-06-23 21:44:25 | [diff] [blame] | 174 | const ExtensionMsg_ExecuteCode_Params& params) { |
rdevlin.cronin | 4bb32d7 | 2015-06-02 21:55:01 | [diff] [blame] | 175 | manager_->HandleExecuteCode(params, render_frame()); |
[email protected] | ac2f8937 | 2014-06-23 21:44:25 | [diff] [blame] | 176 | } |
| 177 | |
rdevlin.cronin | 4bb32d7 | 2015-06-02 21:55:01 | [diff] [blame] | 178 | void ScriptInjectionManager::RFOHelper::OnExecuteDeclarativeScript( |
markdittmer | 9ea140f | 2014-08-29 02:46:15 | [diff] [blame] | 179 | int tab_id, |
| 180 | const ExtensionId& extension_id, |
| 181 | int script_id, |
| 182 | const GURL& url) { |
rdevlin.cronin | 45dca7f | 2015-06-08 19:47:03 | [diff] [blame] | 183 | // TODO(markdittmer): URL-checking isn't the best security measure. |
rdevlin.cronin | 4bb32d7 | 2015-06-02 21:55:01 | [diff] [blame] | 184 | // Begin script injection workflow only if the current URL is identical to |
markdittmer | 9ea140f | 2014-08-29 02:46:15 | [diff] [blame] | 185 | // the one that matched declarative conditions in the browser. |
rdevlin.cronin | 45dca7f | 2015-06-08 19:47:03 | [diff] [blame] | 186 | if (render_frame()->GetWebFrame()->document().url() == url) { |
rdevlin.cronin | 4bb32d7 | 2015-06-02 21:55:01 | [diff] [blame] | 187 | manager_->HandleExecuteDeclarativeScript(render_frame(), |
markdittmer | 9ea140f | 2014-08-29 02:46:15 | [diff] [blame] | 188 | tab_id, |
| 189 | extension_id, |
| 190 | script_id, |
| 191 | url); |
| 192 | } |
| 193 | } |
| 194 | |
rdevlin.cronin | 4bb32d7 | 2015-06-02 21:55:01 | [diff] [blame] | 195 | void ScriptInjectionManager::RFOHelper::OnPermitScriptInjection( |
[email protected] | ac2f8937 | 2014-06-23 21:44:25 | [diff] [blame] | 196 | int64 request_id) { |
| 197 | manager_->HandlePermitScriptInjection(request_id); |
| 198 | } |
| 199 | |
rdevlin.cronin | 4bb32d7 | 2015-06-02 21:55:01 | [diff] [blame] | 200 | void ScriptInjectionManager::RFOHelper::RunIdle() { |
[email protected] | 5672620d | 2014-07-30 10:07:17 | [diff] [blame] | 201 | // Only notify the manager if the frame hasn't either been removed or already |
| 202 | // had idle run since the task to RunIdle() was posted. |
rdevlin.cronin | 3ae4a3201 | 2015-06-30 17:43:19 | [diff] [blame] | 203 | if (should_run_idle_) { |
| 204 | should_run_idle_ = false; |
rdevlin.cronin | 4bb32d7 | 2015-06-02 21:55:01 | [diff] [blame] | 205 | manager_->StartInjectScripts(render_frame(), UserScript::DOCUMENT_IDLE); |
[email protected] | 5672620d | 2014-07-30 10:07:17 | [diff] [blame] | 206 | } |
| 207 | } |
| 208 | |
rdevlin.cronin | 3ae4a3201 | 2015-06-30 17:43:19 | [diff] [blame] | 209 | void ScriptInjectionManager::RFOHelper::InvalidateAndResetFrame() { |
| 210 | // Invalidate any pending idle injections, and reset the frame inject on idle. |
| 211 | weak_factory_.InvalidateWeakPtrs(); |
| 212 | // We reset to inject on idle, because the frame can be reused (in the case of |
| 213 | // navigation). |
| 214 | should_run_idle_ = true; |
rdevlin.cronin | 4bb32d7 | 2015-06-02 21:55:01 | [diff] [blame] | 215 | manager_->InvalidateForFrame(render_frame()); |
[email protected] | ac2f8937 | 2014-06-23 21:44:25 | [diff] [blame] | 216 | } |
| 217 | |
| 218 | ScriptInjectionManager::ScriptInjectionManager( |
[email protected] | 4c35690 | 2014-07-30 09:52:02 | [diff] [blame] | 219 | UserScriptSetManager* user_script_set_manager) |
annekao | 6572d5c | 2015-08-19 16:13:36 | [diff] [blame^] | 220 | : user_script_set_manager_(user_script_set_manager), |
[email protected] | 4c35690 | 2014-07-30 09:52:02 | [diff] [blame] | 221 | user_script_set_manager_observer_(this) { |
| 222 | user_script_set_manager_observer_.Add(user_script_set_manager_); |
[email protected] | ac2f8937 | 2014-06-23 21:44:25 | [diff] [blame] | 223 | } |
| 224 | |
| 225 | ScriptInjectionManager::~ScriptInjectionManager() { |
| 226 | } |
| 227 | |
rdevlin.cronin | 4bb32d7 | 2015-06-02 21:55:01 | [diff] [blame] | 228 | void ScriptInjectionManager::OnRenderFrameCreated( |
| 229 | content::RenderFrame* render_frame) { |
| 230 | rfo_helpers_.push_back(new RFOHelper(render_frame, this)); |
[email protected] | ac2f8937 | 2014-06-23 21:44:25 | [diff] [blame] | 231 | } |
| 232 | |
hanxi | 9b84166 | 2015-03-04 14:36:41 | [diff] [blame] | 233 | void ScriptInjectionManager::OnExtensionUnloaded( |
| 234 | const std::string& extension_id) { |
| 235 | for (auto iter = pending_injections_.begin(); |
| 236 | iter != pending_injections_.end();) { |
| 237 | if ((*iter)->host_id().id() == extension_id) { |
| 238 | (*iter)->OnHostRemoved(); |
| 239 | iter = pending_injections_.erase(iter); |
| 240 | } else { |
| 241 | ++iter; |
| 242 | } |
| 243 | } |
kozyatinskiy | c8bc9a58 | 2015-03-06 09:33:41 | [diff] [blame] | 244 | } |
| 245 | |
| 246 | void ScriptInjectionManager::OnInjectionFinished( |
| 247 | ScriptInjection* injection) { |
| 248 | ScopedVector<ScriptInjection>::iterator iter = |
| 249 | std::find(running_injections_.begin(), |
| 250 | running_injections_.end(), |
| 251 | injection); |
| 252 | if (iter != running_injections_.end()) |
| 253 | running_injections_.erase(iter); |
hanxi | 9b84166 | 2015-03-04 14:36:41 | [diff] [blame] | 254 | } |
| 255 | |
[email protected] | ac2f8937 | 2014-06-23 21:44:25 | [diff] [blame] | 256 | void ScriptInjectionManager::OnUserScriptsUpdated( |
hanxi | 3df97b2 | 2015-03-11 23:40:06 | [diff] [blame] | 257 | const std::set<HostID>& changed_hosts, |
[email protected] | ac2f8937 | 2014-06-23 21:44:25 | [diff] [blame] | 258 | const std::vector<UserScript*>& scripts) { |
| 259 | for (ScopedVector<ScriptInjection>::iterator iter = |
| 260 | pending_injections_.begin(); |
| 261 | iter != pending_injections_.end();) { |
hanxi | 3df97b2 | 2015-03-11 23:40:06 | [diff] [blame] | 262 | if (changed_hosts.count((*iter)->host_id()) > 0) |
[email protected] | ac2f8937 | 2014-06-23 21:44:25 | [diff] [blame] | 263 | iter = pending_injections_.erase(iter); |
| 264 | else |
| 265 | ++iter; |
| 266 | } |
| 267 | } |
| 268 | |
rdevlin.cronin | 4bb32d7 | 2015-06-02 21:55:01 | [diff] [blame] | 269 | void ScriptInjectionManager::RemoveObserver(RFOHelper* helper) { |
| 270 | for (ScopedVector<RFOHelper>::iterator iter = rfo_helpers_.begin(); |
| 271 | iter != rfo_helpers_.end(); |
[email protected] | ac2f8937 | 2014-06-23 21:44:25 | [diff] [blame] | 272 | ++iter) { |
| 273 | if (*iter == helper) { |
rdevlin.cronin | 4bb32d7 | 2015-06-02 21:55:01 | [diff] [blame] | 274 | rfo_helpers_.erase(iter); |
[email protected] | ac2f8937 | 2014-06-23 21:44:25 | [diff] [blame] | 275 | break; |
| 276 | } |
| 277 | } |
| 278 | } |
| 279 | |
rdevlin.cronin | 4bb32d7 | 2015-06-02 21:55:01 | [diff] [blame] | 280 | void ScriptInjectionManager::InvalidateForFrame(content::RenderFrame* frame) { |
rdevlin.cronin | 3ae4a3201 | 2015-06-30 17:43:19 | [diff] [blame] | 281 | // If the frame invalidated is the frame being injected into, we need to |
| 282 | // note it. |
| 283 | active_injection_frames_.erase(frame); |
| 284 | |
[email protected] | ac2f8937 | 2014-06-23 21:44:25 | [diff] [blame] | 285 | for (ScopedVector<ScriptInjection>::iterator iter = |
| 286 | pending_injections_.begin(); |
| 287 | iter != pending_injections_.end();) { |
rdevlin.cronin | 3e11c986 | 2015-06-04 19:54:25 | [diff] [blame] | 288 | if ((*iter)->render_frame() == frame) |
[email protected] | ac2f8937 | 2014-06-23 21:44:25 | [diff] [blame] | 289 | iter = pending_injections_.erase(iter); |
| 290 | else |
| 291 | ++iter; |
| 292 | } |
| 293 | |
| 294 | frame_statuses_.erase(frame); |
| 295 | } |
| 296 | |
rdevlin.cronin | 1463eeaf | 2014-12-11 18:06:56 | [diff] [blame] | 297 | void ScriptInjectionManager::StartInjectScripts( |
rdevlin.cronin | 4bb32d7 | 2015-06-02 21:55:01 | [diff] [blame] | 298 | content::RenderFrame* frame, |
| 299 | UserScript::RunLocation run_location) { |
[email protected] | ac2f8937 | 2014-06-23 21:44:25 | [diff] [blame] | 300 | FrameStatusMap::iterator iter = frame_statuses_.find(frame); |
[email protected] | 5672620d | 2014-07-30 10:07:17 | [diff] [blame] | 301 | // We also don't execute if we detect that the run location is somehow out of |
| 302 | // order. This can happen if: |
| 303 | // - The first run location reported for the frame isn't DOCUMENT_START, or |
| 304 | // - The run location reported doesn't immediately follow the previous |
| 305 | // reported run location. |
| 306 | // We don't want to run because extensions may have requirements that scripts |
| 307 | // running in an earlier run location have run by the time a later script |
| 308 | // runs. Better to just not run. |
rdevlin.cronin | 1463eeaf | 2014-12-11 18:06:56 | [diff] [blame] | 309 | // Note that we check run_location > NextRunLocation() in the second clause |
| 310 | // (as opposed to !=) because earlier signals (like DidCreateDocumentElement) |
| 311 | // can happen multiple times, so we can receive earlier/equal run locations. |
[email protected] | 5672620d | 2014-07-30 10:07:17 | [diff] [blame] | 312 | if ((iter == frame_statuses_.end() && |
| 313 | run_location != UserScript::DOCUMENT_START) || |
rdevlin.cronin | 1463eeaf | 2014-12-11 18:06:56 | [diff] [blame] | 314 | (iter != frame_statuses_.end() && |
| 315 | run_location > NextRunLocation(iter->second))) { |
[email protected] | 5672620d | 2014-07-30 10:07:17 | [diff] [blame] | 316 | // We also invalidate the frame, because the run order of pending injections |
| 317 | // may also be bad. |
| 318 | InvalidateForFrame(frame); |
| 319 | return; |
rdevlin.cronin | 1463eeaf | 2014-12-11 18:06:56 | [diff] [blame] | 320 | } else if (iter != frame_statuses_.end() && iter->second >= run_location) { |
[email protected] | 5672620d | 2014-07-30 10:07:17 | [diff] [blame] | 321 | // Certain run location signals (like DidCreateDocumentElement) can happen |
| 322 | // multiple times. Ignore the subsequent signals. |
| 323 | return; |
[email protected] | ac2f8937 | 2014-06-23 21:44:25 | [diff] [blame] | 324 | } |
| 325 | |
[email protected] | 5672620d | 2014-07-30 10:07:17 | [diff] [blame] | 326 | // Otherwise, all is right in the world, and we can get on with the |
| 327 | // injections! |
[email protected] | 5672620d | 2014-07-30 10:07:17 | [diff] [blame] | 328 | frame_statuses_[frame] = run_location; |
kozyatinskiy | c8bc9a58 | 2015-03-06 09:33:41 | [diff] [blame] | 329 | InjectScripts(frame, run_location); |
rdevlin.cronin | 1463eeaf | 2014-12-11 18:06:56 | [diff] [blame] | 330 | } |
| 331 | |
| 332 | void ScriptInjectionManager::InjectScripts( |
rdevlin.cronin | 4bb32d7 | 2015-06-02 21:55:01 | [diff] [blame] | 333 | content::RenderFrame* frame, |
rdevlin.cronin | 1463eeaf | 2014-12-11 18:06:56 | [diff] [blame] | 334 | UserScript::RunLocation run_location) { |
rdevlin.cronin | d63148dc | 2014-12-16 19:22:34 | [diff] [blame] | 335 | // Find any injections that want to run on the given frame. |
rdevlin.cronin | d63148dc | 2014-12-16 19:22:34 | [diff] [blame] | 336 | ScopedVector<ScriptInjection> frame_injections; |
[email protected] | ac2f8937 | 2014-06-23 21:44:25 | [diff] [blame] | 337 | for (ScopedVector<ScriptInjection>::iterator iter = |
| 338 | pending_injections_.begin(); |
| 339 | iter != pending_injections_.end();) { |
rdevlin.cronin | 3e11c986 | 2015-06-04 19:54:25 | [diff] [blame] | 340 | if ((*iter)->render_frame() == frame) { |
rdevlin.cronin | d63148dc | 2014-12-16 19:22:34 | [diff] [blame] | 341 | frame_injections.push_back(*iter); |
| 342 | iter = pending_injections_.weak_erase(iter); |
[email protected] | ac2f8937 | 2014-06-23 21:44:25 | [diff] [blame] | 343 | } else { |
| 344 | ++iter; |
| 345 | } |
| 346 | } |
| 347 | |
rdevlin.cronin | d63148dc | 2014-12-16 19:22:34 | [diff] [blame] | 348 | // Add any injections for user scripts. |
rdevlin.cronin | b67a3477 | 2015-06-04 16:32:38 | [diff] [blame] | 349 | int tab_id = ExtensionFrameHelper::Get(frame)->tab_id(); |
[email protected] | 4c35690 | 2014-07-30 09:52:02 | [diff] [blame] | 350 | user_script_set_manager_->GetAllInjections( |
rdevlin.cronin | 3e11c986 | 2015-06-04 19:54:25 | [diff] [blame] | 351 | &frame_injections, frame, tab_id, run_location); |
rdevlin.cronin | d63148dc | 2014-12-16 19:22:34 | [diff] [blame] | 352 | |
rdevlin.cronin | 3ae4a3201 | 2015-06-30 17:43:19 | [diff] [blame] | 353 | // Note that we are running in |frame|. |
| 354 | active_injection_frames_.insert(frame); |
| 355 | |
| 356 | ScriptsRunInfo scripts_run_info(frame, run_location); |
kozyatinskiy | c8bc9a58 | 2015-03-06 09:33:41 | [diff] [blame] | 357 | std::vector<ScriptInjection*> released_injections; |
| 358 | frame_injections.release(&released_injections); |
rdevlin.cronin | 3ae4a3201 | 2015-06-30 17:43:19 | [diff] [blame] | 359 | for (ScriptInjection* injection : released_injections) { |
| 360 | // It's possible for the frame to be invalidated in the course of injection |
| 361 | // (if a script removes its own frame, for example). If this happens, abort. |
| 362 | if (!active_injection_frames_.count(frame)) |
| 363 | break; |
kozyatinskiy | c8bc9a58 | 2015-03-06 09:33:41 | [diff] [blame] | 364 | TryToInject(make_scoped_ptr(injection), run_location, &scripts_run_info); |
rdevlin.cronin | 3ae4a3201 | 2015-06-30 17:43:19 | [diff] [blame] | 365 | } |
kozyatinskiy | c8bc9a58 | 2015-03-06 09:33:41 | [diff] [blame] | 366 | |
rdevlin.cronin | 3ae4a3201 | 2015-06-30 17:43:19 | [diff] [blame] | 367 | // We are done running in the frame. |
| 368 | active_injection_frames_.erase(frame); |
| 369 | |
| 370 | scripts_run_info.LogRun(); |
kozyatinskiy | c8bc9a58 | 2015-03-06 09:33:41 | [diff] [blame] | 371 | } |
| 372 | |
| 373 | void ScriptInjectionManager::TryToInject( |
| 374 | scoped_ptr<ScriptInjection> injection, |
| 375 | UserScript::RunLocation run_location, |
| 376 | ScriptsRunInfo* scripts_run_info) { |
| 377 | // Try to inject the script. If the injection is waiting (i.e., for |
| 378 | // permission), add it to the list of pending injections. If the injection |
| 379 | // has blocked, add it to the list of running injections. |
rdevlin.cronin | 4bb32d7 | 2015-06-02 21:55:01 | [diff] [blame] | 380 | // The Unretained below is safe because this object owns all the |
| 381 | // ScriptInjections, so is guaranteed to outlive them. |
kozyatinskiy | c8bc9a58 | 2015-03-06 09:33:41 | [diff] [blame] | 382 | switch (injection->TryToInject( |
| 383 | run_location, |
| 384 | scripts_run_info, |
rdevlin.cronin | 4bb32d7 | 2015-06-02 21:55:01 | [diff] [blame] | 385 | base::Bind(&ScriptInjectionManager::OnInjectionFinished, |
| 386 | base::Unretained(this)))) { |
kozyatinskiy | c8bc9a58 | 2015-03-06 09:33:41 | [diff] [blame] | 387 | case ScriptInjection::INJECTION_WAITING: |
vaibhav1.a | d733268 | 2015-05-27 05:45:43 | [diff] [blame] | 388 | pending_injections_.push_back(injection.Pass()); |
rdevlin.cronin | d63148dc | 2014-12-16 19:22:34 | [diff] [blame] | 389 | break; |
kozyatinskiy | c8bc9a58 | 2015-03-06 09:33:41 | [diff] [blame] | 390 | case ScriptInjection::INJECTION_BLOCKED: |
vaibhav1.a | d733268 | 2015-05-27 05:45:43 | [diff] [blame] | 391 | running_injections_.push_back(injection.Pass()); |
kozyatinskiy | c8bc9a58 | 2015-03-06 09:33:41 | [diff] [blame] | 392 | break; |
| 393 | case ScriptInjection::INJECTION_FINISHED: |
| 394 | break; |
[email protected] | ac2f8937 | 2014-06-23 21:44:25 | [diff] [blame] | 395 | } |
[email protected] | ac2f8937 | 2014-06-23 21:44:25 | [diff] [blame] | 396 | } |
| 397 | |
| 398 | void ScriptInjectionManager::HandleExecuteCode( |
| 399 | const ExtensionMsg_ExecuteCode_Params& params, |
rdevlin.cronin | 4bb32d7 | 2015-06-02 21:55:01 | [diff] [blame] | 400 | content::RenderFrame* render_frame) { |
hanxi | 79f7a57 | 2015-03-09 20:46:59 | [diff] [blame] | 401 | scoped_ptr<const InjectionHost> injection_host; |
| 402 | if (params.host_id.type() == HostID::EXTENSIONS) { |
annekao | 6572d5c | 2015-08-19 16:13:36 | [diff] [blame^] | 403 | injection_host = ExtensionInjectionHost::Create(params.host_id.id()); |
hanxi | 79f7a57 | 2015-03-09 20:46:59 | [diff] [blame] | 404 | if (!injection_host) |
| 405 | return; |
| 406 | } else if (params.host_id.type() == HostID::WEBUI) { |
| 407 | injection_host.reset( |
| 408 | new WebUIInjectionHost(params.host_id)); |
| 409 | } |
hanxi | 9b84166 | 2015-03-04 14:36:41 | [diff] [blame] | 410 | |
[email protected] | c11e659 | 2014-06-27 17:07:34 | [diff] [blame] | 411 | scoped_ptr<ScriptInjection> injection(new ScriptInjection( |
| 412 | scoped_ptr<ScriptInjector>( |
rdevlin.cronin | 3e11c986 | 2015-06-04 19:54:25 | [diff] [blame] | 413 | new ProgrammaticScriptInjector(params, render_frame)), |
| 414 | render_frame, |
hanxi | 79f7a57 | 2015-03-09 20:46:59 | [diff] [blame] | 415 | injection_host.Pass(), |
[email protected] | c11e659 | 2014-06-27 17:07:34 | [diff] [blame] | 416 | static_cast<UserScript::RunLocation>(params.run_at), |
rdevlin.cronin | b67a3477 | 2015-06-04 16:32:38 | [diff] [blame] | 417 | ExtensionFrameHelper::Get(render_frame)->tab_id())); |
[email protected] | ac2f8937 | 2014-06-23 21:44:25 | [diff] [blame] | 418 | |
rdevlin.cronin | 4bb32d7 | 2015-06-02 21:55:01 | [diff] [blame] | 419 | FrameStatusMap::const_iterator iter = frame_statuses_.find(render_frame); |
rdevlin.cronin | 3ae4a3201 | 2015-06-30 17:43:19 | [diff] [blame] | 420 | UserScript::RunLocation run_location = |
| 421 | iter == frame_statuses_.end() ? UserScript::UNDEFINED : iter->second; |
hanxi | a5c856cf | 2015-02-13 20:51:58 | [diff] [blame] | 422 | |
rdevlin.cronin | 3ae4a3201 | 2015-06-30 17:43:19 | [diff] [blame] | 423 | ScriptsRunInfo scripts_run_info(render_frame, run_location); |
| 424 | TryToInject(injection.Pass(), run_location, &scripts_run_info); |
[email protected] | ac2f8937 | 2014-06-23 21:44:25 | [diff] [blame] | 425 | } |
| 426 | |
markdittmer | 9ea140f | 2014-08-29 02:46:15 | [diff] [blame] | 427 | void ScriptInjectionManager::HandleExecuteDeclarativeScript( |
rdevlin.cronin | 4bb32d7 | 2015-06-02 21:55:01 | [diff] [blame] | 428 | content::RenderFrame* render_frame, |
markdittmer | 9ea140f | 2014-08-29 02:46:15 | [diff] [blame] | 429 | int tab_id, |
| 430 | const ExtensionId& extension_id, |
| 431 | int script_id, |
| 432 | const GURL& url) { |
markdittmer | 9ea140f | 2014-08-29 02:46:15 | [diff] [blame] | 433 | scoped_ptr<ScriptInjection> injection = |
| 434 | user_script_set_manager_->GetInjectionForDeclarativeScript( |
| 435 | script_id, |
rdevlin.cronin | 3e11c986 | 2015-06-04 19:54:25 | [diff] [blame] | 436 | render_frame, |
markdittmer | 9ea140f | 2014-08-29 02:46:15 | [diff] [blame] | 437 | tab_id, |
| 438 | url, |
hanxi | 9b84166 | 2015-03-04 14:36:41 | [diff] [blame] | 439 | extension_id); |
kozyatinskiy | c8bc9a58 | 2015-03-06 09:33:41 | [diff] [blame] | 440 | if (injection.get()) { |
rdevlin.cronin | 3ae4a3201 | 2015-06-30 17:43:19 | [diff] [blame] | 441 | ScriptsRunInfo scripts_run_info(render_frame, UserScript::BROWSER_DRIVEN); |
markdittmer | 9ea140f | 2014-08-29 02:46:15 | [diff] [blame] | 442 | // TODO(markdittmer): Use return value of TryToInject for error handling. |
kozyatinskiy | c8bc9a58 | 2015-03-06 09:33:41 | [diff] [blame] | 443 | TryToInject(injection.Pass(), |
| 444 | UserScript::BROWSER_DRIVEN, |
| 445 | &scripts_run_info); |
| 446 | |
rdevlin.cronin | 3ae4a3201 | 2015-06-30 17:43:19 | [diff] [blame] | 447 | scripts_run_info.LogRun(); |
markdittmer | 9ea140f | 2014-08-29 02:46:15 | [diff] [blame] | 448 | } |
| 449 | } |
| 450 | |
[email protected] | d205600 | 2014-07-03 06:18:06 | [diff] [blame] | 451 | void ScriptInjectionManager::HandlePermitScriptInjection(int64 request_id) { |
[email protected] | ac2f8937 | 2014-06-23 21:44:25 | [diff] [blame] | 452 | ScopedVector<ScriptInjection>::iterator iter = |
| 453 | pending_injections_.begin(); |
| 454 | for (; iter != pending_injections_.end(); ++iter) { |
hanxi | 9b84166 | 2015-03-04 14:36:41 | [diff] [blame] | 455 | if ((*iter)->request_id() == request_id) { |
| 456 | DCHECK((*iter)->host_id().type() == HostID::EXTENSIONS); |
[email protected] | ac2f8937 | 2014-06-23 21:44:25 | [diff] [blame] | 457 | break; |
hanxi | 9b84166 | 2015-03-04 14:36:41 | [diff] [blame] | 458 | } |
[email protected] | ac2f8937 | 2014-06-23 21:44:25 | [diff] [blame] | 459 | } |
| 460 | if (iter == pending_injections_.end()) |
| 461 | return; |
| 462 | |
[email protected] | d205600 | 2014-07-03 06:18:06 | [diff] [blame] | 463 | // At this point, because the request is present in pending_injections_, we |
| 464 | // know that this is the same page that issued the request (otherwise, |
rdevlin.cronin | 4bb32d7 | 2015-06-02 21:55:01 | [diff] [blame] | 465 | // RFOHelper's DidStartProvisionalLoad callback would have caused it to be |
[email protected] | d205600 | 2014-07-03 06:18:06 | [diff] [blame] | 466 | // cleared out). |
| 467 | |
[email protected] | ac2f8937 | 2014-06-23 21:44:25 | [diff] [blame] | 468 | scoped_ptr<ScriptInjection> injection(*iter); |
| 469 | pending_injections_.weak_erase(iter); |
| 470 | |
rdevlin.cronin | 3ae4a3201 | 2015-06-30 17:43:19 | [diff] [blame] | 471 | ScriptsRunInfo scripts_run_info(injection->render_frame(), |
| 472 | UserScript::RUN_DEFERRED); |
kozyatinskiy | c8bc9a58 | 2015-03-06 09:33:41 | [diff] [blame] | 473 | ScriptInjection::InjectionResult res = injection->OnPermissionGranted( |
| 474 | &scripts_run_info); |
| 475 | if (res == ScriptInjection::INJECTION_BLOCKED) |
| 476 | running_injections_.push_back(injection.Pass()); |
rdevlin.cronin | 3ae4a3201 | 2015-06-30 17:43:19 | [diff] [blame] | 477 | scripts_run_info.LogRun(); |
[email protected] | ac2f8937 | 2014-06-23 21:44:25 | [diff] [blame] | 478 | } |
| 479 | |
| 480 | } // namespace extensions |