[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 | |
Devlin Cronin | 4cd6bc1 | 2017-12-05 23:46:49 | [diff] [blame] | 10 | #include "base/feature_list.h" |
[email protected] | 3c22078 | 2014-05-20 01:59:46 | [diff] [blame] | 11 | #include "base/lazy_instance.h" |
avi | 2d124c0 | 2015-12-23 06:36:42 | [diff] [blame] | 12 | #include "base/macros.h" |
jdoerrie | e067999a | 2017-04-07 06:39:00 | [diff] [blame] | 13 | #include "base/memory/ptr_util.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" |
Kunihiko Sakamoto | 7d386bf | 2017-07-31 03:19:53 | [diff] [blame] | 22 | #include "extensions/renderer/async_scripts_run_info.h" |
[email protected] | c11e659 | 2014-06-27 17:07:34 | [diff] [blame] | 23 | #include "extensions/renderer/dom_activity_logger.h" |
rdevlin.cronin | c318b93d | 2015-09-14 20:22:29 | [diff] [blame] | 24 | #include "extensions/renderer/extension_frame_helper.h" |
[email protected] | ac2f8937 | 2014-06-23 21:44:25 | [diff] [blame] | 25 | #include "extensions/renderer/extensions_renderer_client.h" |
kozyatinskiy | c8bc9a58 | 2015-03-06 09:33:41 | [diff] [blame] | 26 | #include "extensions/renderer/script_injection_callback.h" |
kozyatinskiy | c8bc9a58 | 2015-03-06 09:33:41 | [diff] [blame] | 27 | #include "extensions/renderer/scripts_run_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(); |
| 60 | IsolatedWorldMap::iterator 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. |
Blink Reformat | 1c4d759e | 2017-04-09 16:34:54 | [diff] [blame] | 73 | frame->SetIsolatedWorldSecurityOrigin( |
| 74 | id, blink::WebSecurityOrigin::Create(injection_host->url())); |
| 75 | frame->SetIsolatedWorldContentSecurityPolicy( |
| 76 | id, |
| 77 | blink::WebString::FromUTF8(injection_host->GetContentSecurityPolicy())); |
| 78 | frame->SetIsolatedWorldHumanReadableName( |
| 79 | id, blink::WebString::FromUTF8(injection_host->name())); |
[email protected] | ac2f8937 | 2014-06-23 21:44:25 | [diff] [blame] | 80 | |
| 81 | return id; |
[email protected] | 0d8d697 | 2014-06-03 22:41:02 | [diff] [blame] | 82 | } |
| 83 | |
ksakamoto | cbf167aa | 2017-03-17 06:45:48 | [diff] [blame] | 84 | // This class manages its own lifetime. |
| 85 | class TimedScriptInjectionCallback : public ScriptInjectionCallback { |
| 86 | public: |
Kunihiko Sakamoto | 7d386bf | 2017-07-31 03:19:53 | [diff] [blame] | 87 | TimedScriptInjectionCallback( |
| 88 | base::WeakPtr<ScriptInjection> injection, |
| 89 | scoped_refptr<AsyncScriptsRunInfo> async_run_info) |
ksakamoto | cbf167aa | 2017-03-17 06:45:48 | [diff] [blame] | 90 | : ScriptInjectionCallback( |
| 91 | base::Bind(&TimedScriptInjectionCallback::OnCompleted, |
| 92 | base::Unretained(this))), |
Kunihiko Sakamoto | 7d386bf | 2017-07-31 03:19:53 | [diff] [blame] | 93 | injection_(injection), |
| 94 | async_run_info_(async_run_info) {} |
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 | 7d386bf | 2017-07-31 03:19:53 | [diff] [blame] | 104 | if (!start_time_.is_null()) { |
| 105 | elapsed = timestamp - start_time_; |
| 106 | if (async_run_info_) |
Kunihiko Sakamoto | cf8f3b58 | 2017-08-29 02:40:21 | [diff] [blame] | 107 | async_run_info_->OnCompleted(timestamp, elapsed); |
Kunihiko Sakamoto | 7d386bf | 2017-07-31 03:19:53 | [diff] [blame] | 108 | } |
ksakamoto | cbf167aa | 2017-03-17 06:45:48 | [diff] [blame] | 109 | injection_->OnJsInjectionCompleted(result, elapsed); |
| 110 | } |
| 111 | } |
| 112 | |
Kunihiko Sakamoto | 7d386bf | 2017-07-31 03:19:53 | [diff] [blame] | 113 | void WillExecute() override { |
| 114 | start_time_ = base::TimeTicks::Now(); |
| 115 | if (async_run_info_) |
| 116 | async_run_info_->WillExecute(start_time_); |
| 117 | } |
ksakamoto | cbf167aa | 2017-03-17 06:45:48 | [diff] [blame] | 118 | |
| 119 | private: |
| 120 | base::WeakPtr<ScriptInjection> injection_; |
| 121 | base::TimeTicks start_time_; |
Kunihiko Sakamoto | 7d386bf | 2017-07-31 03:19:53 | [diff] [blame] | 122 | scoped_refptr<AsyncScriptsRunInfo> async_run_info_; |
ksakamoto | cbf167aa | 2017-03-17 06:45:48 | [diff] [blame] | 123 | }; |
| 124 | |
[email protected] | c11e659 | 2014-06-27 17:07:34 | [diff] [blame] | 125 | } // namespace |
| 126 | |
rdevlin.cronin | d533be96 | 2015-10-02 17:01:18 | [diff] [blame] | 127 | // Watches for the deletion of a RenderFrame, after which is_valid will return |
| 128 | // false. |
| 129 | class ScriptInjection::FrameWatcher : public content::RenderFrameObserver { |
| 130 | public: |
| 131 | FrameWatcher(content::RenderFrame* render_frame, |
| 132 | ScriptInjection* injection) |
| 133 | : content::RenderFrameObserver(render_frame), |
| 134 | injection_(injection) {} |
| 135 | ~FrameWatcher() override {} |
| 136 | |
| 137 | private: |
| 138 | void FrameDetached() override { injection_->invalidate_render_frame(); } |
| 139 | void OnDestruct() override { injection_->invalidate_render_frame(); } |
| 140 | |
| 141 | ScriptInjection* injection_; |
| 142 | |
| 143 | DISALLOW_COPY_AND_ASSIGN(FrameWatcher); |
| 144 | }; |
| 145 | |
[email protected] | ac2f8937 | 2014-06-23 21:44:25 | [diff] [blame] | 146 | // static |
hanxi | a5c856cf | 2015-02-13 20:51:58 | [diff] [blame] | 147 | std::string ScriptInjection::GetHostIdForIsolatedWorld(int isolated_world_id) { |
| 148 | const IsolatedWorldMap& isolated_worlds = g_isolated_worlds.Get(); |
[email protected] | 0d8d697 | 2014-06-03 22:41:02 | [diff] [blame] | 149 | |
hanxi | a5c856cf | 2015-02-13 20:51:58 | [diff] [blame] | 150 | for (const auto& iter : isolated_worlds) { |
| 151 | if (iter.second == isolated_world_id) |
| 152 | return iter.first; |
[email protected] | 3c22078 | 2014-05-20 01:59:46 | [diff] [blame] | 153 | } |
[email protected] | ac2f8937 | 2014-06-23 21:44:25 | [diff] [blame] | 154 | return std::string(); |
[email protected] | 3c22078 | 2014-05-20 01:59:46 | [diff] [blame] | 155 | } |
| 156 | |
[email protected] | ac2f8937 | 2014-06-23 21:44:25 | [diff] [blame] | 157 | // static |
hanxi | a5c856cf | 2015-02-13 20:51:58 | [diff] [blame] | 158 | void ScriptInjection::RemoveIsolatedWorld(const std::string& host_id) { |
| 159 | g_isolated_worlds.Get().erase(host_id); |
[email protected] | 3c22078 | 2014-05-20 01:59:46 | [diff] [blame] | 160 | } |
| 161 | |
dcheng | f6f8066 | 2016-04-20 20:26:04 | [diff] [blame] | 162 | ScriptInjection::ScriptInjection( |
| 163 | std::unique_ptr<ScriptInjector> injector, |
| 164 | content::RenderFrame* render_frame, |
| 165 | std::unique_ptr<const InjectionHost> injection_host, |
rdevlin.cronin | 6fba7ec | 2016-06-24 16:15:05 | [diff] [blame] | 166 | UserScript::RunLocation run_location, |
| 167 | bool log_activity) |
dcheng | e59eca160 | 2015-12-18 17:48:00 | [diff] [blame] | 168 | : injector_(std::move(injector)), |
rdevlin.cronin | 3e11c986 | 2015-06-04 19:54:25 | [diff] [blame] | 169 | render_frame_(render_frame), |
dcheng | e59eca160 | 2015-12-18 17:48:00 | [diff] [blame] | 170 | injection_host_(std::move(injection_host)), |
[email protected] | f8abc6e4 | 2014-06-24 21:14:43 | [diff] [blame] | 171 | run_location_(run_location), |
[email protected] | d205600 | 2014-07-03 06:18:06 | [diff] [blame] | 172 | request_id_(kInvalidRequestId), |
kozyatinskiy | c8bc9a58 | 2015-03-06 09:33:41 | [diff] [blame] | 173 | complete_(false), |
rdevlin.cronin | 3e11c986 | 2015-06-04 19:54:25 | [diff] [blame] | 174 | did_inject_js_(false), |
rdevlin.cronin | 6fba7ec | 2016-06-24 16:15:05 | [diff] [blame] | 175 | log_activity_(log_activity), |
rdevlin.cronin | d533be96 | 2015-10-02 17:01:18 | [diff] [blame] | 176 | frame_watcher_(new FrameWatcher(render_frame, this)), |
rdevlin.cronin | 3e11c986 | 2015-06-04 19:54:25 | [diff] [blame] | 177 | weak_ptr_factory_(this) { |
hanxi | 9b84166 | 2015-03-04 14:36:41 | [diff] [blame] | 178 | CHECK(injection_host_.get()); |
[email protected] | f8abc6e4 | 2014-06-24 21:14:43 | [diff] [blame] | 179 | } |
| 180 | |
| 181 | ScriptInjection::~ScriptInjection() { |
| 182 | if (!complete_) |
rdevlin.cronin | d533be96 | 2015-10-02 17:01:18 | [diff] [blame] | 183 | NotifyWillNotInject(ScriptInjector::WONT_INJECT); |
[email protected] | f8abc6e4 | 2014-06-24 21:14:43 | [diff] [blame] | 184 | } |
| 185 | |
kozyatinskiy | c8bc9a58 | 2015-03-06 09:33:41 | [diff] [blame] | 186 | ScriptInjection::InjectionResult ScriptInjection::TryToInject( |
| 187 | UserScript::RunLocation current_location, |
| 188 | ScriptsRunInfo* scripts_run_info, |
Kunihiko Sakamoto | 7d386bf | 2017-07-31 03:19:53 | [diff] [blame] | 189 | scoped_refptr<AsyncScriptsRunInfo> async_run_info, |
rdevlin.cronin | 4bb32d7 | 2015-06-02 21:55:01 | [diff] [blame] | 190 | const CompletionCallback& async_completion_callback) { |
[email protected] | f8abc6e4 | 2014-06-24 21:14:43 | [diff] [blame] | 191 | if (current_location < run_location_) |
kozyatinskiy | c8bc9a58 | 2015-03-06 09:33:41 | [diff] [blame] | 192 | return INJECTION_WAITING; // Wait for the right location. |
[email protected] | f8abc6e4 | 2014-06-24 21:14:43 | [diff] [blame] | 193 | |
kozyatinskiy | c8bc9a58 | 2015-03-06 09:33:41 | [diff] [blame] | 194 | if (request_id_ != kInvalidRequestId) { |
| 195 | // We're waiting for permission right now, try again later. |
| 196 | return INJECTION_WAITING; |
| 197 | } |
[email protected] | f8abc6e4 | 2014-06-24 21:14:43 | [diff] [blame] | 198 | |
hanxi | 9b84166 | 2015-03-04 14:36:41 | [diff] [blame] | 199 | if (!injection_host_) { |
[email protected] | c11e659 | 2014-06-27 17:07:34 | [diff] [blame] | 200 | NotifyWillNotInject(ScriptInjector::EXTENSION_REMOVED); |
kozyatinskiy | c8bc9a58 | 2015-03-06 09:33:41 | [diff] [blame] | 201 | return INJECTION_FINISHED; // We're done. |
[email protected] | f8abc6e4 | 2014-06-24 21:14:43 | [diff] [blame] | 202 | } |
| 203 | |
rdevlin.cronin | 3e11c986 | 2015-06-04 19:54:25 | [diff] [blame] | 204 | blink::WebLocalFrame* web_frame = render_frame_->GetWebFrame(); |
hanxi | 9b84166 | 2015-03-04 14:36:41 | [diff] [blame] | 205 | switch (injector_->CanExecuteOnFrame( |
rdevlin.cronin | c318b93d | 2015-09-14 20:22:29 | [diff] [blame] | 206 | injection_host_.get(), web_frame, |
| 207 | ExtensionFrameHelper::Get(render_frame_)->tab_id())) { |
[email protected] | 23a8536 | 2014-07-07 23:26:19 | [diff] [blame] | 208 | case PermissionsData::ACCESS_DENIED: |
[email protected] | c11e659 | 2014-06-27 17:07:34 | [diff] [blame] | 209 | NotifyWillNotInject(ScriptInjector::NOT_ALLOWED); |
kozyatinskiy | c8bc9a58 | 2015-03-06 09:33:41 | [diff] [blame] | 210 | return INJECTION_FINISHED; // We're done. |
[email protected] | 23a8536 | 2014-07-07 23:26:19 | [diff] [blame] | 211 | case PermissionsData::ACCESS_WITHHELD: |
rdevlin.cronin | b03d157 | 2015-10-02 02:23:52 | [diff] [blame] | 212 | RequestPermissionFromBrowser(); |
kozyatinskiy | c8bc9a58 | 2015-03-06 09:33:41 | [diff] [blame] | 213 | return INJECTION_WAITING; // Wait around for permission. |
[email protected] | 23a8536 | 2014-07-07 23:26:19 | [diff] [blame] | 214 | case PermissionsData::ACCESS_ALLOWED: |
Kunihiko Sakamoto | 7d386bf | 2017-07-31 03:19:53 | [diff] [blame] | 215 | InjectionResult result = |
| 216 | Inject(scripts_run_info, std::move(async_run_info)); |
kozyatinskiy | c8bc9a58 | 2015-03-06 09:33:41 | [diff] [blame] | 217 | // If the injection is blocked, we need to set the manager so we can |
| 218 | // notify it upon completion. |
| 219 | if (result == INJECTION_BLOCKED) |
rdevlin.cronin | 4bb32d7 | 2015-06-02 21:55:01 | [diff] [blame] | 220 | async_completion_callback_ = async_completion_callback; |
kozyatinskiy | c8bc9a58 | 2015-03-06 09:33:41 | [diff] [blame] | 221 | return result; |
[email protected] | f8abc6e4 | 2014-06-24 21:14:43 | [diff] [blame] | 222 | } |
| 223 | |
rdevlin.cronin | 958e9f8 | 2015-02-17 21:57:14 | [diff] [blame] | 224 | NOTREACHED(); |
kozyatinskiy | c8bc9a58 | 2015-03-06 09:33:41 | [diff] [blame] | 225 | return INJECTION_FINISHED; |
[email protected] | f8abc6e4 | 2014-06-24 21:14:43 | [diff] [blame] | 226 | } |
| 227 | |
kozyatinskiy | c8bc9a58 | 2015-03-06 09:33:41 | [diff] [blame] | 228 | ScriptInjection::InjectionResult ScriptInjection::OnPermissionGranted( |
| 229 | ScriptsRunInfo* scripts_run_info) { |
hanxi | 9b84166 | 2015-03-04 14:36:41 | [diff] [blame] | 230 | if (!injection_host_) { |
[email protected] | c11e659 | 2014-06-27 17:07:34 | [diff] [blame] | 231 | NotifyWillNotInject(ScriptInjector::EXTENSION_REMOVED); |
kozyatinskiy | c8bc9a58 | 2015-03-06 09:33:41 | [diff] [blame] | 232 | return INJECTION_FINISHED; |
[email protected] | f8abc6e4 | 2014-06-24 21:14:43 | [diff] [blame] | 233 | } |
| 234 | |
Kunihiko Sakamoto | 7d386bf | 2017-07-31 03:19:53 | [diff] [blame] | 235 | return Inject(scripts_run_info, nullptr); |
[email protected] | f8abc6e4 | 2014-06-24 21:14:43 | [diff] [blame] | 236 | } |
| 237 | |
hanxi | 9b84166 | 2015-03-04 14:36:41 | [diff] [blame] | 238 | void ScriptInjection::OnHostRemoved() { |
| 239 | injection_host_.reset(nullptr); |
| 240 | } |
| 241 | |
rdevlin.cronin | b03d157 | 2015-10-02 02:23:52 | [diff] [blame] | 242 | void ScriptInjection::RequestPermissionFromBrowser() { |
[email protected] | 23a8536 | 2014-07-07 23:26:19 | [diff] [blame] | 243 | // If we are just notifying the browser of the injection, then send an |
[email protected] | f8abc6e4 | 2014-06-24 21:14:43 | [diff] [blame] | 244 | // invalid request (which is treated like a notification). |
rdevlin.cronin | b03d157 | 2015-10-02 02:23:52 | [diff] [blame] | 245 | request_id_ = g_next_pending_id++; |
rdevlin.cronin | 45dca7f | 2015-06-08 19:47:03 | [diff] [blame] | 246 | render_frame_->Send(new ExtensionHostMsg_RequestScriptInjectionPermission( |
rdevlin.cronin | 8d034e5 | 2016-02-02 22:46:32 | [diff] [blame] | 247 | render_frame_->GetRoutingID(), host_id().id(), injector_->script_type(), |
| 248 | run_location_, request_id_)); |
[email protected] | f8abc6e4 | 2014-06-24 21:14:43 | [diff] [blame] | 249 | } |
| 250 | |
[email protected] | c11e659 | 2014-06-27 17:07:34 | [diff] [blame] | 251 | void ScriptInjection::NotifyWillNotInject( |
| 252 | ScriptInjector::InjectFailureReason reason) { |
[email protected] | f8abc6e4 | 2014-06-24 21:14:43 | [diff] [blame] | 253 | complete_ = true; |
rdevlin.cronin | d533be96 | 2015-10-02 17:01:18 | [diff] [blame] | 254 | injector_->OnWillNotInject(reason, render_frame_); |
[email protected] | f8abc6e4 | 2014-06-24 21:14:43 | [diff] [blame] | 255 | } |
| 256 | |
kozyatinskiy | c8bc9a58 | 2015-03-06 09:33:41 | [diff] [blame] | 257 | ScriptInjection::InjectionResult ScriptInjection::Inject( |
Kunihiko Sakamoto | 7d386bf | 2017-07-31 03:19:53 | [diff] [blame] | 258 | ScriptsRunInfo* scripts_run_info, |
| 259 | scoped_refptr<AsyncScriptsRunInfo> async_run_info) { |
hanxi | 9b84166 | 2015-03-04 14:36:41 | [diff] [blame] | 260 | DCHECK(injection_host_); |
[email protected] | c11e659 | 2014-06-27 17:07:34 | [diff] [blame] | 261 | DCHECK(scripts_run_info); |
| 262 | DCHECK(!complete_); |
catmullings | d4faad4f | 2016-09-08 19:55:30 | [diff] [blame] | 263 | bool should_inject_js = injector_->ShouldInjectJs( |
| 264 | run_location_, scripts_run_info->executing_scripts[host_id().id()]); |
| 265 | bool should_inject_css = injector_->ShouldInjectCss( |
| 266 | run_location_, scripts_run_info->injected_stylesheets[host_id().id()]); |
[email protected] | c11e659 | 2014-06-27 17:07:34 | [diff] [blame] | 267 | |
catmullings | d4faad4f | 2016-09-08 19:55:30 | [diff] [blame] | 268 | // This can happen if the extension specified a script to |
| 269 | // be run in multiple rules, and the script has already run. |
| 270 | // See crbug.com/631247. |
| 271 | if (!should_inject_js && !should_inject_css) { |
| 272 | return INJECTION_FINISHED; |
| 273 | } |
[email protected] | c11e659 | 2014-06-27 17:07:34 | [diff] [blame] | 274 | |
rdevlin.cronin | 4bb32d7 | 2015-06-02 21:55:01 | [diff] [blame] | 275 | if (should_inject_js) |
catmullings | d4faad4f | 2016-09-08 19:55:30 | [diff] [blame] | 276 | InjectJs(&(scripts_run_info->executing_scripts[host_id().id()]), |
Kunihiko Sakamoto | 7d386bf | 2017-07-31 03:19:53 | [diff] [blame] | 277 | &(scripts_run_info->num_js), std::move(async_run_info)); |
rdevlin.cronin | 4bb32d7 | 2015-06-02 21:55:01 | [diff] [blame] | 278 | if (should_inject_css) |
catmullings | d4faad4f | 2016-09-08 19:55:30 | [diff] [blame] | 279 | InjectCss(&(scripts_run_info->injected_stylesheets[host_id().id()]), |
| 280 | &(scripts_run_info->num_css)); |
[email protected] | c11e659 | 2014-06-27 17:07:34 | [diff] [blame] | 281 | |
rdevlin.cronin | 4bb32d7 | 2015-06-02 21:55:01 | [diff] [blame] | 282 | complete_ = did_inject_js_ || !should_inject_js; |
[email protected] | c11e659 | 2014-06-27 17:07:34 | [diff] [blame] | 283 | |
rdevlin.cronin | d533be96 | 2015-10-02 17:01:18 | [diff] [blame] | 284 | if (complete_) { |
dcheng | e59eca160 | 2015-12-18 17:48:00 | [diff] [blame] | 285 | injector_->OnInjectionComplete(std::move(execution_result_), run_location_, |
rdevlin.cronin | d533be96 | 2015-10-02 17:01:18 | [diff] [blame] | 286 | render_frame_); |
| 287 | } else { |
rdevlin.cronin | 4bb32d7 | 2015-06-02 21:55:01 | [diff] [blame] | 288 | ++scripts_run_info->num_blocking_js; |
rdevlin.cronin | d533be96 | 2015-10-02 17:01:18 | [diff] [blame] | 289 | } |
rdevlin.cronin | 4bb32d7 | 2015-06-02 21:55:01 | [diff] [blame] | 290 | |
kozyatinskiy | c8bc9a58 | 2015-03-06 09:33:41 | [diff] [blame] | 291 | return complete_ ? INJECTION_FINISHED : INJECTION_BLOCKED; |
[email protected] | c11e659 | 2014-06-27 17:07:34 | [diff] [blame] | 292 | } |
| 293 | |
Kunihiko Sakamoto | 7d386bf | 2017-07-31 03:19:53 | [diff] [blame] | 294 | void ScriptInjection::InjectJs( |
| 295 | std::set<std::string>* executing_scripts, |
| 296 | size_t* num_injected_js_scripts, |
| 297 | scoped_refptr<AsyncScriptsRunInfo> async_run_info) { |
rdevlin.cronin | 4bb32d7 | 2015-06-02 21:55:01 | [diff] [blame] | 298 | DCHECK(!did_inject_js_); |
rdevlin.cronin | 3e11c986 | 2015-06-04 19:54:25 | [diff] [blame] | 299 | blink::WebLocalFrame* web_frame = render_frame_->GetWebFrame(); |
catmullings | d4faad4f | 2016-09-08 19:55:30 | [diff] [blame] | 300 | std::vector<blink::WebScriptSource> sources = injector_->GetJsSources( |
| 301 | run_location_, executing_scripts, num_injected_js_scripts); |
| 302 | DCHECK(!sources.empty()); |
[email protected] | c11e659 | 2014-06-27 17:07:34 | [diff] [blame] | 303 | bool in_main_world = injector_->ShouldExecuteInMainWorld(); |
| 304 | int world_id = in_main_world |
| 305 | ? DOMActivityLogger::kMainWorldId |
hanxi | 9b84166 | 2015-03-04 14:36:41 | [diff] [blame] | 306 | : GetIsolatedWorldIdForInstance(injection_host_.get(), |
rdevlin.cronin | 3e11c986 | 2015-06-04 19:54:25 | [diff] [blame] | 307 | web_frame); |
kozyatinskiy | c8bc9a58 | 2015-03-06 09:33:41 | [diff] [blame] | 308 | bool is_user_gesture = injector_->IsUserGesture(); |
| 309 | |
dcheng | f6f8066 | 2016-04-20 20:26:04 | [diff] [blame] | 310 | std::unique_ptr<blink::WebScriptExecutionCallback> callback( |
Kunihiko Sakamoto | 7d386bf | 2017-07-31 03:19:53 | [diff] [blame] | 311 | new TimedScriptInjectionCallback(weak_ptr_factory_.GetWeakPtr(), |
| 312 | std::move(async_run_info))); |
[email protected] | c11e659 | 2014-06-27 17:07:34 | [diff] [blame] | 313 | |
| 314 | base::ElapsedTimer exec_timer; |
rdevlin.cronin | 6fba7ec | 2016-06-24 16:15:05 | [diff] [blame] | 315 | if (injection_host_->id().type() == HostID::EXTENSIONS && log_activity_) |
hanxi | 9b84166 | 2015-03-04 14:36:41 | [diff] [blame] | 316 | DOMActivityLogger::AttachToWorld(world_id, injection_host_->id().id()); |
[email protected] | c11e659 | 2014-06-27 17:07:34 | [diff] [blame] | 317 | if (in_main_world) { |
| 318 | // We only inject in the main world for javascript: urls. |
| 319 | DCHECK_EQ(1u, sources.size()); |
| 320 | |
Blink Reformat | 1c4d759e | 2017-04-09 16:34:54 | [diff] [blame] | 321 | web_frame->RequestExecuteScriptAndReturnValue( |
| 322 | sources.front(), is_user_gesture, callback.release()); |
kozyatinskiy | c8bc9a58 | 2015-03-06 09:33:41 | [diff] [blame] | 323 | } else { |
ksakamoto | cbf167aa | 2017-03-17 06:45:48 | [diff] [blame] | 324 | blink::WebLocalFrame::ScriptExecutionType option; |
Kunihiko Sakamoto | cb2844c9 | 2018-03-07 04:31:51 | [diff] [blame] | 325 | if (injector_->script_type() == UserScript::CONTENT_SCRIPT) { |
ksakamoto | cbf167aa | 2017-03-17 06:45:48 | [diff] [blame] | 326 | switch (run_location_) { |
| 327 | case UserScript::DOCUMENT_END: |
| 328 | case UserScript::DOCUMENT_IDLE: |
Blink Reformat | 1c4d759e | 2017-04-09 16:34:54 | [diff] [blame] | 329 | option = blink::WebLocalFrame::kAsynchronousBlockingOnload; |
ksakamoto | cbf167aa | 2017-03-17 06:45:48 | [diff] [blame] | 330 | break; |
| 331 | default: |
Blink Reformat | 1c4d759e | 2017-04-09 16:34:54 | [diff] [blame] | 332 | option = blink::WebLocalFrame::kSynchronous; |
ksakamoto | cbf167aa | 2017-03-17 06:45:48 | [diff] [blame] | 333 | break; |
| 334 | } |
| 335 | } else { |
Blink Reformat | 1c4d759e | 2017-04-09 16:34:54 | [diff] [blame] | 336 | option = blink::WebLocalFrame::kSynchronous; |
ksakamoto | cbf167aa | 2017-03-17 06:45:48 | [diff] [blame] | 337 | } |
Blink Reformat | 1c4d759e | 2017-04-09 16:34:54 | [diff] [blame] | 338 | web_frame->RequestExecuteScriptInIsolatedWorld( |
ksakamoto | cbf167aa | 2017-03-17 06:45:48 | [diff] [blame] | 339 | world_id, &sources.front(), sources.size(), is_user_gesture, option, |
rdevlin.cronin | 3e11c986 | 2015-06-04 19:54:25 | [diff] [blame] | 340 | callback.release()); |
[email protected] | c11e659 | 2014-06-27 17:07:34 | [diff] [blame] | 341 | } |
kozyatinskiy | c8bc9a58 | 2015-03-06 09:33:41 | [diff] [blame] | 342 | } |
[email protected] | c11e659 | 2014-06-27 17:07:34 | [diff] [blame] | 343 | |
kozyatinskiy | c8bc9a58 | 2015-03-06 09:33:41 | [diff] [blame] | 344 | void ScriptInjection::OnJsInjectionCompleted( |
ksakamoto | cbf167aa | 2017-03-17 06:45:48 | [diff] [blame] | 345 | const std::vector<v8::Local<v8::Value>>& results, |
ksakamoto | 4178f50 | 2017-04-07 04:40:21 | [diff] [blame] | 346 | base::Optional<base::TimeDelta> elapsed) { |
rdevlin.cronin | 4bb32d7 | 2015-06-02 21:55:01 | [diff] [blame] | 347 | DCHECK(!did_inject_js_); |
kozyatinskiy | c8bc9a58 | 2015-03-06 09:33:41 | [diff] [blame] | 348 | |
ksakamoto | 4178f50 | 2017-04-07 04:40:21 | [diff] [blame] | 349 | if (injection_host_->id().type() == HostID::EXTENSIONS && elapsed) { |
| 350 | UMA_HISTOGRAM_TIMES("Extensions.InjectedScriptExecutionTime", *elapsed); |
ksakamoto | 049f833 | 2017-03-24 06:39:07 | [diff] [blame] | 351 | switch (run_location_) { |
| 352 | case UserScript::DOCUMENT_START: |
| 353 | UMA_HISTOGRAM_TIMES( |
ksakamoto | 4178f50 | 2017-04-07 04:40:21 | [diff] [blame] | 354 | "Extensions.InjectedScriptExecutionTime.DocumentStart", *elapsed); |
ksakamoto | 049f833 | 2017-03-24 06:39:07 | [diff] [blame] | 355 | break; |
| 356 | case UserScript::DOCUMENT_END: |
| 357 | UMA_HISTOGRAM_TIMES( |
ksakamoto | 4178f50 | 2017-04-07 04:40:21 | [diff] [blame] | 358 | "Extensions.InjectedScriptExecutionTime.DocumentEnd", *elapsed); |
ksakamoto | 049f833 | 2017-03-24 06:39:07 | [diff] [blame] | 359 | break; |
| 360 | case UserScript::DOCUMENT_IDLE: |
| 361 | UMA_HISTOGRAM_TIMES( |
ksakamoto | 4178f50 | 2017-04-07 04:40:21 | [diff] [blame] | 362 | "Extensions.InjectedScriptExecutionTime.DocumentIdle", *elapsed); |
ksakamoto | 049f833 | 2017-03-24 06:39:07 | [diff] [blame] | 363 | break; |
| 364 | default: |
| 365 | break; |
| 366 | } |
| 367 | } |
ksakamoto | cbf167aa | 2017-03-17 06:45:48 | [diff] [blame] | 368 | |
kozyatinskiy | c8bc9a58 | 2015-03-06 09:33:41 | [diff] [blame] | 369 | bool expects_results = injector_->ExpectsResults(); |
[email protected] | c11e659 | 2014-06-27 17:07:34 | [diff] [blame] | 370 | if (expects_results) { |
rdevlin.cronin | 0d94256f | 2016-12-09 15:34:23 | [diff] [blame] | 371 | if (!results.empty() && !results[0].IsEmpty()) { |
kozyatinskiy | efaa59a | 2015-04-03 17:57:16 | [diff] [blame] | 372 | // Right now, we only support returning single results (per frame). |
kozyatinskiy | efaa59a | 2015-04-03 17:57:16 | [diff] [blame] | 373 | // It's safe to always use the main world context when converting |
| 374 | // here. V8ValueConverterImpl shouldn't actually care about the |
| 375 | // context scope, and it switches to v8::Object's creation context |
| 376 | // when encountered. |
rdevlin.cronin | 3e11c986 | 2015-06-04 19:54:25 | [diff] [blame] | 377 | v8::Local<v8::Context> context = |
Blink Reformat | 1c4d759e | 2017-04-09 16:34:54 | [diff] [blame] | 378 | render_frame_->GetWebFrame()->MainWorldScriptContext(); |
rdevlin.cronin | 694c605 | 2017-06-13 22:07:35 | [diff] [blame] | 379 | execution_result_ = |
| 380 | content::V8ValueConverter::Create()->FromV8Value(results[0], context); |
kozyatinskiy | efaa59a | 2015-04-03 17:57:16 | [diff] [blame] | 381 | } |
rdevlin.cronin | 4bb32d7 | 2015-06-02 21:55:01 | [diff] [blame] | 382 | if (!execution_result_.get()) |
Jeremy Roman | 16529d0e | 2017-08-24 18:13:47 | [diff] [blame] | 383 | execution_result_ = std::make_unique<base::Value>(); |
kozyatinskiy | c8bc9a58 | 2015-03-06 09:33:41 | [diff] [blame] | 384 | } |
rdevlin.cronin | 4bb32d7 | 2015-06-02 21:55:01 | [diff] [blame] | 385 | did_inject_js_ = true; |
kozyatinskiy | c8bc9a58 | 2015-03-06 09:33:41 | [diff] [blame] | 386 | |
rdevlin.cronin | 4bb32d7 | 2015-06-02 21:55:01 | [diff] [blame] | 387 | // If |async_completion_callback_| is set, it means the script finished |
| 388 | // asynchronously, and we should run it. |
| 389 | if (!async_completion_callback_.is_null()) { |
ksakamoto | cbf167aa | 2017-03-17 06:45:48 | [diff] [blame] | 390 | complete_ = true; |
dcheng | e59eca160 | 2015-12-18 17:48:00 | [diff] [blame] | 391 | injector_->OnInjectionComplete(std::move(execution_result_), run_location_, |
rdevlin.cronin | d533be96 | 2015-10-02 17:01:18 | [diff] [blame] | 392 | render_frame_); |
rdevlin.cronin | 4bb32d7 | 2015-06-02 21:55:01 | [diff] [blame] | 393 | // Warning: this object can be destroyed after this line! |
| 394 | async_completion_callback_.Run(this); |
[email protected] | c11e659 | 2014-06-27 17:07:34 | [diff] [blame] | 395 | } |
| 396 | } |
| 397 | |
catmullings | d4faad4f | 2016-09-08 19:55:30 | [diff] [blame] | 398 | void ScriptInjection::InjectCss(std::set<std::string>* injected_stylesheets, |
| 399 | size_t* num_injected_stylesheets) { |
| 400 | std::vector<blink::WebString> css_sources = injector_->GetCssSources( |
| 401 | run_location_, injected_stylesheets, num_injected_stylesheets); |
rdevlin.cronin | 3e11c986 | 2015-06-04 19:54:25 | [diff] [blame] | 402 | blink::WebLocalFrame* web_frame = render_frame_->GetWebFrame(); |
Manish Jethani | 9494d72 | 2018-01-20 00:28:47 | [diff] [blame] | 403 | // Default CSS origin is "author", but can be overridden to "user" by scripts. |
| 404 | base::Optional<CSSOrigin> css_origin = injector_->GetCssOrigin(); |
| 405 | blink::WebDocument::CSSOrigin blink_css_origin = |
| 406 | css_origin && *css_origin == CSS_ORIGIN_USER |
| 407 | ? blink::WebDocument::kUserOrigin |
| 408 | : blink::WebDocument::kAuthorOrigin; |
Manish Jethani | ff6ff85 | 2018-02-23 07:24:55 | [diff] [blame] | 409 | blink::WebStyleSheetKey style_sheet_key; |
| 410 | if (const base::Optional<std::string>& injection_key = |
| 411 | injector_->GetInjectionKey()) |
| 412 | style_sheet_key = blink::WebString::FromASCII(*injection_key); |
lazyboy | 49cc0b3a | 2016-08-18 21:55:12 | [diff] [blame] | 413 | for (const blink::WebString& css : css_sources) |
Manish Jethani | ff6ff85 | 2018-02-23 07:24:55 | [diff] [blame] | 414 | web_frame->GetDocument().InsertStyleSheet(css, &style_sheet_key, |
| 415 | blink_css_origin); |
[email protected] | f8abc6e4 | 2014-06-24 21:14:43 | [diff] [blame] | 416 | } |
| 417 | |
[email protected] | 3c22078 | 2014-05-20 01:59:46 | [diff] [blame] | 418 | } // namespace extensions |