[email protected] | 3c22078 | 2014-05-20 01:59:46 | [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.h" |
| 6 | |
[email protected] | ac2f8937 | 2014-06-23 21:44:25 | [diff] [blame] | 7 | #include <map> |
dcheng | e59eca160 | 2015-12-18 17:48:00 | [diff] [blame] | 8 | #include <utility> |
[email protected] | 3c22078 | 2014-05-20 01:59:46 | [diff] [blame] | 9 | |
Sebastien Marchand | 6d0558fd | 2019-01-25 16:49:37 | [diff] [blame] | 10 | #include "base/bind.h" |
Devlin Cronin | 4cd6bc1 | 2017-12-05 23:46:49 | [diff] [blame] | 11 | #include "base/feature_list.h" |
[email protected] | 3c22078 | 2014-05-20 01:59:46 | [diff] [blame] | 12 | #include "base/lazy_instance.h" |
avi | 2d124c0 | 2015-12-23 06:36:42 | [diff] [blame] | 13 | #include "base/macros.h" |
asvitkine | f5d4ee56 | 2016-11-07 18:57:08 | [diff] [blame] | 14 | #include "base/metrics/histogram_macros.h" |
[email protected] | c11e659 | 2014-06-27 17:07:34 | [diff] [blame] | 15 | #include "base/timer/elapsed_timer.h" |
| 16 | #include "base/values.h" |
rdevlin.cronin | 3e11c986 | 2015-06-04 19:54:25 | [diff] [blame] | 17 | #include "content/public/renderer/render_frame.h" |
John Abd-El-Malek | 312a30bb | 2017-10-23 19:51:52 | [diff] [blame] | 18 | #include "content/public/renderer/v8_value_converter.h" |
Devlin Cronin | 4cd6bc1 | 2017-12-05 23:46:49 | [diff] [blame] | 19 | #include "extensions/common/extension_features.h" |
[email protected] | f8abc6e4 | 2014-06-24 21:14:43 | [diff] [blame] | 20 | #include "extensions/common/extension_messages.h" |
hanxi | a5c856cf | 2015-02-13 20:51:58 | [diff] [blame] | 21 | #include "extensions/common/host_id.h" |
[email protected] | c11e659 | 2014-06-27 17:07:34 | [diff] [blame] | 22 | #include "extensions/renderer/dom_activity_logger.h" |
rdevlin.cronin | c318b93d | 2015-09-14 20:22:29 | [diff] [blame] | 23 | #include "extensions/renderer/extension_frame_helper.h" |
[email protected] | ac2f8937 | 2014-06-23 21:44:25 | [diff] [blame] | 24 | #include "extensions/renderer/extensions_renderer_client.h" |
kozyatinskiy | c8bc9a58 | 2015-03-06 09:33:41 | [diff] [blame] | 25 | #include "extensions/renderer/script_injection_callback.h" |
kozyatinskiy | c8bc9a58 | 2015-03-06 09:33:41 | [diff] [blame] | 26 | #include "extensions/renderer/scripts_run_info.h" |
Karan Bhatia | dc28bfbd | 2019-01-15 05:36:11 | [diff] [blame] | 27 | #include "third_party/blink/public/platform/web_isolated_world_info.h" |
Blink Reformat | a30d423 | 2018-04-07 15:31:06 | [diff] [blame] | 28 | #include "third_party/blink/public/platform/web_security_origin.h" |
| 29 | #include "third_party/blink/public/platform/web_string.h" |
| 30 | #include "third_party/blink/public/web/web_document.h" |
| 31 | #include "third_party/blink/public/web/web_local_frame.h" |
| 32 | #include "third_party/blink/public/web/web_script_source.h" |
[email protected] | c11e659 | 2014-06-27 17:07:34 | [diff] [blame] | 33 | #include "url/gurl.h" |
[email protected] | 3c22078 | 2014-05-20 01:59:46 | [diff] [blame] | 34 | |
| 35 | namespace extensions { |
| 36 | |
| 37 | namespace { |
| 38 | |
hanxi | a5c856cf | 2015-02-13 20:51:58 | [diff] [blame] | 39 | using IsolatedWorldMap = std::map<std::string, int>; |
scottmg | 5e65e3a | 2017-03-08 08:48:46 | [diff] [blame] | 40 | base::LazyInstance<IsolatedWorldMap>::DestructorAtExit g_isolated_worlds = |
[email protected] | 3c22078 | 2014-05-20 01:59:46 | [diff] [blame] | 41 | LAZY_INSTANCE_INITIALIZER; |
| 42 | |
avi | 2d124c0 | 2015-12-23 06:36:42 | [diff] [blame] | 43 | const int64_t kInvalidRequestId = -1; |
[email protected] | f8abc6e4 | 2014-06-24 21:14:43 | [diff] [blame] | 44 | |
| 45 | // The id of the next pending injection. |
avi | 2d124c0 | 2015-12-23 06:36:42 | [diff] [blame] | 46 | int64_t g_next_pending_id = 0; |
[email protected] | f8abc6e4 | 2014-06-24 21:14:43 | [diff] [blame] | 47 | |
hanxi | a5c856cf | 2015-02-13 20:51:58 | [diff] [blame] | 48 | // Gets the isolated world ID to use for the given |injection_host| |
| 49 | // in the given |frame|. If no isolated world has been created for that |
| 50 | // |injection_host| one will be created and initialized. |
| 51 | int GetIsolatedWorldIdForInstance(const InjectionHost* injection_host, |
| 52 | blink::WebLocalFrame* frame) { |
[email protected] | ac2f8937 | 2014-06-23 21:44:25 | [diff] [blame] | 53 | static int g_next_isolated_world_id = |
| 54 | ExtensionsRendererClient::Get()->GetLowestIsolatedWorldId(); |
[email protected] | 0d8d697 | 2014-06-03 22:41:02 | [diff] [blame] | 55 | |
[email protected] | ac2f8937 | 2014-06-23 21:44:25 | [diff] [blame] | 56 | IsolatedWorldMap& isolated_worlds = g_isolated_worlds.Get(); |
[email protected] | 0d8d697 | 2014-06-03 22:41:02 | [diff] [blame] | 57 | |
[email protected] | ac2f8937 | 2014-06-23 21:44:25 | [diff] [blame] | 58 | int id = 0; |
hanxi | a5c856cf | 2015-02-13 20:51:58 | [diff] [blame] | 59 | const std::string& key = injection_host->id().id(); |
jdoerrie | a1e1598b | 2018-10-10 09:10:37 | [diff] [blame] | 60 | auto iter = isolated_worlds.find(key); |
[email protected] | ac2f8937 | 2014-06-23 21:44:25 | [diff] [blame] | 61 | if (iter != isolated_worlds.end()) { |
| 62 | id = iter->second; |
| 63 | } else { |
| 64 | id = g_next_isolated_world_id++; |
| 65 | // This map will tend to pile up over time, but realistically, you're never |
hanxi | a5c856cf | 2015-02-13 20:51:58 | [diff] [blame] | 66 | // going to have enough injection hosts for it to matter. |
| 67 | isolated_worlds[key] = id; |
[email protected] | 0d8d697 | 2014-06-03 22:41:02 | [diff] [blame] | 68 | } |
| 69 | |
[email protected] | ac2f8937 | 2014-06-23 21:44:25 | [diff] [blame] | 70 | // We need to set the isolated world origin and CSP even if it's not a new |
| 71 | // world since these are stored per frame, and we might not have used this |
| 72 | // isolated world in this frame before. |
Karan Bhatia | dc28bfbd | 2019-01-15 05:36:11 | [diff] [blame] | 73 | blink::WebIsolatedWorldInfo info; |
| 74 | info.security_origin = |
| 75 | blink::WebSecurityOrigin::Create(injection_host->url()); |
| 76 | info.human_readable_name = blink::WebString::FromUTF8(injection_host->name()); |
| 77 | |
| 78 | const std::string* csp = injection_host->GetContentSecurityPolicy(); |
| 79 | if (csp) |
| 80 | info.content_security_policy = blink::WebString::FromUTF8(*csp); |
| 81 | |
| 82 | frame->SetIsolatedWorldInfo(id, info); |
[email protected] | ac2f8937 | 2014-06-23 21:44:25 | [diff] [blame] | 83 | |
| 84 | return id; |
[email protected] | 0d8d697 | 2014-06-03 22:41:02 | [diff] [blame] | 85 | } |
| 86 | |
ksakamoto | cbf167aa | 2017-03-17 06:45:48 | [diff] [blame] | 87 | // This class manages its own lifetime. |
| 88 | class TimedScriptInjectionCallback : public ScriptInjectionCallback { |
| 89 | public: |
Kunihiko Sakamoto | 46b7bc5 | 2019-06-20 02:42:25 | [diff] [blame] | 90 | TimedScriptInjectionCallback(base::WeakPtr<ScriptInjection> injection) |
ksakamoto | cbf167aa | 2017-03-17 06:45:48 | [diff] [blame] | 91 | : ScriptInjectionCallback( |
| 92 | base::Bind(&TimedScriptInjectionCallback::OnCompleted, |
| 93 | base::Unretained(this))), |
Kunihiko Sakamoto | 46b7bc5 | 2019-06-20 02:42:25 | [diff] [blame] | 94 | injection_(injection) {} |
ksakamoto | cbf167aa | 2017-03-17 06:45:48 | [diff] [blame] | 95 | ~TimedScriptInjectionCallback() override {} |
| 96 | |
| 97 | void OnCompleted(const std::vector<v8::Local<v8::Value>>& result) { |
| 98 | if (injection_) { |
Kunihiko Sakamoto | 7d386bf | 2017-07-31 03:19:53 | [diff] [blame] | 99 | base::TimeTicks timestamp(base::TimeTicks::Now()); |
ksakamoto | 4178f50 | 2017-04-07 04:40:21 | [diff] [blame] | 100 | base::Optional<base::TimeDelta> elapsed; |
| 101 | // If the script will never execute (such as if the context is destroyed), |
| 102 | // willExecute() will not be called, but OnCompleted() will. Only log a |
| 103 | // time for execution if the script, in fact, executed. |
Kunihiko Sakamoto | 46b7bc5 | 2019-06-20 02:42:25 | [diff] [blame] | 104 | if (!start_time_.is_null()) |
Kunihiko Sakamoto | 7d386bf | 2017-07-31 03:19:53 | [diff] [blame] | 105 | elapsed = timestamp - start_time_; |
ksakamoto | cbf167aa | 2017-03-17 06:45:48 | [diff] [blame] | 106 | injection_->OnJsInjectionCompleted(result, elapsed); |
| 107 | } |
| 108 | } |
| 109 | |
Kunihiko Sakamoto | 7d386bf | 2017-07-31 03:19:53 | [diff] [blame] | 110 | void WillExecute() override { |
| 111 | start_time_ = base::TimeTicks::Now(); |
Kunihiko Sakamoto | 7d386bf | 2017-07-31 03:19:53 | [diff] [blame] | 112 | } |
ksakamoto | cbf167aa | 2017-03-17 06:45:48 | [diff] [blame] | 113 | |
| 114 | private: |
| 115 | base::WeakPtr<ScriptInjection> injection_; |
| 116 | base::TimeTicks start_time_; |
| 117 | }; |
| 118 | |
[email protected] | c11e659 | 2014-06-27 17:07:34 | [diff] [blame] | 119 | } // namespace |
| 120 | |
rdevlin.cronin | d533be96 | 2015-10-02 17:01:18 | [diff] [blame] | 121 | // Watches for the deletion of a RenderFrame, after which is_valid will return |
| 122 | // false. |
| 123 | class ScriptInjection::FrameWatcher : public content::RenderFrameObserver { |
| 124 | public: |
| 125 | FrameWatcher(content::RenderFrame* render_frame, |
| 126 | ScriptInjection* injection) |
| 127 | : content::RenderFrameObserver(render_frame), |
| 128 | injection_(injection) {} |
| 129 | ~FrameWatcher() override {} |
| 130 | |
| 131 | private: |
| 132 | void FrameDetached() override { injection_->invalidate_render_frame(); } |
| 133 | void OnDestruct() override { injection_->invalidate_render_frame(); } |
| 134 | |
| 135 | ScriptInjection* injection_; |
| 136 | |
| 137 | DISALLOW_COPY_AND_ASSIGN(FrameWatcher); |
| 138 | }; |
| 139 | |
[email protected] | ac2f8937 | 2014-06-23 21:44:25 | [diff] [blame] | 140 | // static |
hanxi | a5c856cf | 2015-02-13 20:51:58 | [diff] [blame] | 141 | std::string ScriptInjection::GetHostIdForIsolatedWorld(int isolated_world_id) { |
| 142 | const IsolatedWorldMap& isolated_worlds = g_isolated_worlds.Get(); |
[email protected] | 0d8d697 | 2014-06-03 22:41:02 | [diff] [blame] | 143 | |
hanxi | a5c856cf | 2015-02-13 20:51:58 | [diff] [blame] | 144 | for (const auto& iter : isolated_worlds) { |
| 145 | if (iter.second == isolated_world_id) |
| 146 | return iter.first; |
[email protected] | 3c22078 | 2014-05-20 01:59:46 | [diff] [blame] | 147 | } |
[email protected] | ac2f8937 | 2014-06-23 21:44:25 | [diff] [blame] | 148 | return std::string(); |
[email protected] | 3c22078 | 2014-05-20 01:59:46 | [diff] [blame] | 149 | } |
| 150 | |
[email protected] | ac2f8937 | 2014-06-23 21:44:25 | [diff] [blame] | 151 | // static |
hanxi | a5c856cf | 2015-02-13 20:51:58 | [diff] [blame] | 152 | void ScriptInjection::RemoveIsolatedWorld(const std::string& host_id) { |
| 153 | g_isolated_worlds.Get().erase(host_id); |
[email protected] | 3c22078 | 2014-05-20 01:59:46 | [diff] [blame] | 154 | } |
| 155 | |
dcheng | f6f8066 | 2016-04-20 20:26:04 | [diff] [blame] | 156 | ScriptInjection::ScriptInjection( |
| 157 | std::unique_ptr<ScriptInjector> injector, |
| 158 | content::RenderFrame* render_frame, |
| 159 | std::unique_ptr<const InjectionHost> injection_host, |
rdevlin.cronin | 6fba7ec | 2016-06-24 16:15:05 | [diff] [blame] | 160 | UserScript::RunLocation run_location, |
| 161 | bool log_activity) |
dcheng | e59eca160 | 2015-12-18 17:48:00 | [diff] [blame] | 162 | : injector_(std::move(injector)), |
rdevlin.cronin | 3e11c986 | 2015-06-04 19:54:25 | [diff] [blame] | 163 | render_frame_(render_frame), |
dcheng | e59eca160 | 2015-12-18 17:48:00 | [diff] [blame] | 164 | injection_host_(std::move(injection_host)), |
[email protected] | f8abc6e4 | 2014-06-24 21:14:43 | [diff] [blame] | 165 | run_location_(run_location), |
[email protected] | d205600 | 2014-07-03 06:18:06 | [diff] [blame] | 166 | request_id_(kInvalidRequestId), |
kozyatinskiy | c8bc9a58 | 2015-03-06 09:33:41 | [diff] [blame] | 167 | complete_(false), |
rdevlin.cronin | 3e11c986 | 2015-06-04 19:54:25 | [diff] [blame] | 168 | did_inject_js_(false), |
rdevlin.cronin | 6fba7ec | 2016-06-24 16:15:05 | [diff] [blame] | 169 | log_activity_(log_activity), |
Jeremy Roman | 9fc2de6 | 2019-07-12 14:15:03 | [diff] [blame] | 170 | frame_watcher_(new FrameWatcher(render_frame, this)) { |
hanxi | 9b84166 | 2015-03-04 14:36:41 | [diff] [blame] | 171 | CHECK(injection_host_.get()); |
[email protected] | f8abc6e4 | 2014-06-24 21:14:43 | [diff] [blame] | 172 | } |
| 173 | |
| 174 | ScriptInjection::~ScriptInjection() { |
| 175 | if (!complete_) |
rdevlin.cronin | d533be96 | 2015-10-02 17:01:18 | [diff] [blame] | 176 | NotifyWillNotInject(ScriptInjector::WONT_INJECT); |
[email protected] | f8abc6e4 | 2014-06-24 21:14:43 | [diff] [blame] | 177 | } |
| 178 | |
kozyatinskiy | c8bc9a58 | 2015-03-06 09:33:41 | [diff] [blame] | 179 | ScriptInjection::InjectionResult ScriptInjection::TryToInject( |
| 180 | UserScript::RunLocation current_location, |
| 181 | ScriptsRunInfo* scripts_run_info, |
rdevlin.cronin | 4bb32d7 | 2015-06-02 21:55:01 | [diff] [blame] | 182 | const CompletionCallback& async_completion_callback) { |
[email protected] | f8abc6e4 | 2014-06-24 21:14:43 | [diff] [blame] | 183 | if (current_location < run_location_) |
kozyatinskiy | c8bc9a58 | 2015-03-06 09:33:41 | [diff] [blame] | 184 | return INJECTION_WAITING; // Wait for the right location. |
[email protected] | f8abc6e4 | 2014-06-24 21:14:43 | [diff] [blame] | 185 | |
kozyatinskiy | c8bc9a58 | 2015-03-06 09:33:41 | [diff] [blame] | 186 | if (request_id_ != kInvalidRequestId) { |
| 187 | // We're waiting for permission right now, try again later. |
| 188 | return INJECTION_WAITING; |
| 189 | } |
[email protected] | f8abc6e4 | 2014-06-24 21:14:43 | [diff] [blame] | 190 | |
hanxi | 9b84166 | 2015-03-04 14:36:41 | [diff] [blame] | 191 | if (!injection_host_) { |
[email protected] | c11e659 | 2014-06-27 17:07:34 | [diff] [blame] | 192 | NotifyWillNotInject(ScriptInjector::EXTENSION_REMOVED); |
kozyatinskiy | c8bc9a58 | 2015-03-06 09:33:41 | [diff] [blame] | 193 | return INJECTION_FINISHED; // We're done. |
[email protected] | f8abc6e4 | 2014-06-24 21:14:43 | [diff] [blame] | 194 | } |
| 195 | |
rdevlin.cronin | 3e11c986 | 2015-06-04 19:54:25 | [diff] [blame] | 196 | blink::WebLocalFrame* web_frame = render_frame_->GetWebFrame(); |
hanxi | 9b84166 | 2015-03-04 14:36:41 | [diff] [blame] | 197 | switch (injector_->CanExecuteOnFrame( |
rdevlin.cronin | c318b93d | 2015-09-14 20:22:29 | [diff] [blame] | 198 | injection_host_.get(), web_frame, |
| 199 | ExtensionFrameHelper::Get(render_frame_)->tab_id())) { |
Devlin Cronin | 3e532b8 | 2018-05-03 21:27:19 | [diff] [blame] | 200 | case PermissionsData::PageAccess::kDenied: |
[email protected] | c11e659 | 2014-06-27 17:07:34 | [diff] [blame] | 201 | NotifyWillNotInject(ScriptInjector::NOT_ALLOWED); |
kozyatinskiy | c8bc9a58 | 2015-03-06 09:33:41 | [diff] [blame] | 202 | return INJECTION_FINISHED; // We're done. |
Devlin Cronin | 3e532b8 | 2018-05-03 21:27:19 | [diff] [blame] | 203 | case PermissionsData::PageAccess::kWithheld: |
rdevlin.cronin | b03d157 | 2015-10-02 02:23:52 | [diff] [blame] | 204 | RequestPermissionFromBrowser(); |
kozyatinskiy | c8bc9a58 | 2015-03-06 09:33:41 | [diff] [blame] | 205 | return INJECTION_WAITING; // Wait around for permission. |
Devlin Cronin | 3e532b8 | 2018-05-03 21:27:19 | [diff] [blame] | 206 | case PermissionsData::PageAccess::kAllowed: |
Kunihiko Sakamoto | 46b7bc5 | 2019-06-20 02:42:25 | [diff] [blame] | 207 | InjectionResult result = Inject(scripts_run_info); |
kozyatinskiy | c8bc9a58 | 2015-03-06 09:33:41 | [diff] [blame] | 208 | // If the injection is blocked, we need to set the manager so we can |
| 209 | // notify it upon completion. |
| 210 | if (result == INJECTION_BLOCKED) |
rdevlin.cronin | 4bb32d7 | 2015-06-02 21:55:01 | [diff] [blame] | 211 | async_completion_callback_ = async_completion_callback; |
kozyatinskiy | c8bc9a58 | 2015-03-06 09:33:41 | [diff] [blame] | 212 | return result; |
[email protected] | f8abc6e4 | 2014-06-24 21:14:43 | [diff] [blame] | 213 | } |
| 214 | |
rdevlin.cronin | 958e9f8 | 2015-02-17 21:57:14 | [diff] [blame] | 215 | NOTREACHED(); |
kozyatinskiy | c8bc9a58 | 2015-03-06 09:33:41 | [diff] [blame] | 216 | return INJECTION_FINISHED; |
[email protected] | f8abc6e4 | 2014-06-24 21:14:43 | [diff] [blame] | 217 | } |
| 218 | |
kozyatinskiy | c8bc9a58 | 2015-03-06 09:33:41 | [diff] [blame] | 219 | ScriptInjection::InjectionResult ScriptInjection::OnPermissionGranted( |
| 220 | ScriptsRunInfo* scripts_run_info) { |
hanxi | 9b84166 | 2015-03-04 14:36:41 | [diff] [blame] | 221 | if (!injection_host_) { |
[email protected] | c11e659 | 2014-06-27 17:07:34 | [diff] [blame] | 222 | NotifyWillNotInject(ScriptInjector::EXTENSION_REMOVED); |
kozyatinskiy | c8bc9a58 | 2015-03-06 09:33:41 | [diff] [blame] | 223 | return INJECTION_FINISHED; |
[email protected] | f8abc6e4 | 2014-06-24 21:14:43 | [diff] [blame] | 224 | } |
| 225 | |
Kunihiko Sakamoto | 46b7bc5 | 2019-06-20 02:42:25 | [diff] [blame] | 226 | return Inject(scripts_run_info); |
[email protected] | f8abc6e4 | 2014-06-24 21:14:43 | [diff] [blame] | 227 | } |
| 228 | |
hanxi | 9b84166 | 2015-03-04 14:36:41 | [diff] [blame] | 229 | void ScriptInjection::OnHostRemoved() { |
| 230 | injection_host_.reset(nullptr); |
| 231 | } |
| 232 | |
rdevlin.cronin | b03d157 | 2015-10-02 02:23:52 | [diff] [blame] | 233 | void ScriptInjection::RequestPermissionFromBrowser() { |
[email protected] | 23a8536 | 2014-07-07 23:26:19 | [diff] [blame] | 234 | // If we are just notifying the browser of the injection, then send an |
[email protected] | f8abc6e4 | 2014-06-24 21:14:43 | [diff] [blame] | 235 | // invalid request (which is treated like a notification). |
rdevlin.cronin | b03d157 | 2015-10-02 02:23:52 | [diff] [blame] | 236 | request_id_ = g_next_pending_id++; |
rdevlin.cronin | 45dca7f | 2015-06-08 19:47:03 | [diff] [blame] | 237 | render_frame_->Send(new ExtensionHostMsg_RequestScriptInjectionPermission( |
rdevlin.cronin | 8d034e5 | 2016-02-02 22:46:32 | [diff] [blame] | 238 | render_frame_->GetRoutingID(), host_id().id(), injector_->script_type(), |
| 239 | run_location_, request_id_)); |
[email protected] | f8abc6e4 | 2014-06-24 21:14:43 | [diff] [blame] | 240 | } |
| 241 | |
[email protected] | c11e659 | 2014-06-27 17:07:34 | [diff] [blame] | 242 | void ScriptInjection::NotifyWillNotInject( |
| 243 | ScriptInjector::InjectFailureReason reason) { |
[email protected] | f8abc6e4 | 2014-06-24 21:14:43 | [diff] [blame] | 244 | complete_ = true; |
rdevlin.cronin | d533be96 | 2015-10-02 17:01:18 | [diff] [blame] | 245 | injector_->OnWillNotInject(reason, render_frame_); |
[email protected] | f8abc6e4 | 2014-06-24 21:14:43 | [diff] [blame] | 246 | } |
| 247 | |
kozyatinskiy | c8bc9a58 | 2015-03-06 09:33:41 | [diff] [blame] | 248 | ScriptInjection::InjectionResult ScriptInjection::Inject( |
Kunihiko Sakamoto | 46b7bc5 | 2019-06-20 02:42:25 | [diff] [blame] | 249 | ScriptsRunInfo* scripts_run_info) { |
hanxi | 9b84166 | 2015-03-04 14:36:41 | [diff] [blame] | 250 | DCHECK(injection_host_); |
[email protected] | c11e659 | 2014-06-27 17:07:34 | [diff] [blame] | 251 | DCHECK(scripts_run_info); |
| 252 | DCHECK(!complete_); |
catmullings | d4faad4f | 2016-09-08 19:55:30 | [diff] [blame] | 253 | bool should_inject_js = injector_->ShouldInjectJs( |
| 254 | run_location_, scripts_run_info->executing_scripts[host_id().id()]); |
| 255 | bool should_inject_css = injector_->ShouldInjectCss( |
| 256 | run_location_, scripts_run_info->injected_stylesheets[host_id().id()]); |
[email protected] | c11e659 | 2014-06-27 17:07:34 | [diff] [blame] | 257 | |
catmullings | d4faad4f | 2016-09-08 19:55:30 | [diff] [blame] | 258 | // This can happen if the extension specified a script to |
| 259 | // be run in multiple rules, and the script has already run. |
| 260 | // See crbug.com/631247. |
| 261 | if (!should_inject_js && !should_inject_css) { |
| 262 | return INJECTION_FINISHED; |
| 263 | } |
[email protected] | c11e659 | 2014-06-27 17:07:34 | [diff] [blame] | 264 | |
rdevlin.cronin | 4bb32d7 | 2015-06-02 21:55:01 | [diff] [blame] | 265 | if (should_inject_js) |
catmullings | d4faad4f | 2016-09-08 19:55:30 | [diff] [blame] | 266 | InjectJs(&(scripts_run_info->executing_scripts[host_id().id()]), |
Kunihiko Sakamoto | 46b7bc5 | 2019-06-20 02:42:25 | [diff] [blame] | 267 | &(scripts_run_info->num_js)); |
rdevlin.cronin | 4bb32d7 | 2015-06-02 21:55:01 | [diff] [blame] | 268 | if (should_inject_css) |
catmullings | d4faad4f | 2016-09-08 19:55:30 | [diff] [blame] | 269 | InjectCss(&(scripts_run_info->injected_stylesheets[host_id().id()]), |
| 270 | &(scripts_run_info->num_css)); |
[email protected] | c11e659 | 2014-06-27 17:07:34 | [diff] [blame] | 271 | |
rdevlin.cronin | 4bb32d7 | 2015-06-02 21:55:01 | [diff] [blame] | 272 | complete_ = did_inject_js_ || !should_inject_js; |
[email protected] | c11e659 | 2014-06-27 17:07:34 | [diff] [blame] | 273 | |
rdevlin.cronin | d533be96 | 2015-10-02 17:01:18 | [diff] [blame] | 274 | if (complete_) { |
dcheng | e59eca160 | 2015-12-18 17:48:00 | [diff] [blame] | 275 | injector_->OnInjectionComplete(std::move(execution_result_), run_location_, |
rdevlin.cronin | d533be96 | 2015-10-02 17:01:18 | [diff] [blame] | 276 | render_frame_); |
| 277 | } else { |
rdevlin.cronin | 4bb32d7 | 2015-06-02 21:55:01 | [diff] [blame] | 278 | ++scripts_run_info->num_blocking_js; |
rdevlin.cronin | d533be96 | 2015-10-02 17:01:18 | [diff] [blame] | 279 | } |
rdevlin.cronin | 4bb32d7 | 2015-06-02 21:55:01 | [diff] [blame] | 280 | |
kozyatinskiy | c8bc9a58 | 2015-03-06 09:33:41 | [diff] [blame] | 281 | return complete_ ? INJECTION_FINISHED : INJECTION_BLOCKED; |
[email protected] | c11e659 | 2014-06-27 17:07:34 | [diff] [blame] | 282 | } |
| 283 | |
Kunihiko Sakamoto | 46b7bc5 | 2019-06-20 02:42:25 | [diff] [blame] | 284 | void ScriptInjection::InjectJs(std::set<std::string>* executing_scripts, |
| 285 | size_t* num_injected_js_scripts) { |
rdevlin.cronin | 4bb32d7 | 2015-06-02 21:55:01 | [diff] [blame] | 286 | DCHECK(!did_inject_js_); |
rdevlin.cronin | 3e11c986 | 2015-06-04 19:54:25 | [diff] [blame] | 287 | blink::WebLocalFrame* web_frame = render_frame_->GetWebFrame(); |
catmullings | d4faad4f | 2016-09-08 19:55:30 | [diff] [blame] | 288 | std::vector<blink::WebScriptSource> sources = injector_->GetJsSources( |
| 289 | run_location_, executing_scripts, num_injected_js_scripts); |
| 290 | DCHECK(!sources.empty()); |
Devlin Cronin | e684a21 | 2019-10-05 15:26:36 | [diff] [blame] | 291 | int world_id = |
| 292 | GetIsolatedWorldIdForInstance(injection_host_.get(), web_frame); |
kozyatinskiy | c8bc9a58 | 2015-03-06 09:33:41 | [diff] [blame] | 293 | bool is_user_gesture = injector_->IsUserGesture(); |
| 294 | |
dcheng | f6f8066 | 2016-04-20 20:26:04 | [diff] [blame] | 295 | std::unique_ptr<blink::WebScriptExecutionCallback> callback( |
Kunihiko Sakamoto | 46b7bc5 | 2019-06-20 02:42:25 | [diff] [blame] | 296 | new TimedScriptInjectionCallback(weak_ptr_factory_.GetWeakPtr())); |
[email protected] | c11e659 | 2014-06-27 17:07:34 | [diff] [blame] | 297 | |
| 298 | base::ElapsedTimer exec_timer; |
rdevlin.cronin | 6fba7ec | 2016-06-24 16:15:05 | [diff] [blame] | 299 | if (injection_host_->id().type() == HostID::EXTENSIONS && log_activity_) |
hanxi | 9b84166 | 2015-03-04 14:36:41 | [diff] [blame] | 300 | DOMActivityLogger::AttachToWorld(world_id, injection_host_->id().id()); |
[email protected] | c11e659 | 2014-06-27 17:07:34 | [diff] [blame] | 301 | |
Devlin Cronin | 0e60ff7 | 2019-11-26 01:13:07 | [diff] [blame] | 302 | // For content scripts executing during page load, we run them asynchronously |
| 303 | // in order to reduce UI jank experienced by the user. (We don't do this for |
| 304 | // DOCUMENT_START scripts, because there's no UI to jank until after those |
| 305 | // run, so we run them as soon as we can.) |
| 306 | // Note: We could potentially also run deferred and browser-driven scripts |
| 307 | // asynchronously; however, these are rare enough that there probably isn't |
| 308 | // UI jank. If this changes, we can update this. |
| 309 | bool should_execute_asynchronously = |
| 310 | injector_->script_type() == UserScript::CONTENT_SCRIPT && |
| 311 | (run_location_ == UserScript::DOCUMENT_END || |
| 312 | run_location_ == UserScript::DOCUMENT_IDLE); |
| 313 | blink::WebLocalFrame::ScriptExecutionType execution_option = |
| 314 | should_execute_asynchronously |
| 315 | ? blink::WebLocalFrame::kAsynchronousBlockingOnload |
| 316 | : blink::WebLocalFrame::kSynchronous; |
Devlin Cronin | e684a21 | 2019-10-05 15:26:36 | [diff] [blame] | 317 | web_frame->RequestExecuteScriptInIsolatedWorld( |
Devlin Cronin | 0e60ff7 | 2019-11-26 01:13:07 | [diff] [blame] | 318 | world_id, &sources.front(), sources.size(), is_user_gesture, |
| 319 | execution_option, callback.release()); |
kozyatinskiy | c8bc9a58 | 2015-03-06 09:33:41 | [diff] [blame] | 320 | } |
[email protected] | c11e659 | 2014-06-27 17:07:34 | [diff] [blame] | 321 | |
kozyatinskiy | c8bc9a58 | 2015-03-06 09:33:41 | [diff] [blame] | 322 | void ScriptInjection::OnJsInjectionCompleted( |
ksakamoto | cbf167aa | 2017-03-17 06:45:48 | [diff] [blame] | 323 | const std::vector<v8::Local<v8::Value>>& results, |
ksakamoto | 4178f50 | 2017-04-07 04:40:21 | [diff] [blame] | 324 | base::Optional<base::TimeDelta> elapsed) { |
rdevlin.cronin | 4bb32d7 | 2015-06-02 21:55:01 | [diff] [blame] | 325 | DCHECK(!did_inject_js_); |
kozyatinskiy | c8bc9a58 | 2015-03-06 09:33:41 | [diff] [blame] | 326 | |
ksakamoto | 4178f50 | 2017-04-07 04:40:21 | [diff] [blame] | 327 | if (injection_host_->id().type() == HostID::EXTENSIONS && elapsed) { |
| 328 | UMA_HISTOGRAM_TIMES("Extensions.InjectedScriptExecutionTime", *elapsed); |
ksakamoto | 049f833 | 2017-03-24 06:39:07 | [diff] [blame] | 329 | switch (run_location_) { |
| 330 | case UserScript::DOCUMENT_START: |
| 331 | UMA_HISTOGRAM_TIMES( |
ksakamoto | 4178f50 | 2017-04-07 04:40:21 | [diff] [blame] | 332 | "Extensions.InjectedScriptExecutionTime.DocumentStart", *elapsed); |
ksakamoto | 049f833 | 2017-03-24 06:39:07 | [diff] [blame] | 333 | break; |
| 334 | case UserScript::DOCUMENT_END: |
| 335 | UMA_HISTOGRAM_TIMES( |
ksakamoto | 4178f50 | 2017-04-07 04:40:21 | [diff] [blame] | 336 | "Extensions.InjectedScriptExecutionTime.DocumentEnd", *elapsed); |
ksakamoto | 049f833 | 2017-03-24 06:39:07 | [diff] [blame] | 337 | break; |
| 338 | case UserScript::DOCUMENT_IDLE: |
| 339 | UMA_HISTOGRAM_TIMES( |
ksakamoto | 4178f50 | 2017-04-07 04:40:21 | [diff] [blame] | 340 | "Extensions.InjectedScriptExecutionTime.DocumentIdle", *elapsed); |
ksakamoto | 049f833 | 2017-03-24 06:39:07 | [diff] [blame] | 341 | break; |
| 342 | default: |
| 343 | break; |
| 344 | } |
| 345 | } |
ksakamoto | cbf167aa | 2017-03-17 06:45:48 | [diff] [blame] | 346 | |
kozyatinskiy | c8bc9a58 | 2015-03-06 09:33:41 | [diff] [blame] | 347 | bool expects_results = injector_->ExpectsResults(); |
[email protected] | c11e659 | 2014-06-27 17:07:34 | [diff] [blame] | 348 | if (expects_results) { |
rdevlin.cronin | 0d94256f | 2016-12-09 15:34:23 | [diff] [blame] | 349 | if (!results.empty() && !results[0].IsEmpty()) { |
kozyatinskiy | efaa59a | 2015-04-03 17:57:16 | [diff] [blame] | 350 | // Right now, we only support returning single results (per frame). |
kozyatinskiy | efaa59a | 2015-04-03 17:57:16 | [diff] [blame] | 351 | // It's safe to always use the main world context when converting |
| 352 | // here. V8ValueConverterImpl shouldn't actually care about the |
| 353 | // context scope, and it switches to v8::Object's creation context |
| 354 | // when encountered. |
rdevlin.cronin | 3e11c986 | 2015-06-04 19:54:25 | [diff] [blame] | 355 | v8::Local<v8::Context> context = |
Blink Reformat | 1c4d759e | 2017-04-09 16:34:54 | [diff] [blame] | 356 | render_frame_->GetWebFrame()->MainWorldScriptContext(); |
rdevlin.cronin | 694c605 | 2017-06-13 22:07:35 | [diff] [blame] | 357 | execution_result_ = |
| 358 | content::V8ValueConverter::Create()->FromV8Value(results[0], context); |
kozyatinskiy | efaa59a | 2015-04-03 17:57:16 | [diff] [blame] | 359 | } |
rdevlin.cronin | 4bb32d7 | 2015-06-02 21:55:01 | [diff] [blame] | 360 | if (!execution_result_.get()) |
Jeremy Roman | 16529d0e | 2017-08-24 18:13:47 | [diff] [blame] | 361 | execution_result_ = std::make_unique<base::Value>(); |
kozyatinskiy | c8bc9a58 | 2015-03-06 09:33:41 | [diff] [blame] | 362 | } |
rdevlin.cronin | 4bb32d7 | 2015-06-02 21:55:01 | [diff] [blame] | 363 | did_inject_js_ = true; |
kozyatinskiy | c8bc9a58 | 2015-03-06 09:33:41 | [diff] [blame] | 364 | |
rdevlin.cronin | 4bb32d7 | 2015-06-02 21:55:01 | [diff] [blame] | 365 | // If |async_completion_callback_| is set, it means the script finished |
| 366 | // asynchronously, and we should run it. |
| 367 | if (!async_completion_callback_.is_null()) { |
ksakamoto | cbf167aa | 2017-03-17 06:45:48 | [diff] [blame] | 368 | complete_ = true; |
dcheng | e59eca160 | 2015-12-18 17:48:00 | [diff] [blame] | 369 | injector_->OnInjectionComplete(std::move(execution_result_), run_location_, |
rdevlin.cronin | d533be96 | 2015-10-02 17:01:18 | [diff] [blame] | 370 | render_frame_); |
rdevlin.cronin | 4bb32d7 | 2015-06-02 21:55:01 | [diff] [blame] | 371 | // Warning: this object can be destroyed after this line! |
| 372 | async_completion_callback_.Run(this); |
[email protected] | c11e659 | 2014-06-27 17:07:34 | [diff] [blame] | 373 | } |
| 374 | } |
| 375 | |
catmullings | d4faad4f | 2016-09-08 19:55:30 | [diff] [blame] | 376 | void ScriptInjection::InjectCss(std::set<std::string>* injected_stylesheets, |
| 377 | size_t* num_injected_stylesheets) { |
| 378 | std::vector<blink::WebString> css_sources = injector_->GetCssSources( |
| 379 | run_location_, injected_stylesheets, num_injected_stylesheets); |
rdevlin.cronin | 3e11c986 | 2015-06-04 19:54:25 | [diff] [blame] | 380 | blink::WebLocalFrame* web_frame = render_frame_->GetWebFrame(); |
Manish Jethani | 9494d72 | 2018-01-20 00:28:47 | [diff] [blame] | 381 | // Default CSS origin is "author", but can be overridden to "user" by scripts. |
| 382 | base::Optional<CSSOrigin> css_origin = injector_->GetCssOrigin(); |
| 383 | blink::WebDocument::CSSOrigin blink_css_origin = |
| 384 | css_origin && *css_origin == CSS_ORIGIN_USER |
| 385 | ? blink::WebDocument::kUserOrigin |
| 386 | : blink::WebDocument::kAuthorOrigin; |
Manish Jethani | ff6ff85 | 2018-02-23 07:24:55 | [diff] [blame] | 387 | blink::WebStyleSheetKey style_sheet_key; |
| 388 | if (const base::Optional<std::string>& injection_key = |
| 389 | injector_->GetInjectionKey()) |
| 390 | style_sheet_key = blink::WebString::FromASCII(*injection_key); |
lazyboy | 49cc0b3a | 2016-08-18 21:55:12 | [diff] [blame] | 391 | for (const blink::WebString& css : css_sources) |
Manish Jethani | ff6ff85 | 2018-02-23 07:24:55 | [diff] [blame] | 392 | web_frame->GetDocument().InsertStyleSheet(css, &style_sheet_key, |
| 393 | blink_css_origin); |
[email protected] | f8abc6e4 | 2014-06-24 21:14:43 | [diff] [blame] | 394 | } |
| 395 | |
[email protected] | 3c22078 | 2014-05-20 01:59:46 | [diff] [blame] | 396 | } // namespace extensions |