blob: d3140e1f696893ef5dffa0944c02a4e594b97afc [file] [log] [blame]
// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "components/content_capture/browser/content_capture_receiver_manager.h"
#include <utility>
#include "components/content_capture/browser/content_capture_receiver.h"
#include "content/public/browser/browser_context.h"
#include "content/public/browser/navigation_handle.h"
#include "content/public/browser/web_contents.h"
namespace content_capture {
namespace {
const void* const kUserDataKey = &kUserDataKey;
} // namespace
ContentCaptureReceiverManager::ContentCaptureReceiverManager(
content::WebContents* web_contents)
: content::WebContentsObserver(web_contents) {
const std::vector<content::RenderFrameHost*> frames =
web_contents->GetAllFrames();
for (content::RenderFrameHost* frame : frames)
RenderFrameCreated(frame);
web_contents->SetUserData(
kUserDataKey, std::unique_ptr<ContentCaptureReceiverManager>(this));
}
ContentCaptureReceiverManager::~ContentCaptureReceiverManager() = default;
// static
ContentCaptureReceiverManager* ContentCaptureReceiverManager::FromWebContents(
content::WebContents* contents) {
return static_cast<ContentCaptureReceiverManager*>(
contents->GetUserData(kUserDataKey));
}
// static
void ContentCaptureReceiverManager::BindContentCaptureReceiver(
mojom::ContentCaptureReceiverAssociatedRequest request,
content::RenderFrameHost* render_frame_host) {
content::WebContents* web_contents =
content::WebContents::FromRenderFrameHost(render_frame_host);
if (!web_contents)
return;
ContentCaptureReceiverManager* manager =
ContentCaptureReceiverManager::FromWebContents(web_contents);
if (!manager)
return;
auto* receiver = manager->ContentCaptureReceiverForFrame(render_frame_host);
if (receiver)
receiver->BindRequest(std::move(request));
}
ContentCaptureReceiver*
ContentCaptureReceiverManager::ContentCaptureReceiverForFrame(
content::RenderFrameHost* render_frame_host) {
auto mapping = frame_map_.find(render_frame_host);
return mapping == frame_map_.end() ? nullptr : mapping->second.get();
}
void ContentCaptureReceiverManager::RenderFrameCreated(
content::RenderFrameHost* render_frame_host) {
// The frame might not have content, but it could be parent of other frame. we
// always create the ContentCaptureReceiver for ContentCaptureSession
// purpose.
if (ContentCaptureReceiverForFrame(render_frame_host))
return;
frame_map_.insert(std::make_pair(
render_frame_host,
std::make_unique<ContentCaptureReceiver>(render_frame_host)));
}
void ContentCaptureReceiverManager::RenderFrameDeleted(
content::RenderFrameHost* render_frame_host) {
frame_map_.erase(render_frame_host);
}
void ContentCaptureReceiverManager::ReadyToCommitNavigation(
content::NavigationHandle* navigation_handle) {
auto* receiver =
ContentCaptureReceiverForFrame(navigation_handle->GetRenderFrameHost());
if (web_contents()->GetBrowserContext()->IsOffTheRecord() ||
!ShouldCapture(navigation_handle->GetURL())) {
receiver->StopCapture();
return;
}
receiver->StartCapture();
}
void ContentCaptureReceiverManager::DidCaptureContent(
ContentCaptureReceiver* content_capture_receiver,
const ContentCaptureData& data) {
// The root of |data| is frame, we need get its ancestor only.
ContentCaptureSession parent_session;
BuildContentCaptureSession(content_capture_receiver, true /* ancestor_only */,
&parent_session);
DidCaptureContent(parent_session, data);
}
void ContentCaptureReceiverManager::DidUpdateContent(
ContentCaptureReceiver* content_capture_receiver,
const ContentCaptureData& data) {
ContentCaptureSession parent_session;
BuildContentCaptureSession(content_capture_receiver, true /* ancestor_only */,
&parent_session);
DidUpdateContent(parent_session, data);
}
void ContentCaptureReceiverManager::DidRemoveContent(
ContentCaptureReceiver* content_capture_receiver,
const std::vector<int64_t>& data) {
ContentCaptureSession session;
// The |data| is a list of text content id, the session should include
// |content_capture_receiver| associated frame.
BuildContentCaptureSession(content_capture_receiver,
false /* ancestor_only */, &session);
DidRemoveContent(session, data);
}
void ContentCaptureReceiverManager::DidRemoveSession(
ContentCaptureReceiver* content_capture_receiver) {
ContentCaptureSession session;
// The session should include the removed frame that the
// |content_capture_receiver| associated with.
// We want the last reported content capture session, instead of the current
// one for the scenario like below:
// Main frame navigates to different url which has the same origin of previous
// one, it triggers the previous child frame being removed but the main RFH
// unchanged, if we use BuildContentCaptureSession() which always use the
// current URL to build session, the new session will be created for current
// main frame URL, the returned ContentCaptureSession is wrong.
if (!BuildContentCaptureSessionLastSeen(content_capture_receiver, &session))
return;
DidRemoveSession(session);
}
void ContentCaptureReceiverManager::BuildContentCaptureSession(
ContentCaptureReceiver* content_capture_receiver,
bool ancestor_only,
ContentCaptureSession* session) {
if (!ancestor_only)
session->push_back(content_capture_receiver->GetFrameContentCaptureData());
content::RenderFrameHost* rfh = content_capture_receiver->rfh()->GetParent();
while (rfh) {
ContentCaptureReceiver* receiver = ContentCaptureReceiverForFrame(rfh);
// TODO(michaelbai): Only creates ContentCaptureReceiver here, clean up the
// code in RenderFrameCreated().
if (!receiver) {
RenderFrameCreated(rfh);
receiver = ContentCaptureReceiverForFrame(rfh);
}
session->push_back(receiver->GetFrameContentCaptureData());
rfh = receiver->rfh()->GetParent();
}
}
bool ContentCaptureReceiverManager::BuildContentCaptureSessionLastSeen(
ContentCaptureReceiver* content_capture_receiver,
ContentCaptureSession* session) {
session->push_back(
content_capture_receiver->GetFrameContentCaptureDataLastSeen());
content::RenderFrameHost* rfh = content_capture_receiver->rfh()->GetParent();
while (rfh) {
ContentCaptureReceiver* receiver = ContentCaptureReceiverForFrame(rfh);
if (!receiver)
return false;
session->push_back(receiver->GetFrameContentCaptureDataLastSeen());
rfh = receiver->rfh()->GetParent();
}
return true;
}
} // namespace content_capture