onscreen_content_provider.cc 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308
  1. // Copyright 2019 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 "components/content_capture/browser/onscreen_content_provider.h"
  5. #include <utility>
  6. #include "base/memory/ptr_util.h"
  7. #include "base/notreached.h"
  8. #include "components/content_capture/browser/content_capture_consumer.h"
  9. #include "components/content_capture/browser/content_capture_receiver.h"
  10. #include "content/public/browser/browser_context.h"
  11. #include "content/public/browser/global_routing_id.h"
  12. #include "content/public/browser/navigation_entry.h"
  13. #include "content/public/browser/navigation_handle.h"
  14. #include "content/public/browser/web_contents.h"
  15. #include "mojo/public/cpp/bindings/pending_associated_receiver.h"
  16. #include "third_party/blink/public/mojom/favicon/favicon_url.mojom.h"
  17. namespace content_capture {
  18. WEB_CONTENTS_USER_DATA_KEY_IMPL(OnscreenContentProvider);
  19. OnscreenContentProvider::OnscreenContentProvider(
  20. content::WebContents* web_contents)
  21. : content::WebContentsObserver(web_contents),
  22. content::WebContentsUserData<OnscreenContentProvider>(*web_contents) {
  23. web_contents->ForEachRenderFrameHost(base::BindRepeating(
  24. [](OnscreenContentProvider* provider,
  25. content::RenderFrameHost* render_frame_host) {
  26. // Don't cross into inner WebContents since we wouldn't be notified of
  27. // its changes.
  28. if (content::WebContents::FromRenderFrameHost(render_frame_host) !=
  29. provider->web_contents()) {
  30. return content::RenderFrameHost::FrameIterationAction::kSkipChildren;
  31. }
  32. if (render_frame_host->IsRenderFrameLive()) {
  33. provider->RenderFrameCreated(render_frame_host);
  34. }
  35. return content::RenderFrameHost::FrameIterationAction::kContinue;
  36. },
  37. this));
  38. }
  39. OnscreenContentProvider::~OnscreenContentProvider() = default;
  40. // static
  41. OnscreenContentProvider* OnscreenContentProvider::Create(
  42. content::WebContents* web_contents) {
  43. DCHECK(!FromWebContents(web_contents));
  44. CreateForWebContents(web_contents);
  45. return FromWebContents(web_contents);
  46. }
  47. // static
  48. void OnscreenContentProvider::BindContentCaptureReceiver(
  49. mojo::PendingAssociatedReceiver<mojom::ContentCaptureReceiver>
  50. pending_receiver,
  51. content::RenderFrameHost* render_frame_host) {
  52. content::WebContents* web_contents =
  53. content::WebContents::FromRenderFrameHost(render_frame_host);
  54. if (!web_contents)
  55. return;
  56. OnscreenContentProvider* manager =
  57. OnscreenContentProvider::FromWebContents(web_contents);
  58. if (!manager)
  59. return;
  60. auto* receiver = manager->ContentCaptureReceiverForFrame(render_frame_host);
  61. if (receiver)
  62. receiver->BindPendingReceiver(std::move(pending_receiver));
  63. }
  64. void OnscreenContentProvider::AddConsumer(ContentCaptureConsumer& consumer) {
  65. consumers_.push_back(&consumer);
  66. }
  67. void OnscreenContentProvider::RemoveConsumer(ContentCaptureConsumer& consumer) {
  68. for (auto it = consumers_.begin(); it != consumers_.end(); ++it) {
  69. if (*it == &consumer) {
  70. ContentCaptureSession session;
  71. if (BuildContentCaptureSessionForMainFrame(&session)) {
  72. consumer.DidRemoveSession(session);
  73. }
  74. consumers_.erase(it);
  75. return;
  76. }
  77. }
  78. NOTREACHED();
  79. }
  80. ContentCaptureReceiver* OnscreenContentProvider::ContentCaptureReceiverForFrame(
  81. content::RenderFrameHost* render_frame_host) const {
  82. auto mapping = frame_map_.find(render_frame_host->GetGlobalId());
  83. return mapping == frame_map_.end() ? nullptr : mapping->second.get();
  84. }
  85. void OnscreenContentProvider::RenderFrameCreated(
  86. content::RenderFrameHost* render_frame_host) {
  87. // The frame might not have content, but it could be parent of other frame. we
  88. // always create the ContentCaptureReceiver for ContentCaptureSession
  89. // purpose.
  90. if (ContentCaptureReceiverForFrame(render_frame_host))
  91. return;
  92. frame_map_.insert(std::make_pair(
  93. render_frame_host->GetGlobalId(),
  94. std::make_unique<ContentCaptureReceiver>(render_frame_host)));
  95. }
  96. void OnscreenContentProvider::RenderFrameDeleted(
  97. content::RenderFrameHost* render_frame_host) {
  98. if (auto* content_capture_receiver =
  99. ContentCaptureReceiverForFrame(render_frame_host)) {
  100. content_capture_receiver->RemoveSession();
  101. }
  102. frame_map_.erase(render_frame_host->GetGlobalId());
  103. }
  104. void OnscreenContentProvider::ReadyToCommitNavigation(
  105. content::NavigationHandle* navigation_handle) {
  106. // Don't remove the session for the same document navigation.
  107. if (!navigation_handle->IsSameDocument()) {
  108. if (auto* rfh = content::RenderFrameHost::FromID(
  109. navigation_handle->GetPreviousRenderFrameHostId())) {
  110. if (auto* receiver = ContentCaptureReceiverForFrame(rfh)) {
  111. receiver->RemoveSession();
  112. }
  113. }
  114. }
  115. if (auto* receiver = ContentCaptureReceiverForFrame(
  116. navigation_handle->GetRenderFrameHost())) {
  117. if (web_contents()->GetBrowserContext()->IsOffTheRecord() ||
  118. !ShouldCapture(navigation_handle->GetURL())) {
  119. receiver->StopCapture();
  120. return;
  121. }
  122. receiver->StartCapture();
  123. }
  124. }
  125. void OnscreenContentProvider::TitleWasSet(content::NavigationEntry* entry) {
  126. // Set the title to the mainframe.
  127. if (auto* receiver = ContentCaptureReceiverForFrame(
  128. web_contents()->GetPrimaryMainFrame())) {
  129. // To match what the user sees, intentionally get the title from WebContents
  130. // instead of NavigationEntry, though they might be same.
  131. receiver->SetTitle(web_contents()->GetTitle());
  132. }
  133. }
  134. void OnscreenContentProvider::DidCaptureContent(
  135. ContentCaptureReceiver* content_capture_receiver,
  136. const ContentCaptureFrame& data) {
  137. // The root of |data| is frame, we need get its ancestor only.
  138. ContentCaptureSession parent_session;
  139. BuildContentCaptureSession(content_capture_receiver, true /* ancestor_only */,
  140. &parent_session);
  141. for (auto* consumer : consumers_)
  142. consumer->DidCaptureContent(parent_session, data);
  143. }
  144. void OnscreenContentProvider::DidUpdateContent(
  145. ContentCaptureReceiver* content_capture_receiver,
  146. const ContentCaptureFrame& data) {
  147. ContentCaptureSession parent_session;
  148. BuildContentCaptureSession(content_capture_receiver, true /* ancestor_only */,
  149. &parent_session);
  150. for (auto* consumer : consumers_)
  151. consumer->DidUpdateContent(parent_session, data);
  152. }
  153. void OnscreenContentProvider::DidRemoveContent(
  154. ContentCaptureReceiver* content_capture_receiver,
  155. const std::vector<int64_t>& data) {
  156. ContentCaptureSession session;
  157. // The |data| is a list of text content id, the session should include
  158. // |content_capture_receiver| associated frame.
  159. BuildContentCaptureSession(content_capture_receiver,
  160. false /* ancestor_only */, &session);
  161. for (auto* consumer : consumers_)
  162. consumer->DidRemoveContent(session, data);
  163. }
  164. void OnscreenContentProvider::DidRemoveSession(
  165. ContentCaptureReceiver* content_capture_receiver) {
  166. ContentCaptureSession session;
  167. // The session should include the removed frame that the
  168. // |content_capture_receiver| associated with.
  169. // We want the last reported content capture session, instead of the current
  170. // one for the scenario like below:
  171. // Main frame navigates to different url which has the same origin of previous
  172. // one, it triggers the previous child frame being removed but the main RFH
  173. // unchanged, if we use BuildContentCaptureSession() which always use the
  174. // current URL to build session, the new session will be created for current
  175. // main frame URL, the returned ContentCaptureSession is wrong.
  176. if (!BuildContentCaptureSessionLastSeen(content_capture_receiver, &session))
  177. return;
  178. for (auto* consumer : consumers_)
  179. consumer->DidRemoveSession(session);
  180. }
  181. void OnscreenContentProvider::DidUpdateTitle(
  182. ContentCaptureReceiver* content_capture_receiver) {
  183. ContentCaptureSession session;
  184. BuildContentCaptureSession(content_capture_receiver,
  185. /*ancestor_only=*/false, &session);
  186. // Shall only update mainframe's title.
  187. DCHECK(session.size() == 1);
  188. for (auto* consumer : consumers_)
  189. consumer->DidUpdateTitle(*session.begin());
  190. }
  191. void OnscreenContentProvider::DidUpdateFaviconURL(
  192. content::RenderFrameHost* render_frame_host,
  193. const std::vector<blink::mojom::FaviconURLPtr>& candidates) {
  194. if (ContentCaptureReceiver::
  195. disable_get_favicon_from_web_contents_for_testing()) {
  196. return;
  197. }
  198. NotifyFaviconURLUpdated(render_frame_host, candidates);
  199. }
  200. void OnscreenContentProvider::NotifyFaviconURLUpdated(
  201. content::RenderFrameHost* render_frame_host,
  202. const std::vector<blink::mojom::FaviconURLPtr>& candidates) {
  203. DCHECK(render_frame_host->IsInPrimaryMainFrame());
  204. if (auto* receiver = ContentCaptureReceiverForFrame(render_frame_host)) {
  205. receiver->UpdateFaviconURL(candidates);
  206. }
  207. }
  208. void OnscreenContentProvider::DidUpdateFavicon(
  209. ContentCaptureReceiver* content_capture_receiver) {
  210. ContentCaptureSession session;
  211. BuildContentCaptureSession(content_capture_receiver,
  212. /*ancestor_only=*/false, &session);
  213. // Shall only update mainframe's title.
  214. DCHECK(session.size() == 1);
  215. for (auto* consumer : consumers_)
  216. consumer->DidUpdateFavicon(*session.begin());
  217. }
  218. void OnscreenContentProvider::BuildContentCaptureSession(
  219. ContentCaptureReceiver* content_capture_receiver,
  220. bool ancestor_only,
  221. ContentCaptureSession* session) {
  222. if (!ancestor_only)
  223. session->push_back(content_capture_receiver->GetContentCaptureFrame());
  224. content::RenderFrameHost* rfh =
  225. content_capture_receiver->rfh()->GetParentOrOuterDocument();
  226. while (rfh) {
  227. ContentCaptureReceiver* receiver = ContentCaptureReceiverForFrame(rfh);
  228. // TODO(michaelbai): Only creates ContentCaptureReceiver here, clean up the
  229. // code in RenderFrameCreated().
  230. if (!receiver) {
  231. RenderFrameCreated(rfh);
  232. receiver = ContentCaptureReceiverForFrame(rfh);
  233. DCHECK(receiver);
  234. }
  235. session->push_back(receiver->GetContentCaptureFrame());
  236. rfh = receiver->rfh()->GetParentOrOuterDocument();
  237. }
  238. }
  239. bool OnscreenContentProvider::BuildContentCaptureSessionLastSeen(
  240. ContentCaptureReceiver* content_capture_receiver,
  241. ContentCaptureSession* session) {
  242. session->push_back(
  243. content_capture_receiver->GetContentCaptureFrameLastSeen());
  244. content::RenderFrameHost* rfh =
  245. content_capture_receiver->rfh()->GetParentOrOuterDocument();
  246. while (rfh) {
  247. ContentCaptureReceiver* receiver = ContentCaptureReceiverForFrame(rfh);
  248. if (!receiver)
  249. return false;
  250. session->push_back(receiver->GetContentCaptureFrameLastSeen());
  251. rfh = receiver->rfh()->GetParentOrOuterDocument();
  252. }
  253. return true;
  254. }
  255. bool OnscreenContentProvider::BuildContentCaptureSessionForMainFrame(
  256. ContentCaptureSession* session) {
  257. if (auto* receiver = ContentCaptureReceiverForFrame(
  258. web_contents()->GetPrimaryMainFrame())) {
  259. session->push_back(receiver->GetContentCaptureFrame());
  260. return true;
  261. }
  262. return false;
  263. }
  264. bool OnscreenContentProvider::ShouldCapture(const GURL& url) {
  265. for (auto* consumer : consumers_) {
  266. if (consumer->ShouldCapture(url))
  267. return true;
  268. }
  269. return false;
  270. }
  271. } // namespace content_capture