extension_web_contents_observer.cc 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394
  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. #include "extensions/browser/extension_web_contents_observer.h"
  5. #include "base/check.h"
  6. #include "components/sessions/content/session_tab_helper.h"
  7. #include "content/public/browser/child_process_security_policy.h"
  8. #include "content/public/browser/navigation_handle.h"
  9. #include "content/public/browser/render_frame_host.h"
  10. #include "content/public/browser/render_process_host.h"
  11. #include "content/public/browser/site_instance.h"
  12. #include "content/public/browser/web_contents.h"
  13. #include "content/public/common/url_constants.h"
  14. #include "extensions/browser/content_script_tracker.h"
  15. #include "extensions/browser/extension_api_frame_id_map.h"
  16. #include "extensions/browser/extension_frame_host.h"
  17. #include "extensions/browser/extension_prefs.h"
  18. #include "extensions/browser/extension_registry.h"
  19. #include "extensions/browser/extension_util.h"
  20. #include "extensions/browser/extensions_browser_client.h"
  21. #include "extensions/browser/kiosk/kiosk_delegate.h"
  22. #include "extensions/browser/process_manager.h"
  23. #include "extensions/browser/renderer_startup_helper.h"
  24. #include "extensions/browser/view_type_utils.h"
  25. #include "extensions/common/constants.h"
  26. #include "extensions/common/extension.h"
  27. #include "extensions/common/extension_messages.h"
  28. #include "extensions/common/mojom/view_type.mojom.h"
  29. #include "third_party/blink/public/common/associated_interfaces/associated_interface_provider.h"
  30. #include "third_party/blink/public/mojom/autoplay/autoplay.mojom.h"
  31. #include "url/origin.h"
  32. namespace extensions {
  33. // static
  34. ExtensionWebContentsObserver* ExtensionWebContentsObserver::GetForWebContents(
  35. content::WebContents* web_contents) {
  36. return ExtensionsBrowserClient::Get()->GetExtensionWebContentsObserver(
  37. web_contents);
  38. }
  39. // static
  40. void ExtensionWebContentsObserver::BindLocalFrameHost(
  41. mojo::PendingAssociatedReceiver<mojom::LocalFrameHost> receiver,
  42. content::RenderFrameHost* rfh) {
  43. auto* web_contents = content::WebContents::FromRenderFrameHost(rfh);
  44. if (!web_contents)
  45. return;
  46. auto* observer = GetForWebContents(web_contents);
  47. if (!observer)
  48. return;
  49. auto* efh = observer->extension_frame_host_.get();
  50. if (!efh)
  51. return;
  52. efh->BindLocalFrameHost(std::move(receiver), rfh);
  53. }
  54. std::unique_ptr<ExtensionFrameHost>
  55. ExtensionWebContentsObserver::CreateExtensionFrameHost(
  56. content::WebContents* web_contents) {
  57. return std::make_unique<ExtensionFrameHost>(web_contents);
  58. }
  59. void ExtensionWebContentsObserver::ListenToWindowIdChangesFrom(
  60. sessions::SessionTabHelper* helper) {
  61. if (!window_id_subscription_) {
  62. // We use an unretained receiver here: the callback is inside the
  63. // subscription, which is a member of |this|, so it can't be run after the
  64. // destruction of |this|.
  65. window_id_subscription_ = helper->RegisterForWindowIdChanged(
  66. base::BindRepeating(&ExtensionWebContentsObserver::OnWindowIdChanged,
  67. base::Unretained(this)));
  68. }
  69. }
  70. void ExtensionWebContentsObserver::Initialize() {
  71. if (initialized_)
  72. return;
  73. initialized_ = true;
  74. extension_frame_host_ = CreateExtensionFrameHost(web_contents());
  75. web_contents()->ForEachRenderFrameHost(base::BindRepeating(
  76. [](ExtensionWebContentsObserver* observer,
  77. content::RenderFrameHost* render_frame_host) {
  78. // We only initialize the frame if the renderer counterpart is live;
  79. // otherwise we wait for the RenderFrameCreated notification.
  80. if (render_frame_host->IsRenderFrameLive())
  81. observer->InitializeRenderFrame(render_frame_host);
  82. },
  83. this));
  84. // It would be ideal if SessionTabHelper was created before this object,
  85. // because then we could start observing it here instead of needing to be
  86. // externally notified when it is created, but it isn't. If that ordering ever
  87. // changes, this code can be restructured and ListenToWindowIdChangesFrom()
  88. // can become private.
  89. DCHECK(!sessions::SessionTabHelper::FromWebContents(web_contents()));
  90. }
  91. ExtensionWebContentsObserver::ExtensionWebContentsObserver(
  92. content::WebContents* web_contents)
  93. : content::WebContentsObserver(web_contents),
  94. browser_context_(web_contents->GetBrowserContext()),
  95. dispatcher_(browser_context_),
  96. initialized_(false) {
  97. dispatcher_.set_delegate(this);
  98. }
  99. ExtensionWebContentsObserver::~ExtensionWebContentsObserver() {
  100. }
  101. void ExtensionWebContentsObserver::InitializeRenderFrame(
  102. content::RenderFrameHost* render_frame_host) {
  103. DCHECK(initialized_);
  104. DCHECK(render_frame_host);
  105. DCHECK(render_frame_host->IsRenderFrameLive());
  106. // At the initialization of the render frame, the last committed URL is not
  107. // reliable, so do not take it into account in determining whether it is an
  108. // extension frame.
  109. const Extension* frame_extension =
  110. GetExtensionFromFrame(render_frame_host, false);
  111. // This observer is attached to every WebContents, so we are also notified of
  112. // frames that are not in an extension process.
  113. if (!frame_extension)
  114. return;
  115. // |render_frame_host->GetProcess()| is an extension process. Grant permission
  116. // to request pages from the extension's origin.
  117. content::ChildProcessSecurityPolicy* security_policy =
  118. content::ChildProcessSecurityPolicy::GetInstance();
  119. int process_id = render_frame_host->GetProcess()->GetID();
  120. security_policy->GrantRequestOrigin(process_id, frame_extension->origin());
  121. // Notify the render frame of the view type.
  122. GetLocalFrame(render_frame_host)
  123. ->NotifyRenderViewType(GetViewType(web_contents()));
  124. ProcessManager::Get(browser_context_)
  125. ->RegisterRenderFrameHost(web_contents(), render_frame_host,
  126. frame_extension);
  127. }
  128. content::WebContents* ExtensionWebContentsObserver::GetAssociatedWebContents()
  129. const {
  130. DCHECK(initialized_);
  131. return web_contents();
  132. }
  133. void ExtensionWebContentsObserver::RenderFrameCreated(
  134. content::RenderFrameHost* render_frame_host) {
  135. DCHECK(initialized_);
  136. InitializeRenderFrame(render_frame_host);
  137. ContentScriptTracker::RenderFrameCreated(PassKey(), render_frame_host);
  138. const Extension* extension = GetExtensionFromFrame(render_frame_host, false);
  139. if (!extension)
  140. return;
  141. Manifest::Type type = extension->GetType();
  142. // Some extensions use file:// URLs.
  143. //
  144. // Note: this particular grant isn't relevant for hosted apps, but in the
  145. // future we should be careful about granting privileges to hosted app
  146. // subframes in places like this, since they currently stay in process with
  147. // their parent. A malicious site shouldn't be able to gain a hosted app's
  148. // privileges just by embedding a subframe to a popular hosted app.
  149. //
  150. // Note: Keep this logic in sync with related logic in
  151. // ChromeContentBrowserClient::RegisterNonNetworkSubresourceURLLoaderFactories.
  152. if (type == Manifest::TYPE_EXTENSION ||
  153. type == Manifest::TYPE_LEGACY_PACKAGED_APP) {
  154. util::InitializeFileSchemeAccessForExtension(
  155. render_frame_host->GetProcess()->GetID(), extension->id(),
  156. browser_context_);
  157. }
  158. // Tells the new frame that it's hosted in an extension process.
  159. //
  160. // This will often be a redundant IPC, because activating extensions happens
  161. // at the process level, not at the frame level. However, without some mild
  162. // refactoring this isn't trivial to do, and this way is simpler.
  163. //
  164. // Plus, we can delete the concept of activating an extension once site
  165. // isolation is turned on.
  166. RendererStartupHelperFactory::GetForBrowserContext(browser_context_)
  167. ->ActivateExtensionInProcess(*extension, render_frame_host->GetProcess());
  168. }
  169. void ExtensionWebContentsObserver::RenderFrameDeleted(
  170. content::RenderFrameHost* render_frame_host) {
  171. DCHECK(initialized_);
  172. local_frame_map_.erase(render_frame_host);
  173. ProcessManager::Get(browser_context_)
  174. ->UnregisterRenderFrameHost(render_frame_host);
  175. ExtensionApiFrameIdMap::Get()->OnRenderFrameDeleted(render_frame_host);
  176. ContentScriptTracker::RenderFrameDeleted(PassKey(), render_frame_host);
  177. }
  178. void ExtensionWebContentsObserver::ReadyToCommitNavigation(
  179. content::NavigationHandle* navigation_handle) {
  180. ContentScriptTracker::ReadyToCommitNavigation(PassKey(), navigation_handle);
  181. // We don't force autoplay to allow while prerendering.
  182. if (navigation_handle->GetRenderFrameHost()->GetLifecycleState() ==
  183. content::RenderFrameHost::LifecycleState::kPrerendering &&
  184. !navigation_handle->IsPrerenderedPageActivation()) {
  185. return;
  186. }
  187. const ExtensionRegistry* const registry =
  188. ExtensionRegistry::Get(browser_context_);
  189. content::RenderFrameHost* parent_or_outerdoc =
  190. navigation_handle->GetParentFrameOrOuterDocument();
  191. content::RenderFrameHost* outermost_main_rfh =
  192. parent_or_outerdoc ? parent_or_outerdoc->GetOutermostMainFrame()
  193. : navigation_handle->GetRenderFrameHost();
  194. const Extension* const extension =
  195. GetExtensionFromFrame(outermost_main_rfh, false);
  196. KioskDelegate* const kiosk_delegate =
  197. ExtensionsBrowserClient::Get()->GetKioskDelegate();
  198. DCHECK(kiosk_delegate);
  199. bool is_kiosk =
  200. extension && kiosk_delegate->IsAutoLaunchedKioskApp(extension->id());
  201. // If the top most frame is an extension, packaged app, hosted app, etc. then
  202. // the main frame and all iframes should be able to autoplay without
  203. // restriction. <webview> should still have autoplay blocked though.
  204. GURL url =
  205. parent_or_outerdoc
  206. ? parent_or_outerdoc->GetOutermostMainFrame()->GetLastCommittedURL()
  207. : navigation_handle->GetURL();
  208. if (is_kiosk || registry->enabled_extensions().GetExtensionOrAppByURL(url)) {
  209. mojo::AssociatedRemote<blink::mojom::AutoplayConfigurationClient> client;
  210. navigation_handle->GetRenderFrameHost()
  211. ->GetRemoteAssociatedInterfaces()
  212. ->GetInterface(&client);
  213. client->AddAutoplayFlags(url::Origin::Create(navigation_handle->GetURL()),
  214. blink::mojom::kAutoplayFlagForceAllow);
  215. }
  216. }
  217. void ExtensionWebContentsObserver::DidFinishNavigation(
  218. content::NavigationHandle* navigation_handle) {
  219. DCHECK(initialized_);
  220. if (!navigation_handle->HasCommitted())
  221. return;
  222. ProcessManager* pm = ProcessManager::Get(browser_context_);
  223. content::RenderFrameHost* render_frame_host =
  224. navigation_handle->GetRenderFrameHost();
  225. DCHECK(render_frame_host);
  226. const Extension* frame_extension =
  227. GetExtensionFromFrame(render_frame_host, true);
  228. if (pm->IsRenderFrameHostRegistered(render_frame_host)) {
  229. if (!frame_extension)
  230. pm->UnregisterRenderFrameHost(render_frame_host);
  231. } else if (frame_extension && render_frame_host->IsRenderFrameLive()) {
  232. pm->RegisterRenderFrameHost(web_contents(), render_frame_host,
  233. frame_extension);
  234. }
  235. ContentScriptTracker::DidFinishNavigation(PassKey(), navigation_handle);
  236. }
  237. void ExtensionWebContentsObserver::MediaPictureInPictureChanged(
  238. bool is_picture_in_picture) {
  239. DCHECK(initialized_);
  240. if (GetViewType(web_contents()) ==
  241. mojom::ViewType::kExtensionBackgroundPage) {
  242. ProcessManager* const process_manager =
  243. ProcessManager::Get(browser_context_);
  244. const Extension* const extension =
  245. process_manager->GetExtensionForWebContents(web_contents());
  246. if (extension == nullptr)
  247. return;
  248. if (is_picture_in_picture)
  249. process_manager->IncrementLazyKeepaliveCount(extension, Activity::MEDIA,
  250. Activity::kPictureInPicture);
  251. else
  252. process_manager->DecrementLazyKeepaliveCount(extension, Activity::MEDIA,
  253. Activity::kPictureInPicture);
  254. }
  255. }
  256. void ExtensionWebContentsObserver::PepperInstanceCreated() {
  257. DCHECK(initialized_);
  258. if (GetViewType(web_contents()) ==
  259. mojom::ViewType::kExtensionBackgroundPage) {
  260. ProcessManager* const process_manager =
  261. ProcessManager::Get(browser_context_);
  262. const Extension* const extension =
  263. process_manager->GetExtensionForWebContents(web_contents());
  264. if (extension)
  265. process_manager->IncrementLazyKeepaliveCount(
  266. extension, Activity::PEPPER_API, std::string());
  267. }
  268. }
  269. void ExtensionWebContentsObserver::PepperInstanceDeleted() {
  270. DCHECK(initialized_);
  271. if (GetViewType(web_contents()) ==
  272. mojom::ViewType::kExtensionBackgroundPage) {
  273. ProcessManager* const process_manager =
  274. ProcessManager::Get(browser_context_);
  275. const Extension* const extension =
  276. process_manager->GetExtensionForWebContents(web_contents());
  277. if (extension)
  278. process_manager->DecrementLazyKeepaliveCount(
  279. extension, Activity::PEPPER_API, std::string());
  280. }
  281. }
  282. std::string ExtensionWebContentsObserver::GetExtensionIdFromFrame(
  283. content::RenderFrameHost* render_frame_host) const {
  284. DCHECK(initialized_);
  285. const GURL& site = render_frame_host->GetSiteInstance()->GetSiteURL();
  286. if (!site.SchemeIs(kExtensionScheme))
  287. return std::string();
  288. return site.host();
  289. }
  290. const Extension* ExtensionWebContentsObserver::GetExtensionFromFrame(
  291. content::RenderFrameHost* render_frame_host,
  292. bool verify_url) const {
  293. DCHECK(initialized_);
  294. std::string extension_id = GetExtensionIdFromFrame(render_frame_host);
  295. if (extension_id.empty())
  296. return nullptr;
  297. content::BrowserContext* browser_context =
  298. render_frame_host->GetProcess()->GetBrowserContext();
  299. const Extension* extension = ExtensionRegistry::Get(browser_context)
  300. ->enabled_extensions()
  301. .GetByID(extension_id);
  302. if (!extension)
  303. return nullptr;
  304. if (verify_url) {
  305. const url::Origin& origin(render_frame_host->GetLastCommittedOrigin());
  306. // This check is needed to eliminate origins that are not within a
  307. // hosted-app's web extent, and sandboxed extension frames with an opaque
  308. // origin.
  309. // TODO(1139108) See if extension check is still needed after bug is fixed.
  310. auto* extension_for_origin = ExtensionRegistry::Get(browser_context)
  311. ->enabled_extensions()
  312. .GetExtensionOrAppByURL(origin.GetURL());
  313. if (origin.opaque() || extension_for_origin != extension)
  314. return nullptr;
  315. }
  316. return extension;
  317. }
  318. mojom::LocalFrame* ExtensionWebContentsObserver::GetLocalFrame(
  319. content::RenderFrameHost* render_frame_host) {
  320. // Attempting to get a remote interface before IsRenderFrameLive() will fail,
  321. // leaving a broken pipe that will block all further messages. Return nullptr
  322. // instead. Callers should try again after RenderFrameCreated().
  323. if (!render_frame_host->IsRenderFrameLive())
  324. return nullptr;
  325. mojo::AssociatedRemote<mojom::LocalFrame>& remote =
  326. local_frame_map_[render_frame_host];
  327. if (!remote.is_bound()) {
  328. render_frame_host->GetRemoteAssociatedInterfaces()->GetInterface(
  329. remote.BindNewEndpointAndPassReceiver());
  330. }
  331. return remote.get();
  332. }
  333. void ExtensionWebContentsObserver::OnWindowIdChanged(const SessionID& id) {
  334. web_contents()->ForEachRenderFrameHost(base::BindRepeating(
  335. [](int32_t window_id, ExtensionWebContentsObserver* observer,
  336. content::RenderFrameHost* rfh) {
  337. auto* local_frame = observer->GetLocalFrame(rfh);
  338. if (local_frame)
  339. local_frame->UpdateBrowserWindowId(window_id);
  340. },
  341. id.id(), base::Unretained(this)));
  342. }
  343. } // namespace extensions