extension_frame_helper.cc 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622
  1. // Copyright 2013 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. #include "extensions/renderer/extension_frame_helper.h"
  5. #include <set>
  6. #include "base/feature_list.h"
  7. #include "base/metrics/histogram_macros.h"
  8. #include "base/ranges/algorithm.h"
  9. #include "base/strings/string_util.h"
  10. #include "base/timer/elapsed_timer.h"
  11. #include "content/public/renderer/render_frame.h"
  12. #include "extensions/common/api/messaging/message.h"
  13. #include "extensions/common/api/messaging/port_id.h"
  14. #include "extensions/common/constants.h"
  15. #include "extensions/common/extension_features.h"
  16. #include "extensions/common/extension_messages.h"
  17. #include "extensions/common/manifest_handlers/background_info.h"
  18. #include "extensions/renderer/api/automation/automation_api_helper.h"
  19. #include "extensions/renderer/console.h"
  20. #include "extensions/renderer/dispatcher.h"
  21. #include "extensions/renderer/native_extension_bindings_system.h"
  22. #include "extensions/renderer/native_renderer_messaging_service.h"
  23. #include "extensions/renderer/script_context.h"
  24. #include "extensions/renderer/script_context_set.h"
  25. #include "third_party/blink/public/common/associated_interfaces/associated_interface_provider.h"
  26. #include "third_party/blink/public/common/associated_interfaces/associated_interface_registry.h"
  27. #include "third_party/blink/public/platform/web_isolated_world_info.h"
  28. #include "third_party/blink/public/platform/web_security_origin.h"
  29. #include "third_party/blink/public/web/web_console_message.h"
  30. #include "third_party/blink/public/web/web_document.h"
  31. #include "third_party/blink/public/web/web_document_loader.h"
  32. #include "third_party/blink/public/web/web_local_frame.h"
  33. #include "third_party/blink/public/web/web_settings.h"
  34. #include "third_party/blink/public/web/web_view.h"
  35. #include "v8/include/v8-container.h"
  36. #include "v8/include/v8-context.h"
  37. #include "v8/include/v8-isolate.h"
  38. #include "v8/include/v8-object.h"
  39. #include "v8/include/v8-primitive.h"
  40. namespace extensions {
  41. namespace {
  42. base::LazyInstance<std::set<const ExtensionFrameHelper*>>::DestructorAtExit
  43. g_frame_helpers = LAZY_INSTANCE_INITIALIZER;
  44. // Returns true if the render frame corresponding with |frame_helper| matches
  45. // the given criteria.
  46. //
  47. // We deliberately do not access any methods that require a v8::Context or
  48. // ScriptContext. See also comment below.
  49. bool RenderFrameMatches(const ExtensionFrameHelper* frame_helper,
  50. mojom::ViewType match_view_type,
  51. int match_window_id,
  52. int match_tab_id,
  53. const std::string& match_extension_id) {
  54. if (match_view_type != mojom::ViewType::kInvalid &&
  55. frame_helper->view_type() != match_view_type)
  56. return false;
  57. // Not all frames have a valid ViewType, e.g. devtools, most GuestViews, and
  58. // unclassified detached WebContents.
  59. if (frame_helper->view_type() == mojom::ViewType::kInvalid)
  60. return false;
  61. // This logic matches ExtensionWebContentsObserver::GetExtensionFromFrame.
  62. blink::WebSecurityOrigin origin =
  63. frame_helper->render_frame()->GetWebFrame()->GetSecurityOrigin();
  64. if (origin.IsOpaque() ||
  65. !base::EqualsASCII(origin.Protocol().Utf16(), kExtensionScheme) ||
  66. !base::EqualsASCII(origin.Host().Utf16(), match_extension_id.c_str()))
  67. return false;
  68. if (match_window_id != extension_misc::kUnknownWindowId &&
  69. frame_helper->browser_window_id() != match_window_id)
  70. return false;
  71. if (match_tab_id != extension_misc::kUnknownTabId &&
  72. frame_helper->tab_id() != match_tab_id)
  73. return false;
  74. // Returning handles to frames that haven't created a script context yet
  75. // can result in the caller "forcing" a script context (by accessing
  76. // properties on the window object). This, in turn, can cause the script
  77. // context to be initialized prematurely, with invalid values (e.g., the
  78. // inability to retrieve a valid URL from the frame). That then leads to
  79. // the ScriptContext being misclassified.
  80. // Don't return any frames until they have a valid ScriptContext to limit
  81. // the chances for bindings to prematurely initialize these contexts.
  82. // This fixes https://crbug.com/1021014.
  83. return frame_helper->did_create_script_context();
  84. }
  85. // Runs every callback in |callbacks_to_be_run_and_cleared| while |frame_helper|
  86. // is valid, and clears |callbacks_to_be_run_and_cleared|.
  87. void RunCallbacksWhileFrameIsValid(
  88. base::WeakPtr<ExtensionFrameHelper> frame_helper,
  89. std::vector<base::OnceClosure>* callbacks_to_be_run_and_cleared) {
  90. // The JavaScript code can cause re-entrancy. To avoid a deadlock, don't run
  91. // callbacks that are added during the iteration.
  92. std::vector<base::OnceClosure> callbacks;
  93. callbacks_to_be_run_and_cleared->swap(callbacks);
  94. for (auto& callback : callbacks) {
  95. std::move(callback).Run();
  96. if (!frame_helper.get())
  97. return; // Frame and ExtensionFrameHelper invalidated by callback.
  98. }
  99. }
  100. enum class PortType {
  101. EXTENSION,
  102. TAB,
  103. NATIVE_APP,
  104. };
  105. // Returns an extension hosted in the |render_frame| (or nullptr if the frame
  106. // doesn't host an extension).
  107. const Extension* GetExtensionFromFrame(content::RenderFrame* render_frame) {
  108. DCHECK(render_frame);
  109. ScriptContext* context =
  110. ScriptContextSet::GetMainWorldContextForFrame(render_frame);
  111. return context ? context->effective_extension() : nullptr;
  112. }
  113. } // namespace
  114. ExtensionFrameHelper::ExtensionFrameHelper(content::RenderFrame* render_frame,
  115. Dispatcher* extension_dispatcher)
  116. : content::RenderFrameObserver(render_frame),
  117. content::RenderFrameObserverTracker<ExtensionFrameHelper>(render_frame),
  118. extension_dispatcher_(extension_dispatcher) {
  119. g_frame_helpers.Get().insert(this);
  120. if (render_frame->GetWebFrame()->IsOutermostMainFrame()) {
  121. // Manages its own lifetime.
  122. new AutomationApiHelper(render_frame);
  123. }
  124. render_frame->GetAssociatedInterfaceRegistry()
  125. ->AddInterface<mojom::LocalFrame>(
  126. base::BindRepeating(&ExtensionFrameHelper::BindLocalFrame,
  127. weak_ptr_factory_.GetWeakPtr()));
  128. }
  129. ExtensionFrameHelper::~ExtensionFrameHelper() {
  130. g_frame_helpers.Get().erase(this);
  131. }
  132. // static
  133. std::vector<content::RenderFrame*> ExtensionFrameHelper::GetExtensionFrames(
  134. const std::string& extension_id,
  135. int browser_window_id,
  136. int tab_id,
  137. mojom::ViewType view_type) {
  138. std::vector<content::RenderFrame*> render_frames;
  139. for (const ExtensionFrameHelper* helper : g_frame_helpers.Get()) {
  140. if (RenderFrameMatches(helper, view_type, browser_window_id, tab_id,
  141. extension_id))
  142. render_frames.push_back(helper->render_frame());
  143. }
  144. return render_frames;
  145. }
  146. // static
  147. v8::Local<v8::Array> ExtensionFrameHelper::GetV8MainFrames(
  148. v8::Local<v8::Context> context,
  149. const std::string& extension_id,
  150. int browser_window_id,
  151. int tab_id,
  152. mojom::ViewType view_type) {
  153. // WebFrame::ScriptCanAccess uses the isolate's current context. We need to
  154. // make sure that the current context is the one we're expecting.
  155. DCHECK(context == context->GetIsolate()->GetCurrentContext());
  156. std::vector<content::RenderFrame*> render_frames =
  157. GetExtensionFrames(extension_id, browser_window_id, tab_id, view_type);
  158. v8::Local<v8::Array> v8_frames = v8::Array::New(context->GetIsolate());
  159. int v8_index = 0;
  160. for (content::RenderFrame* frame : render_frames) {
  161. blink::WebLocalFrame* web_frame = frame->GetWebFrame();
  162. if (!web_frame->IsOutermostMainFrame())
  163. continue;
  164. if (!blink::WebFrame::ScriptCanAccess(web_frame))
  165. continue;
  166. v8::Local<v8::Context> frame_context = web_frame->MainWorldScriptContext();
  167. if (!frame_context.IsEmpty()) {
  168. v8::Local<v8::Value> window = frame_context->Global();
  169. CHECK(!window.IsEmpty());
  170. v8::Maybe<bool> maybe =
  171. v8_frames->CreateDataProperty(context, v8_index++, window);
  172. CHECK(maybe.IsJust() && maybe.FromJust());
  173. }
  174. }
  175. return v8_frames;
  176. }
  177. // static
  178. content::RenderFrame* ExtensionFrameHelper::GetBackgroundPageFrame(
  179. const std::string& extension_id) {
  180. for (const ExtensionFrameHelper* helper : g_frame_helpers.Get()) {
  181. if (RenderFrameMatches(helper, mojom::ViewType::kExtensionBackgroundPage,
  182. extension_misc::kUnknownWindowId,
  183. extension_misc::kUnknownTabId, extension_id)) {
  184. blink::WebLocalFrame* web_frame = helper->render_frame()->GetWebFrame();
  185. // Check if this is the outermost main frame (do not return embedded
  186. // main frames like portals or fenced frames).
  187. if (web_frame->IsOutermostMainFrame())
  188. return helper->render_frame();
  189. }
  190. }
  191. return nullptr;
  192. }
  193. v8::Local<v8::Value> ExtensionFrameHelper::GetV8BackgroundPageMainFrame(
  194. v8::Isolate* isolate,
  195. const std::string& extension_id) {
  196. content::RenderFrame* main_frame = GetBackgroundPageFrame(extension_id);
  197. v8::Local<v8::Value> background_page;
  198. blink::WebLocalFrame* web_frame =
  199. main_frame ? main_frame->GetWebFrame() : nullptr;
  200. if (web_frame && blink::WebFrame::ScriptCanAccess(web_frame))
  201. background_page = web_frame->MainWorldScriptContext()->Global();
  202. else
  203. background_page = v8::Undefined(isolate);
  204. return background_page;
  205. }
  206. // static
  207. content::RenderFrame* ExtensionFrameHelper::FindFrame(
  208. content::RenderFrame* relative_to_frame,
  209. const std::string& name) {
  210. // Only pierce browsing instance boundaries if |relative_to_frame| is an
  211. // extension.
  212. const Extension* extension = GetExtensionFromFrame(relative_to_frame);
  213. if (!extension)
  214. return nullptr;
  215. for (const ExtensionFrameHelper* target : g_frame_helpers.Get()) {
  216. // Skip frames with a mismatched name.
  217. if (target->render_frame()->GetWebFrame()->AssignedName().Utf8() != name)
  218. continue;
  219. // Only pierce browsing instance boundaries if the target frame is from the
  220. // same extension (but not when another extension shares the same renderer
  221. // process because of reuse trigerred by process limit).
  222. if (extension != GetExtensionFromFrame(target->render_frame()))
  223. continue;
  224. return target->render_frame();
  225. }
  226. return nullptr;
  227. }
  228. // static
  229. bool ExtensionFrameHelper::IsContextForEventPage(const ScriptContext* context) {
  230. content::RenderFrame* render_frame = context->GetRenderFrame();
  231. return context->extension() && render_frame &&
  232. BackgroundInfo::HasLazyBackgroundPage(context->extension()) &&
  233. ExtensionFrameHelper::Get(render_frame)->view_type() ==
  234. mojom::ViewType::kExtensionBackgroundPage;
  235. }
  236. void ExtensionFrameHelper::BindLocalFrame(
  237. mojo::PendingAssociatedReceiver<mojom::LocalFrame> pending_receiver) {
  238. local_frame_receiver_.Bind(std::move(pending_receiver));
  239. }
  240. void ExtensionFrameHelper::DidCreateDocumentElement() {
  241. did_create_current_document_element_ = true;
  242. extension_dispatcher_->DidCreateDocumentElement(
  243. render_frame()->GetWebFrame());
  244. }
  245. void ExtensionFrameHelper::DidCreateNewDocument() {
  246. did_create_current_document_element_ = false;
  247. }
  248. void ExtensionFrameHelper::RunScriptsAtDocumentStart() {
  249. DCHECK(did_create_current_document_element_);
  250. RunCallbacksWhileFrameIsValid(weak_ptr_factory_.GetWeakPtr(),
  251. &document_element_created_callbacks_);
  252. // |this| might be dead by now.
  253. }
  254. void ExtensionFrameHelper::RunScriptsAtDocumentEnd() {
  255. RunCallbacksWhileFrameIsValid(weak_ptr_factory_.GetWeakPtr(),
  256. &document_load_finished_callbacks_);
  257. // |this| might be dead by now.
  258. }
  259. void ExtensionFrameHelper::RunScriptsAtDocumentIdle() {
  260. RunCallbacksWhileFrameIsValid(weak_ptr_factory_.GetWeakPtr(),
  261. &document_idle_callbacks_);
  262. // |this| might be dead by now.
  263. }
  264. void ExtensionFrameHelper::ScheduleAtDocumentStart(base::OnceClosure callback) {
  265. document_element_created_callbacks_.push_back(std::move(callback));
  266. }
  267. void ExtensionFrameHelper::ScheduleAtDocumentEnd(base::OnceClosure callback) {
  268. document_load_finished_callbacks_.push_back(std::move(callback));
  269. }
  270. void ExtensionFrameHelper::ScheduleAtDocumentIdle(base::OnceClosure callback) {
  271. document_idle_callbacks_.push_back(std::move(callback));
  272. }
  273. mojom::LocalFrameHost* ExtensionFrameHelper::GetLocalFrameHost() {
  274. if (!local_frame_host_remote_.is_bound()) {
  275. render_frame()->GetRemoteAssociatedInterfaces()->GetInterface(
  276. local_frame_host_remote_.BindNewEndpointAndPassReceiver());
  277. }
  278. return local_frame_host_remote_.get();
  279. }
  280. void ExtensionFrameHelper::ReadyToCommitNavigation(
  281. blink::WebDocumentLoader* document_loader) {
  282. // New window created by chrome.app.window.create() must not start parsing the
  283. // document immediately. The chrome.app.window.create() callback (if any)
  284. // needs to be called prior to the new window's 'load' event. The parser will
  285. // be resumed when it happens. It doesn't apply to sandboxed pages.
  286. if (view_type_ == mojom::ViewType::kAppWindow &&
  287. render_frame()->GetWebFrame()->IsOutermostMainFrame() &&
  288. !has_started_first_navigation_ &&
  289. GURL(document_loader->GetUrl()).SchemeIs(kExtensionScheme) &&
  290. !ScriptContext::IsSandboxedPage(document_loader->GetUrl())) {
  291. document_loader->BlockParser();
  292. }
  293. has_started_first_navigation_ = true;
  294. if (!delayed_main_world_script_initialization_)
  295. return;
  296. base::AutoReset<bool> auto_reset(&is_initializing_main_world_script_context_,
  297. true);
  298. delayed_main_world_script_initialization_ = false;
  299. v8::HandleScope handle_scope(v8::Isolate::GetCurrent());
  300. v8::Local<v8::Context> context =
  301. render_frame()->GetWebFrame()->MainWorldScriptContext();
  302. v8::Context::Scope context_scope(context);
  303. // Normally we would use Document's URL for all kinds of checks, e.g. whether
  304. // to inject a content script. However, when committing a navigation, we
  305. // should use the URL of a Document being committed instead. This URL is
  306. // accessible through WebDocumentLoader::GetURL().
  307. // The scope below temporary maps a frame to a document loader, so that places
  308. // which retrieve URL can use the right one. Ideally, we would plumb the
  309. // correct URL (or maybe WebDocumentLoader) through the callchain, but there
  310. // are many callers which will have to pass nullptr.
  311. ScriptContext::ScopedFrameDocumentLoader scoped_document_loader(
  312. render_frame()->GetWebFrame(), document_loader);
  313. extension_dispatcher_->DidCreateScriptContext(
  314. render_frame()->GetWebFrame(), context, blink::kMainDOMWorldId);
  315. // TODO(devlin): Add constants for main world id, no extension group.
  316. }
  317. void ExtensionFrameHelper::DidCommitProvisionalLoad(
  318. ui::PageTransition transition) {
  319. if (base::FeatureList::IsEnabled(
  320. extensions_features::kAvoidEarlyExtensionScriptContextCreation)) {
  321. return;
  322. }
  323. // Grant cross browsing instance frame lookup if we are an extension. This
  324. // should match the conditions in FindFrame.
  325. content::RenderFrame* frame = render_frame();
  326. if (GetExtensionFromFrame(frame))
  327. frame->SetAllowsCrossBrowsingInstanceFrameLookup();
  328. }
  329. void ExtensionFrameHelper::DidCreateScriptContext(
  330. v8::Local<v8::Context> context,
  331. int32_t world_id) {
  332. if (world_id == blink::kMainDOMWorldId) {
  333. // Accessing MainWorldScriptContext() in ReadyToCommitNavigation() may
  334. // trigger the script context initializing, so we don't want to initialize a
  335. // second time here.
  336. if (is_initializing_main_world_script_context_)
  337. return;
  338. if (render_frame()->IsBrowserSideNavigationPending()) {
  339. // Defer initializing the extensions script context now because it depends
  340. // on having the URL of the provisional load which isn't available at this
  341. // point.
  342. // We can come here twice in the case of window.open(url): first for
  343. // about:blank empty document, then possibly for the actual url load
  344. // (depends on whoever triggers window proxy init), before getting
  345. // ReadyToCommitNavigation.
  346. delayed_main_world_script_initialization_ = true;
  347. return;
  348. }
  349. // Sometimes DidCreateScriptContext comes before ReadyToCommitNavigation.
  350. // In this case we don't have to wait until ReadyToCommitNavigation.
  351. // TODO(dgozman): ensure consistent call order between
  352. // DidCreateScriptContext and ReadyToCommitNavigation.
  353. delayed_main_world_script_initialization_ = false;
  354. }
  355. extension_dispatcher_->DidCreateScriptContext(render_frame()->GetWebFrame(),
  356. context, world_id);
  357. }
  358. void ExtensionFrameHelper::WillReleaseScriptContext(
  359. v8::Local<v8::Context> context,
  360. int32_t world_id) {
  361. extension_dispatcher_->WillReleaseScriptContext(
  362. render_frame()->GetWebFrame(), context, world_id);
  363. }
  364. bool ExtensionFrameHelper::OnMessageReceived(const IPC::Message& message) {
  365. bool handled = true;
  366. IPC_BEGIN_MESSAGE_MAP(ExtensionFrameHelper, message)
  367. IPC_MESSAGE_HANDLER(ExtensionMsg_ValidateMessagePort,
  368. OnExtensionValidateMessagePort)
  369. IPC_MESSAGE_HANDLER(ExtensionMsg_DispatchOnConnect,
  370. OnExtensionDispatchOnConnect)
  371. IPC_MESSAGE_HANDLER(ExtensionMsg_DeliverMessage, OnExtensionDeliverMessage)
  372. IPC_MESSAGE_HANDLER(ExtensionMsg_DispatchOnDisconnect,
  373. OnExtensionDispatchOnDisconnect)
  374. IPC_MESSAGE_UNHANDLED(handled = false)
  375. IPC_END_MESSAGE_MAP()
  376. return handled;
  377. }
  378. void ExtensionFrameHelper::OnExtensionValidateMessagePort(int worker_thread_id,
  379. const PortId& id) {
  380. DCHECK_EQ(kMainThreadId, worker_thread_id);
  381. extension_dispatcher_->bindings_system()
  382. ->messaging_service()
  383. ->ValidateMessagePort(
  384. extension_dispatcher_->script_context_set_iterator(), id,
  385. render_frame());
  386. }
  387. void ExtensionFrameHelper::OnExtensionDispatchOnConnect(
  388. int worker_thread_id,
  389. const PortId& target_port_id,
  390. const std::string& channel_name,
  391. const ExtensionMsg_TabConnectionInfo& source,
  392. const ExtensionMsg_ExternalConnectionInfo& info) {
  393. DCHECK_EQ(kMainThreadId, worker_thread_id);
  394. extension_dispatcher_->bindings_system()
  395. ->messaging_service()
  396. ->DispatchOnConnect(extension_dispatcher_->script_context_set_iterator(),
  397. target_port_id, channel_name, source, info,
  398. render_frame());
  399. }
  400. void ExtensionFrameHelper::OnExtensionDeliverMessage(int worker_thread_id,
  401. const PortId& target_id,
  402. const Message& message) {
  403. DCHECK_EQ(kMainThreadId, worker_thread_id);
  404. extension_dispatcher_->bindings_system()->messaging_service()->DeliverMessage(
  405. extension_dispatcher_->script_context_set_iterator(), target_id, message,
  406. render_frame());
  407. }
  408. void ExtensionFrameHelper::OnExtensionDispatchOnDisconnect(
  409. int worker_thread_id,
  410. const PortId& id,
  411. const std::string& error_message) {
  412. DCHECK_EQ(kMainThreadId, worker_thread_id);
  413. extension_dispatcher_->bindings_system()
  414. ->messaging_service()
  415. ->DispatchOnDisconnect(
  416. extension_dispatcher_->script_context_set_iterator(), id,
  417. error_message, render_frame());
  418. }
  419. void ExtensionFrameHelper::SetTabId(int32_t tab_id) {
  420. CHECK_EQ(tab_id_, -1);
  421. CHECK_GE(tab_id, 0);
  422. tab_id_ = tab_id;
  423. }
  424. void ExtensionFrameHelper::NotifyRenderViewType(mojom::ViewType type) {
  425. // TODO(devlin): It'd be really nice to be able to
  426. // DCHECK_EQ(mojom::ViewType::kInvalid, view_type_) here.
  427. view_type_ = type;
  428. }
  429. void ExtensionFrameHelper::MessageInvoke(const std::string& extension_id,
  430. const std::string& module_name,
  431. const std::string& function_name,
  432. base::Value::List args) {
  433. extension_dispatcher_->InvokeModuleSystemMethod(
  434. render_frame(), extension_id, module_name, function_name, args);
  435. }
  436. void ExtensionFrameHelper::ExecuteCode(mojom::ExecuteCodeParamsPtr param,
  437. ExecuteCodeCallback callback) {
  438. // Sanity checks.
  439. if (param->injection->is_css()) {
  440. if (param->injection->get_css()->sources.empty()) {
  441. local_frame_receiver_.ReportBadMessage(
  442. "At least one CSS source must be specified.");
  443. return;
  444. }
  445. if (param->injection->get_css()->operation ==
  446. mojom::CSSInjection::Operation::kRemove &&
  447. !base::ranges::all_of(param->injection->get_css()->sources,
  448. [](const mojom::CSSSourcePtr& source) {
  449. return source->key.has_value();
  450. })) {
  451. local_frame_receiver_.ReportBadMessage(
  452. "An injection key must be specified for CSS removal.");
  453. return;
  454. }
  455. } else {
  456. DCHECK(param->injection->is_js()); // Enforced by mojo.
  457. if (param->injection->get_js()->sources.empty()) {
  458. local_frame_receiver_.ReportBadMessage(
  459. "At least one JS source must be specified.");
  460. return;
  461. }
  462. }
  463. extension_dispatcher_->ExecuteCode(std::move(param), std::move(callback),
  464. render_frame());
  465. }
  466. void ExtensionFrameHelper::SetFrameName(const std::string& name) {
  467. render_frame()->GetWebFrame()->SetName(blink::WebString::FromUTF8(name));
  468. }
  469. void ExtensionFrameHelper::AppWindowClosed(bool send_onclosed) {
  470. DCHECK(render_frame()->GetWebFrame()->IsOutermostMainFrame());
  471. if (!send_onclosed)
  472. return;
  473. v8::HandleScope scope(v8::Isolate::GetCurrent());
  474. v8::Local<v8::Context> v8_context =
  475. render_frame()->GetWebFrame()->MainWorldScriptContext();
  476. ScriptContext* script_context =
  477. ScriptContextSet::GetContextByV8Context(v8_context);
  478. if (!script_context)
  479. return;
  480. script_context->module_system()->CallModuleMethodSafe("app.window",
  481. "onAppWindowClosed");
  482. }
  483. void ExtensionFrameHelper::SetSpatialNavigationEnabled(bool enabled) {
  484. render_frame()
  485. ->GetWebView()
  486. ->GetSettings()
  487. ->SetSpatialNavigationEnabled(enabled);
  488. }
  489. void ExtensionFrameHelper::ExecuteDeclarativeScript(
  490. int32_t tab_id,
  491. const std::string& extension_id,
  492. const std::string& script_id,
  493. const GURL& url) {
  494. // TODO(https://crbug.com/1186220): URL-checking isn't the best approach to
  495. // avoid user data leak. Consider what we can do to mitigate this case.
  496. // Begin script injection workflow only if the current URL is identical to the
  497. // one that matched declarative conditions in the browser.
  498. if (GURL(render_frame()->GetWebFrame()->GetDocument().Url()) == url) {
  499. extension_dispatcher_->ExecuteDeclarativeScript(
  500. render_frame(), tab_id, extension_id, script_id, url);
  501. }
  502. }
  503. void ExtensionFrameHelper::UpdateBrowserWindowId(int32_t window_id) {
  504. browser_window_id_ = window_id;
  505. }
  506. void ExtensionFrameHelper::NotifyDidCreateScriptContext(int32_t world_id) {
  507. did_create_script_context_ = true;
  508. }
  509. void ExtensionFrameHelper::OnDestruct() {
  510. delete this;
  511. }
  512. void ExtensionFrameHelper::DraggableRegionsChanged() {
  513. if (!render_frame()->GetWebFrame()->IsOutermostMainFrame())
  514. return;
  515. blink::WebVector<blink::WebDraggableRegion> webregions =
  516. render_frame()->GetWebFrame()->GetDocument().DraggableRegions();
  517. std::vector<DraggableRegion> regions;
  518. for (blink::WebDraggableRegion& webregion : webregions) {
  519. render_frame()->ConvertViewportToWindow(&webregion.bounds);
  520. regions.push_back(DraggableRegion());
  521. DraggableRegion& region = regions.back();
  522. region.bounds = webregion.bounds;
  523. region.draggable = webregion.draggable;
  524. }
  525. Send(new ExtensionHostMsg_UpdateDraggableRegions(routing_id(), regions));
  526. }
  527. void ExtensionFrameHelper::DidClearWindowObject() {
  528. // DidClearWindowObject() is called right at the end of
  529. // DocumentLoader::CreateParserPostCommit(). This is late enough in the commit
  530. // process that it won't interfere with any optimizations, since the code
  531. // below may cause the V8 context to be initialized.
  532. //
  533. // Calling this multiple times in a page load is safe because
  534. // SetAllowsCrossBrowsingInstanceFrameLookup() just sets a bool to true on the
  535. // SecurityOrigin.
  536. if (base::FeatureList::IsEnabled(
  537. extensions_features::kAvoidEarlyExtensionScriptContextCreation)) {
  538. // Grant cross browsing instance frame lookup if we are an extension. This
  539. // should match the conditions in FindFrame.
  540. content::RenderFrame* frame = render_frame();
  541. if (GetExtensionFromFrame(frame))
  542. frame->SetAllowsCrossBrowsingInstanceFrameLookup();
  543. }
  544. }
  545. } // namespace extensions