diff options
author | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2019-05-24 11:40:17 +0200 |
---|---|---|
committer | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2019-05-24 12:42:11 +0000 |
commit | 5d87695f37678f96492b258bbab36486c59866b4 (patch) | |
tree | be9783bbaf04fb930c4d74ca9c00b5e7954c8bc6 /chromium/components/content_capture | |
parent | 6c11fb357ec39bf087b8b632e2b1e375aef1b38b (diff) | |
download | qtwebengine-chromium-5d87695f37678f96492b258bbab36486c59866b4.tar.gz |
BASELINE: Update Chromium to 75.0.3770.56
Change-Id: I86d2007fd27a45d5797eee06f4c9369b8b50ac4f
Reviewed-by: Alexandru Croitor <alexandru.croitor@qt.io>
Diffstat (limited to 'chromium/components/content_capture')
19 files changed, 540 insertions, 69 deletions
diff --git a/chromium/components/content_capture/android/BUILD.gn b/chromium/components/content_capture/android/BUILD.gn index 7aeb2828c61..e231abd04a8 100644 --- a/chromium/components/content_capture/android/BUILD.gn +++ b/chromium/components/content_capture/android/BUILD.gn @@ -6,6 +6,8 @@ import("//build/config/android/rules.gni") source_set("android") { sources = [ + "content_capture_controller.cc", + "content_capture_controller.h", "content_capture_features_android.cc", "content_capture_receiver_manager_android.cc", "content_capture_receiver_manager_android.h", @@ -13,6 +15,7 @@ source_set("android") { deps = [ ":jni_headers", "//components/content_capture/browser", + "//third_party/re2", ] } @@ -23,6 +26,7 @@ android_library("java") { ] java_files = [ "java/src/org/chromium/components/content_capture/ContentCaptureConsumer.java", + "java/src/org/chromium/components/content_capture/ContentCaptureController.java", "java/src/org/chromium/components/content_capture/ContentCaptureData.java", "java/src/org/chromium/components/content_capture/ContentCaptureFeatures.java", "java/src/org/chromium/components/content_capture/ContentCaptureReceiverManager.java", @@ -32,6 +36,7 @@ android_library("java") { generate_jni("jni_headers") { sources = [ + "java/src/org/chromium/components/content_capture/ContentCaptureController.java", "java/src/org/chromium/components/content_capture/ContentCaptureData.java", "java/src/org/chromium/components/content_capture/ContentCaptureFeatures.java", "java/src/org/chromium/components/content_capture/ContentCaptureReceiverManager.java", diff --git a/chromium/components/content_capture/android/DEPS b/chromium/components/content_capture/android/DEPS index fb1ebefc6e4..e63745db9fd 100644 --- a/chromium/components/content_capture/android/DEPS +++ b/chromium/components/content_capture/android/DEPS @@ -2,4 +2,5 @@ include_rules = [ "+content/public/android", "+content/public/browser", "+jni", + "+third_party/re2", ]
\ No newline at end of file diff --git a/chromium/components/content_capture/android/content_capture_controller.cc b/chromium/components/content_capture/android/content_capture_controller.cc new file mode 100644 index 00000000000..01c725a5d03 --- /dev/null +++ b/chromium/components/content_capture/android/content_capture_controller.cc @@ -0,0 +1,96 @@ +// 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/android/content_capture_controller.h" + +#include "base/android/jni_android.h" +#include "base/android/jni_array.h" +#include "base/no_destructor.h" +#include "jni/ContentCaptureController_jni.h" +#include "third_party/re2/src/re2/re2.h" + +using base::android::AppendJavaStringArrayToStringVector; +using base::android::AttachCurrentThread; +using base::android::JavaBooleanArrayToBoolVector; +using base::android::JavaParamRef; +using base::android::ScopedJavaLocalRef; + +namespace content_capture { + +// static +jlong JNI_ContentCaptureController_Init(JNIEnv* env, + const JavaParamRef<jobject>& jcaller) { + auto* controller = ContentCaptureController::Get(); + controller->SetJavaPeer(env, jcaller); + return reinterpret_cast<jlong>(controller); +} + +// static +ContentCaptureController* ContentCaptureController::Get() { + static base::NoDestructor<ContentCaptureController> s; + return s.get(); +} + +ContentCaptureController::ContentCaptureController() = default; +ContentCaptureController::~ContentCaptureController() = default; + +void ContentCaptureController::SetWhitelist( + JNIEnv* env, + const JavaParamRef<jobject>& jcaller, + const JavaParamRef<jobjectArray>& jwhitelist, + const JavaParamRef<jbooleanArray>& jisRegEx) { + DCHECK(jwhitelist && jisRegEx || !(jwhitelist || jisRegEx)); + has_whitelist_ = false; + if (!jwhitelist) + return; + std::vector<std::string> whitelist; + std::vector<bool> is_regex; + AppendJavaStringArrayToStringVector(env, jwhitelist, &whitelist); + JavaBooleanArrayToBoolVector(env, jisRegEx, &is_regex); + if (whitelist.size() != is_regex.size()) + return; + has_whitelist_ = true; + size_t index = 0; + for (auto w : whitelist) { + if (is_regex[index++]) + whitelist_regex_.push_back(std::make_unique<re2::RE2>(w)); + else + whitelist_.push_back(w); + } +} + +void ContentCaptureController::SetJavaPeer( + JNIEnv* env, + const JavaParamRef<jobject>& jcaller) { + java_ref_ = JavaObjectWeakGlobalRef(env, jcaller); +} + +bool ContentCaptureController::ShouldCapture(const GURL& url) { + if (!has_whitelist_.has_value()) { + JNIEnv* env = AttachCurrentThread(); + ScopedJavaLocalRef<jobject> obj = java_ref_.get(env); + if (obj.is_null()) + return false; + Java_ContentCaptureController_pullWhitelist(env, obj); + } + DCHECK(has_whitelist_.has_value()); + + // Everything is whitelisted. + if (!has_whitelist_.value() || !url.has_host()) + return true; + + std::string host = url.host(); + for (auto& w : whitelist_) { + if (w == host) + return true; + } + + for (auto& w : whitelist_regex_) { + if (re2::RE2::FullMatch(host, *w)) + return true; + } + return false; +} + +} // namespace content_capture diff --git a/chromium/components/content_capture/android/content_capture_controller.h b/chromium/components/content_capture/android/content_capture_controller.h new file mode 100644 index 00000000000..57b50bf8130 --- /dev/null +++ b/chromium/components/content_capture/android/content_capture_controller.h @@ -0,0 +1,49 @@ +// 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. + +#ifndef COMPONENTS_CONTENT_CAPTURE_ANDROID_CONTENT_CAPTURE_CONTROLLER_H_ +#define COMPONENTS_CONTENT_CAPTURE_ANDROID_CONTENT_CAPTURE_CONTROLLER_H_ + +#include "base/android/jni_weak_ref.h" +#include "base/android/scoped_java_ref.h" +#include "base/optional.h" +#include "url/gurl.h" + +namespace re2 { +class RE2; +} // namespace re2 + +namespace content_capture { + +// This class has one instance per process and is called by +// ContentReceiverManager to check if the given url shall be captured. +class ContentCaptureController { + public: + static ContentCaptureController* Get(); + + // Not call constructor directly, instead, uses Get(). + ContentCaptureController(); + + // Returns if the given |url| shall be captured. + bool ShouldCapture(const GURL& url); + + // The methods called by Java peer. + void SetWhitelist(JNIEnv* env, + const base::android::JavaParamRef<jobject>& jcaller, + const base::android::JavaParamRef<jobjectArray>& jwhitelist, + const base::android::JavaParamRef<jbooleanArray>& jtype); + void SetJavaPeer(JNIEnv* env, + const base::android::JavaParamRef<jobject>& jcaller); + + private: + virtual ~ContentCaptureController(); + JavaObjectWeakGlobalRef java_ref_; + base::Optional<bool> has_whitelist_; + std::vector<std::string> whitelist_; + std::vector<std::unique_ptr<re2::RE2>> whitelist_regex_; +}; + +} // namespace content_capture + +#endif // COMPONENTS_CONTENT_CAPTURE_ANDROID_CONTENT_CAPTURE_CONTROLLER_H_ diff --git a/chromium/components/content_capture/android/content_capture_receiver_manager_android.cc b/chromium/components/content_capture/android/content_capture_receiver_manager_android.cc index 522bfdef526..e514dc34618 100644 --- a/chromium/components/content_capture/android/content_capture_receiver_manager_android.cc +++ b/chromium/components/content_capture/android/content_capture_receiver_manager_android.cc @@ -9,6 +9,7 @@ #include "base/android/jni_android.h" #include "base/android/jni_array.h" #include "base/android/jni_string.h" +#include "components/content_capture/android/content_capture_controller.h" #include "content/public/browser/web_contents.h" #include "jni/ContentCaptureData_jni.h" #include "jni/ContentCaptureReceiverManager_jni.h" @@ -19,16 +20,22 @@ using base::android::JavaRef; using base::android::ScopedJavaLocalRef; using base::android::ToJavaLongArray; -void JNI_ContentCaptureReceiverManager_Init( +static ScopedJavaLocalRef<jobject> +JNI_ContentCaptureReceiverManager_CreateOrGet( JNIEnv* env, - const base::android::JavaParamRef<jobject>& jcaller, - const base::android::JavaParamRef<jobject>& jweb_contents) { - auto* web_contents = content::WebContents::FromJavaWebContents(jweb_contents); + const base::android::JavaParamRef<jobject>& jwebContents) { + auto* web_contents = content::WebContents::FromJavaWebContents(jwebContents); DCHECK(web_contents); - DCHECK(!content_capture::ContentCaptureReceiverManager::FromWebContents( - web_contents)); - content_capture::ContentCaptureReceiverManagerAndroid::Create(web_contents, - jcaller); + auto* manager = + content_capture::ContentCaptureReceiverManager::FromWebContents( + web_contents); + if (!manager) { + manager = content_capture::ContentCaptureReceiverManagerAndroid::Create( + env, web_contents); + } + return static_cast<content_capture::ContentCaptureReceiverManagerAndroid*>( + manager) + ->GetJavaObject(); } namespace content_capture { @@ -73,19 +80,22 @@ ScopedJavaLocalRef<jobjectArray> ToJavaArrayOfContentCaptureData( } // namespace ContentCaptureReceiverManagerAndroid::ContentCaptureReceiverManagerAndroid( - content::WebContents* web_contents, - const JavaRef<jobject>& jcaller) - : ContentCaptureReceiverManager(web_contents), java_ref_(jcaller) {} + JNIEnv* env, + content::WebContents* web_contents) + : ContentCaptureReceiverManager(web_contents), + java_ref_(Java_ContentCaptureReceiverManager_Constructor(env)) {} ContentCaptureReceiverManagerAndroid::~ContentCaptureReceiverManagerAndroid() = default; -void ContentCaptureReceiverManagerAndroid::Create( - content::WebContents* web_contents, - const JavaRef<jobject>& jcaller) { - if (FromWebContents(web_contents)) - return; - new ContentCaptureReceiverManagerAndroid(web_contents, jcaller); +ContentCaptureReceiverManagerAndroid* +ContentCaptureReceiverManagerAndroid::Create( + JNIEnv* env, + content::WebContents* web_contents) { + auto* manager = FromWebContents(web_contents); + if (manager) + return static_cast<ContentCaptureReceiverManagerAndroid*>(manager); + return new ContentCaptureReceiverManagerAndroid(env, web_contents); } void ContentCaptureReceiverManagerAndroid::DidCaptureContent( @@ -121,4 +131,13 @@ void ContentCaptureReceiverManagerAndroid::DidRemoveSession( env, java_ref_, ToJavaArrayOfContentCaptureData(env, session)); } +bool ContentCaptureReceiverManagerAndroid::ShouldCapture(const GURL& url) { + return ContentCaptureController::Get()->ShouldCapture(url); +} + +ScopedJavaLocalRef<jobject> +ContentCaptureReceiverManagerAndroid::GetJavaObject() { + return ScopedJavaLocalRef<jobject>(java_ref_); +} + } // namespace content_capture diff --git a/chromium/components/content_capture/android/content_capture_receiver_manager_android.h b/chromium/components/content_capture/android/content_capture_receiver_manager_android.h index 46ec64df0b6..12a9b321703 100644 --- a/chromium/components/content_capture/android/content_capture_receiver_manager_android.h +++ b/chromium/components/content_capture/android/content_capture_receiver_manager_android.h @@ -18,8 +18,9 @@ class ContentCaptureReceiverManagerAndroid : public ContentCaptureReceiverManager { public: ~ContentCaptureReceiverManagerAndroid() override; - static void Create(content::WebContents* web_contents, - const base::android::JavaRef<jobject>& jcaller); + static ContentCaptureReceiverManagerAndroid* Create( + JNIEnv* env, + content::WebContents* web_contents); void DidCaptureContent(const ContentCaptureSession& parent_session, const ContentCaptureData& data) override; @@ -27,10 +28,14 @@ class ContentCaptureReceiverManagerAndroid const std::vector<int64_t>& data) override; void DidRemoveSession(const ContentCaptureSession& session) override; + base::android::ScopedJavaLocalRef<jobject> GetJavaObject(); + + protected: + bool ShouldCapture(const GURL& url) override; + private: - ContentCaptureReceiverManagerAndroid( - content::WebContents* web_contents, - const base::android::JavaRef<jobject>& jcaller); + ContentCaptureReceiverManagerAndroid(JNIEnv* env, + content::WebContents* web_contents); base::android::ScopedJavaGlobalRef<jobject> java_ref_; }; diff --git a/chromium/components/content_capture/android/java/src/org/chromium/components/content_capture/ContentCaptureConsumer.java b/chromium/components/content_capture/android/java/src/org/chromium/components/content_capture/ContentCaptureConsumer.java index d9a458dbb12..aacf65df9e6 100644 --- a/chromium/components/content_capture/android/java/src/org/chromium/components/content_capture/ContentCaptureConsumer.java +++ b/chromium/components/content_capture/android/java/src/org/chromium/components/content_capture/ContentCaptureConsumer.java @@ -4,27 +4,52 @@ package org.chromium.components.content_capture; +import org.chromium.content_public.browser.WebContents; + /** - * This interface is for consumer to consume the captured content. + * This abstract class is for consumer to consume the captured content. */ -public interface ContentCaptureConsumer { +public abstract class ContentCaptureConsumer { + private ContentCaptureReceiverManager mManager; + public ContentCaptureConsumer(WebContents webContents) { + onWebContentsChanged(webContents); + } + /** * Invoked when the content is captured from a frame. * @param parentFrame is the parent of the frame from that the content captured. * @param contentCaptureData is the captured content tree, its root is the frame. */ - void onContentCaptured(FrameSession parentFrame, ContentCaptureData contentCaptureData); + public abstract void onContentCaptured( + FrameSession parentFrame, ContentCaptureData contentCaptureData); /** * Invoked when the session is removed * @param session is the removed frame. */ - void onSessionRemoved(FrameSession session); + public abstract void onSessionRemoved(FrameSession session); /** * Invoked when the content is removed from a frame * @param session defines the frame from that the content removed * @param removedIds are array of removed content id. */ - void onContentRemoved(FrameSession session, long[] removedIds); + public abstract void onContentRemoved(FrameSession session, long[] removedIds); + + public void onWebContentsChanged(WebContents current) { + if (!ContentCaptureFeatures.isEnabled()) return; + if (mManager != null) mManager.setContentCaptureConsumer(null); + if (current != null) { + mManager = ContentCaptureReceiverManager.createOrGet(current); + mManager.setContentCaptureConsumer(this); + } else { + mManager = null; + } + } + + public void destroy() { + if (mManager == null) return; + mManager.setContentCaptureConsumer(null); + mManager = null; + } } diff --git a/chromium/components/content_capture/android/java/src/org/chromium/components/content_capture/ContentCaptureController.java b/chromium/components/content_capture/android/java/src/org/chromium/components/content_capture/ContentCaptureController.java new file mode 100644 index 00000000000..8de95c970e0 --- /dev/null +++ b/chromium/components/content_capture/android/java/src/org/chromium/components/content_capture/ContentCaptureController.java @@ -0,0 +1,58 @@ +// 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. + +package org.chromium.components.content_capture; + +import org.chromium.base.annotations.CalledByNative; +import org.chromium.base.annotations.JNINamespace; + +/** + * The abstract class to provide the whitelist and the runtime control of if ContentCapture should + * start. + */ +@JNINamespace("content_capture") +public abstract class ContentCaptureController { + /** + * The singleton instance of ContentCaptureController, shall be set by subclass. + */ + protected static ContentCaptureController sContentCaptureController; + + private long mNativeContentCaptureController; + + public static ContentCaptureController getInstance() { + return sContentCaptureController; + } + + protected ContentCaptureController() { + mNativeContentCaptureController = nativeInit(this); + } + + /** + * @return if ContentCapture should be started for this app at all. + */ + public abstract boolean shouldStartCapture(); + + /** + * Invoked by native side to pull the whitelist, the subclass should implement this and set + * the whitelist by call setWhiteList. + */ + @CalledByNative + protected abstract void pullWhitelist(); + + /** + * Invoked by subclass to set the whitelist to native side. No whitelist (whitelist == null) + * indicates everything is whitelisted, empty whitelist (whitelist.length == 0) indicates + * nothing is whitelisted. + * + * @param whitelist the array of whitelist, it could be the hostname or the regex. + * @param isRegex to indicate that the corresponding whitelist is the regex or not. + */ + protected void setWhitelist(String[] whitelist, boolean[] isRegex) { + nativeSetWhitelist(mNativeContentCaptureController, whitelist, isRegex); + } + + private static native long nativeInit(Object contentCaptureController); + private native void nativeSetWhitelist( + long nativeContentCaptureController, String[] whitelist, boolean[] isRegex); +} diff --git a/chromium/components/content_capture/android/java/src/org/chromium/components/content_capture/ContentCaptureFeatures.java b/chromium/components/content_capture/android/java/src/org/chromium/components/content_capture/ContentCaptureFeatures.java index dc7ad565197..7400008628f 100644 --- a/chromium/components/content_capture/android/java/src/org/chromium/components/content_capture/ContentCaptureFeatures.java +++ b/chromium/components/content_capture/android/java/src/org/chromium/components/content_capture/ContentCaptureFeatures.java @@ -3,12 +3,20 @@ // found in the LICENSE file. package org.chromium.components.content_capture; +import org.chromium.base.CommandLine; + /** * The class to get if feature is enabled from native. */ public class ContentCaptureFeatures { + private static final String FLAG = "dump-captured-content-to-logcat-for-testing"; + public static boolean isEnabled() { return nativeIsEnabled(); } + + public static boolean isDumpForTestingEnabled() { + return CommandLine.getInstance().hasSwitch(FLAG); + } private static native boolean nativeIsEnabled(); } diff --git a/chromium/components/content_capture/android/java/src/org/chromium/components/content_capture/ContentCaptureReceiverManager.java b/chromium/components/content_capture/android/java/src/org/chromium/components/content_capture/ContentCaptureReceiverManager.java index c217f21c317..f6d7e8f6c0a 100644 --- a/chromium/components/content_capture/android/java/src/org/chromium/components/content_capture/ContentCaptureReceiverManager.java +++ b/chromium/components/content_capture/android/java/src/org/chromium/components/content_capture/ContentCaptureReceiverManager.java @@ -4,9 +4,6 @@ package org.chromium.components.content_capture; -import android.view.ViewGroup; - -import org.chromium.base.CommandLine; import org.chromium.base.Log; import org.chromium.base.annotations.CalledByNative; import org.chromium.content_public.browser.WebContents; @@ -18,25 +15,17 @@ import java.util.Arrays; */ public class ContentCaptureReceiverManager { private static final String TAG = "ContentCapture"; - private static final String FLAG = "dump-captured-content-to-logcat-for-testing"; private static Boolean sDump; private ContentCaptureConsumer mContentCaptureConsumer; - public static ContentCaptureReceiverManager create(WebContents webContents) { - ContentCaptureReceiverManager manager = new ContentCaptureReceiverManager(); - manager.nativeInit(webContents); - return manager; - } - - public ContentCaptureReceiverManager() { - if (sDump == null) sDump = CommandLine.getInstance().hasSwitch(FLAG); + public static ContentCaptureReceiverManager createOrGet(WebContents webContents) { + return nativeCreateOrGet(webContents); } - public void onContainerViewChanged(ViewGroup containerView) { - // Reset current consumer, the new consumer that associates with contanerView shall be set - // from setContentCaptureConsumer(). - mContentCaptureConsumer = null; + @CalledByNative + private ContentCaptureReceiverManager() { + if (sDump == null) sDump = ContentCaptureFeatures.isDumpForTestingEnabled(); } public void setContentCaptureConsumer(ContentCaptureConsumer consumer) { @@ -74,5 +63,5 @@ public class ContentCaptureReceiverManager { return frameSession; } - private native void nativeInit(WebContents webContents); + private static native ContentCaptureReceiverManager nativeCreateOrGet(WebContents webContents); } diff --git a/chromium/components/content_capture/browser/DEPS b/chromium/components/content_capture/browser/DEPS index d79a7d00f02..8d9595f2bfb 100644 --- a/chromium/components/content_capture/browser/DEPS +++ b/chromium/components/content_capture/browser/DEPS @@ -1,4 +1,5 @@ include_rules = [ "+content/public/browser", "+content/public/test", + "+third_party/blink/public/common/associated_interfaces", ] diff --git a/chromium/components/content_capture/browser/content_capture_receiver.cc b/chromium/components/content_capture/browser/content_capture_receiver.cc index 394869bd014..b0573e9500a 100644 --- a/chromium/components/content_capture/browser/content_capture_receiver.cc +++ b/chromium/components/content_capture/browser/content_capture_receiver.cc @@ -6,9 +6,11 @@ #include <utility> +#include "base/strings/utf_string_conversions.h" #include "components/content_capture/browser/content_capture_receiver_manager.h" #include "content/public/browser/render_process_host.h" #include "content/public/browser/web_contents.h" +#include "third_party/blink/public/common/associated_interfaces/associated_interface_provider.h" namespace content_capture { @@ -35,8 +37,12 @@ void ContentCaptureReceiver::DidCaptureContent(const ContentCaptureData& data, if (first_data) { // The session id of this frame isn't changed for new document navigation, // so the previous session should be terminated. - if (frame_content_capture_data_.id != 0) + // The parent frame might be captured after child, we need to check if url + // is changed, otherwise the child frame's session will be removed. + if (frame_content_capture_data_.id != 0 && + frame_content_capture_data_.value != data.value) { manager->DidRemoveSession(this); + } frame_content_capture_data_.id = id_; // Copies everything except id and children. @@ -46,6 +52,10 @@ void ContentCaptureReceiver::DidCaptureContent(const ContentCaptureData& data, // We can't avoid copy the data here, because id need to be overriden. ContentCaptureData content(data); content.id = id_; + // Always have frame URL attached, since the ContentCaptureConsumer will + // be reset once activity is resumed, URL is needed to rebuild session. + if (!first_data) + content.value = frame_content_capture_data_.value; manager->DidCaptureContent(this, content); } @@ -56,4 +66,55 @@ void ContentCaptureReceiver::DidRemoveContent( manager->DidRemoveContent(this, data); } +void ContentCaptureReceiver::StartCapture() { + if (content_capture_enabled) + return; + + if (auto& sender = GetContentCaptureSender()) { + sender->StartCapture(); + content_capture_enabled = true; + } +} + +void ContentCaptureReceiver::StopCapture() { + if (!content_capture_enabled) + return; + + if (auto& sender = GetContentCaptureSender()) { + sender->StopCapture(); + content_capture_enabled = false; + } +} + +const mojom::ContentCaptureSenderAssociatedPtr& +ContentCaptureReceiver::GetContentCaptureSender() { + if (!content_capture_sender_) { + rfh_->GetRemoteAssociatedInterfaces()->GetInterface( + mojo::MakeRequest(&content_capture_sender_)); + } + return content_capture_sender_; +} + +const ContentCaptureData& ContentCaptureReceiver::GetFrameContentCaptureData() { + base::string16 url = base::UTF8ToUTF16(rfh_->GetLastCommittedURL().spec()); + if (url == frame_content_capture_data_.value) + return frame_content_capture_data_; + + bool should_remove_session = frame_content_capture_data_.id != 0; + frame_content_capture_data_.id = id_; + frame_content_capture_data_.value = url; + const base::Optional<gfx::Size>& size = rfh_->GetFrameSize(); + if (size.has_value()) + frame_content_capture_data_.bounds = gfx::Rect(size.value()); + + // frame_content_capture_data_ must be set to new value before removing + // sesesion, otherwises, it causes infinite loop. + if (should_remove_session) { + auto* manager = ContentCaptureReceiverManager::FromWebContents( + content::WebContents::FromRenderFrameHost(rfh_)); + manager->DidRemoveSession(this); + } + return frame_content_capture_data_; +} + } // namespace content_capture diff --git a/chromium/components/content_capture/browser/content_capture_receiver.h b/chromium/components/content_capture/browser/content_capture_receiver.h index d745b072423..f3a6522ac9a 100644 --- a/chromium/components/content_capture/browser/content_capture_receiver.h +++ b/chromium/components/content_capture/browser/content_capture_receiver.h @@ -33,15 +33,17 @@ class ContentCaptureReceiver : public mojom::ContentCaptureReceiver { void DidCaptureContent(const ContentCaptureData& data, bool first_data) override; void DidRemoveContent(const std::vector<int64_t>& data) override; + void StartCapture(); + void StopCapture(); content::RenderFrameHost* rfh() const { return rfh_; } // Return ContentCaptureData of the associated frame. - const ContentCaptureData& frame_content_capture_data() const { - return frame_content_capture_data_; - } + const ContentCaptureData& GetFrameContentCaptureData(); private: + const mojom::ContentCaptureSenderAssociatedPtr& GetContentCaptureSender(); + mojo::AssociatedBinding<mojom::ContentCaptureReceiver> bindings_; content::RenderFrameHost* rfh_; ContentCaptureData frame_content_capture_data_; @@ -53,7 +55,8 @@ class ContentCaptureReceiver : public mojom::ContentCaptureReceiver { // frame's; if the Id is generated in sender, the // ContentCaptureReceiverManager can't get parent frame id in both cases. int64_t id_; - + bool content_capture_enabled = false; + mojom::ContentCaptureSenderAssociatedPtr content_capture_sender_ = nullptr; DISALLOW_COPY_AND_ASSIGN(ContentCaptureReceiver); }; diff --git a/chromium/components/content_capture/browser/content_capture_receiver_manager.cc b/chromium/components/content_capture/browser/content_capture_receiver_manager.cc index 57e7702c39d..353ef88bd78 100644 --- a/chromium/components/content_capture/browser/content_capture_receiver_manager.cc +++ b/chromium/components/content_capture/browser/content_capture_receiver_manager.cc @@ -7,6 +7,7 @@ #include <utility> #include "components/content_capture/browser/content_capture_receiver.h" +#include "content/public/browser/navigation_handle.h" #include "content/public/browser/web_contents.h" namespace content_capture { @@ -30,12 +31,14 @@ ContentCaptureReceiverManager::ContentCaptureReceiverManager( 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) { @@ -79,13 +82,23 @@ void ContentCaptureReceiverManager::RenderFrameDeleted( frame_map_.erase(render_frame_host); } +void ContentCaptureReceiverManager::ReadyToCommitNavigation( + content::NavigationHandle* navigation_handle) { + auto* receiver = + ContentCaptureReceiverForFrame(navigation_handle->GetRenderFrameHost()); + if (ShouldCapture(navigation_handle->GetURL())) + receiver->StartCapture(); + else + receiver->StopCapture(); +} + 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); + BuildContentCaptureSession(content_capture_receiver, true /* ancestor_only */, + &parent_session); DidCaptureContent(parent_session, data); } @@ -95,7 +108,7 @@ void ContentCaptureReceiverManager::DidRemoveContent( ContentCaptureSession session; // The |data| is a list of text content id, the session should include // |content_capture_receiver| associated frame. - BuildContentCaptureSession(*content_capture_receiver, + BuildContentCaptureSession(content_capture_receiver, false /* ancestor_only */, &session); DidRemoveContent(session, data); } @@ -105,19 +118,19 @@ void ContentCaptureReceiverManager::DidRemoveSession( ContentCaptureSession session; // The session should include the removed frame that the // |content_capture_receiver| associated with. - BuildContentCaptureSession(*content_capture_receiver, + BuildContentCaptureSession(content_capture_receiver, false /* ancestor_only */, &session); DidRemoveSession(session); } void ContentCaptureReceiverManager::BuildContentCaptureSession( - const ContentCaptureReceiver& content_capture_receiver, + ContentCaptureReceiver* content_capture_receiver, bool ancestor_only, ContentCaptureSession* session) { if (!ancestor_only) - session->push_back(content_capture_receiver.frame_content_capture_data()); + session->push_back(content_capture_receiver->GetFrameContentCaptureData()); - content::RenderFrameHost* rfh = content_capture_receiver.rfh()->GetParent(); + content::RenderFrameHost* rfh = content_capture_receiver->rfh()->GetParent(); while (rfh) { ContentCaptureReceiver* receiver = ContentCaptureReceiverForFrame(rfh); // TODO(michaelbai): Only creates ContentCaptureReceiver here, clean up the @@ -126,7 +139,7 @@ void ContentCaptureReceiverManager::BuildContentCaptureSession( RenderFrameCreated(rfh); receiver = ContentCaptureReceiverForFrame(rfh); } - session->push_back(receiver->frame_content_capture_data()); + session->push_back(receiver->GetFrameContentCaptureData()); rfh = receiver->rfh()->GetParent(); } } diff --git a/chromium/components/content_capture/browser/content_capture_receiver_manager.h b/chromium/components/content_capture/browser/content_capture_receiver_manager.h index 4ddbc8b1d80..2e16c4da0cc 100644 --- a/chromium/components/content_capture/browser/content_capture_receiver_manager.h +++ b/chromium/components/content_capture/browser/content_capture_receiver_manager.h @@ -50,6 +50,8 @@ class ContentCaptureReceiverManager : public content::WebContentsObserver, // content::WebContentsObserver: void RenderFrameCreated(content::RenderFrameHost* render_frame_host) override; void RenderFrameDeleted(content::RenderFrameHost* render_frame_host) override; + void ReadyToCommitNavigation( + content::NavigationHandle* navigation_handle) override; size_t GetFrameMapSizeForTesting() const { return frame_map_.size(); } @@ -66,13 +68,15 @@ class ContentCaptureReceiverManager : public content::WebContentsObserver, // Invoked when the given |session| was removed. virtual void DidRemoveSession(const ContentCaptureSession& session) = 0; + virtual bool ShouldCapture(const GURL& url) = 0; + private: friend class ContentCaptureReceiverManagerHelper; // Builds ContentCaptureSession and returns in |session|, |ancestor_only| // specifies if only ancestor should be returned in |session|. void BuildContentCaptureSession( - const ContentCaptureReceiver& content_capture_receiver, + ContentCaptureReceiver* content_capture_receiver, bool ancestor_only, ContentCaptureSession* session); diff --git a/chromium/components/content_capture/browser/content_capture_receiver_test.cc b/chromium/components/content_capture/browser/content_capture_receiver_test.cc index 9ada1eb1209..7c411e0795b 100644 --- a/chromium/components/content_capture/browser/content_capture_receiver_test.cc +++ b/chromium/components/content_capture/browser/content_capture_receiver_test.cc @@ -18,6 +18,10 @@ namespace content_capture { namespace { +static const char kMainFrameUrl[] = "http://foo.com/main.html"; +static const char kMainFrameUrl2[] = "http://foo.com/2.html"; +static const char kChildFrameUrl[] = "http://foo.org/child.html"; + // Fake ContentCaptureSender to call ContentCaptureReceiver mojom interface. class FakeContentCaptureSender { public: @@ -70,6 +74,8 @@ class ContentCaptureReceiverManagerHelper removed_session_ = data; } + bool ShouldCapture(const GURL& url) override { return false; } + const ContentCaptureSession& parent_session() const { return parent_session_; } @@ -102,7 +108,7 @@ class ContentCaptureReceiverTest : public content::RenderViewHostTestHarness { ContentCaptureReceiverManager::FromWebContents(web_contents())); // This needed to keep the WebContentsObserverSanityChecker checks happy for // when AppendChild is called. - NavigateAndCommit(GURL("about:blank")); + NavigateAndCommit(GURL(kMainFrameUrl)); content_capture_sender_ = std::make_unique<FakeContentCaptureSender>(); main_frame_ = web_contents()->GetMainFrame(); // Binds sender with receiver. @@ -115,12 +121,26 @@ class ContentCaptureReceiverTest : public content::RenderViewHostTestHarness { child.value = base::ASCIIToUTF16("Hello"); child.bounds = gfx::Rect(5, 5, 5, 5); // No need to set id in sender. - test_data_.value = base::ASCIIToUTF16("http://foo.com/bar"); + test_data_.value = base::ASCIIToUTF16(kMainFrameUrl); test_data_.bounds = gfx::Rect(10, 10); test_data_.children.push_back(child); - test_data2_.value = base::ASCIIToUTF16("http://foo.org/bar"); + test_data2_.value = base::ASCIIToUTF16(kChildFrameUrl); test_data2_.bounds = gfx::Rect(10, 10); test_data2_.children.push_back(child); + // Update to test_data_. + ContentCaptureData child2; + // Have the unique id for text content. + child2.id = 3; + child2.value = base::ASCIIToUTF16("World"); + child2.bounds = gfx::Rect(5, 10, 5, 5); + test_data_update_.value = base::ASCIIToUTF16(kMainFrameUrl); + test_data_update_.bounds = gfx::Rect(10, 10); + test_data_update_.children.push_back(child2); + } + + void NavigateMainFrame(const GURL& url) { + NavigateAndCommit(url); + main_frame_ = web_contents()->GetMainFrame(); } void SetupChildFrame() { @@ -143,6 +163,9 @@ class ContentCaptureReceiverTest : public content::RenderViewHostTestHarness { const ContentCaptureData& test_data() const { return test_data_; } const ContentCaptureData& test_data2() const { return test_data2_; } + const ContentCaptureData& test_data_update() const { + return test_data_update_; + } const std::vector<int64_t>& expected_removed_ids() const { return expected_removed_ids_; } @@ -163,6 +186,14 @@ class ContentCaptureReceiverTest : public content::RenderViewHostTestHarness { return expected; } + ContentCaptureData GetExpectedTestDataUpdate(bool main_frame) const { + ContentCaptureData expected(test_data_update_); + // Replaces the id with expected id. + expected.id = ContentCaptureReceiver::GetIdFrom(main_frame ? main_frame_ + : child_frame_); + return expected; + } + ContentCaptureReceiverManagerHelper* content_capture_receiver_manager_helper() const { return content_capture_receiver_manager_helper_; @@ -175,6 +206,7 @@ class ContentCaptureReceiverTest : public content::RenderViewHostTestHarness { EXPECT_EQ(expected[i].id, result[i].id); EXPECT_EQ(expected[i].value, result[i].value); EXPECT_EQ(expected[i].bounds, result[i].bounds); + EXPECT_TRUE(result[i].children.empty()); } } @@ -213,6 +245,7 @@ class ContentCaptureReceiverTest : public content::RenderViewHostTestHarness { content::RenderFrameHost* child_frame_ = nullptr; ContentCaptureData test_data_; ContentCaptureData test_data2_; + ContentCaptureData test_data_update_; // Expected removed Ids. std::vector<int64_t> expected_removed_ids_{2}; }; @@ -237,14 +270,14 @@ TEST_F(ContentCaptureReceiverTest, DidCaptureContentWithUpdate) { EXPECT_EQ(GetExpectedTestData(true /* main_frame */), content_capture_receiver_manager_helper()->captured_data()); // Simulates to update the content within the same document. - DidCaptureContent(test_data2(), false /* first_data */); + DidCaptureContent(test_data_update(), false /* first_data */); // Verifies to get test_data2() with correct frame content id. EXPECT_TRUE( content_capture_receiver_manager_helper()->parent_session().empty()); // Verifies that the sesssion isn't removed. EXPECT_TRUE( content_capture_receiver_manager_helper()->removed_session().empty()); - EXPECT_EQ(GetExpectedTestData2(true /* main_frame */), + EXPECT_EQ(GetExpectedTestDataUpdate(true /* main_frame */), content_capture_receiver_manager_helper()->captured_data()); } @@ -324,6 +357,69 @@ TEST_F(ContentCaptureReceiverTest, ChildFrameDidCaptureContent) { content_capture_receiver_manager_helper()->captured_data()); } +TEST_F(ContentCaptureReceiverTest, ChildFrameCaptureContentFirst) { + // Simulate add child frame. + SetupChildFrame(); + // Simulate to capture the content from child frame. + DidCaptureContentForChildFrame(test_data2(), true /* first_data */); + // Verifies that the parent_session was set correctly. + EXPECT_FALSE( + content_capture_receiver_manager_helper()->parent_session().empty()); + + ContentCaptureData data = GetExpectedTestData(true /* main_frame */); + // Currently, there is no way to fake frame size, set it to 0. + data.bounds = gfx::Rect(); + std::vector<ContentCaptureData> expected{data}; + + VerifySession(expected, + content_capture_receiver_manager_helper()->parent_session()); + EXPECT_TRUE( + content_capture_receiver_manager_helper()->removed_session().empty()); + // Verifies that we receive the correct content from child frame. + EXPECT_EQ(GetExpectedTestData2(false /* main_frame */), + content_capture_receiver_manager_helper()->captured_data()); + + // When main frame navigates to same url, the parent session will not change. + NavigateMainFrame(GURL(kMainFrameUrl)); + SetupChildFrame(); + DidCaptureContentForChildFrame(test_data2(), true /* first_data */); + VerifySession(expected, + content_capture_receiver_manager_helper()->parent_session()); + EXPECT_TRUE( + content_capture_receiver_manager_helper()->removed_session().empty()); + + // When main frame navigates to same domain, the parent session will change. + NavigateMainFrame(GURL(kMainFrameUrl2)); + SetupChildFrame(); + DidCaptureContentForChildFrame(test_data2(), true /* first_data */); + + // Intentionally reuse the data.id from previous result, so we know navigating + // to same domain didn't create new ContentCaptureReceiver when call + // VerifySession(), otherwise, we can't test the code to handle the navigation + // in ContentCaptureReceiver. + data.value = base::ASCIIToUTF16(kMainFrameUrl2); + // Currently, there is no way to fake frame size, set it to 0. + data.bounds = gfx::Rect(); + expected.clear(); + expected.push_back(data); + VerifySession(expected, + content_capture_receiver_manager_helper()->parent_session()); + + // When main frame navigates to different domain, the parent session will + // change. + NavigateMainFrame(GURL(kChildFrameUrl)); + SetupChildFrame(); + DidCaptureContentForChildFrame(test_data2(), true /* first_data */); + + data = GetExpectedTestData2(true /* main_frame */); + // Currently, there is no way to fake frame size, set it to 0. + data.bounds = gfx::Rect(); + expected.clear(); + expected.push_back(data); + VerifySession(expected, + content_capture_receiver_manager_helper()->parent_session()); +} + class ContentCaptureReceiverMultipleFrameTest : public ContentCaptureReceiverTest { public: diff --git a/chromium/components/content_capture/common/content_capture.mojom b/chromium/components/content_capture/common/content_capture.mojom index 5bb2ff35dc1..a6591428e94 100644 --- a/chromium/components/content_capture/common/content_capture.mojom +++ b/chromium/components/content_capture/common/content_capture.mojom @@ -15,4 +15,11 @@ interface ContentCaptureReceiver { // Invoked to notify that a list of content |ids| has been removed. DidRemoveContent(array<int64> ids); -};
\ No newline at end of file +}; + +// This interface has one instance per RenderFrame in renderer process. +interface ContentCaptureSender { + // Invoked to start/stop ContentCapture, it is stopped by default. + StartCapture(); + StopCapture(); +}; diff --git a/chromium/components/content_capture/renderer/content_capture_sender.cc b/chromium/components/content_capture/renderer/content_capture_sender.cc index 989ec63b723..e3730c7533d 100644 --- a/chromium/components/content_capture/renderer/content_capture_sender.cc +++ b/chromium/components/content_capture/renderer/content_capture_sender.cc @@ -9,19 +9,28 @@ #include "components/content_capture/common/content_capture_features.h" #include "content/public/renderer/render_frame.h" #include "third_party/blink/public/common/associated_interfaces/associated_interface_provider.h" +#include "third_party/blink/public/common/associated_interfaces/associated_interface_registry.h" #include "third_party/blink/public/web/web_content_holder.h" #include "third_party/blink/public/web/web_document.h" #include "third_party/blink/public/web/web_local_frame.h" namespace content_capture { -ContentCaptureSender::ContentCaptureSender(content::RenderFrame* render_frame) - : content::RenderFrameObserver(render_frame) { - render_frame->GetWebFrame()->SetContentCaptureClient(this); +ContentCaptureSender::ContentCaptureSender( + content::RenderFrame* render_frame, + blink::AssociatedInterfaceRegistry* registry) + : content::RenderFrameObserver(render_frame), binding_(this) { + registry->AddInterface(base::BindRepeating(&ContentCaptureSender::BindRequest, + base::Unretained(this))); } ContentCaptureSender::~ContentCaptureSender() {} +void ContentCaptureSender::BindRequest( + mojom::ContentCaptureSenderAssociatedRequest request) { + binding_.Bind(std::move(request)); +} + cc::NodeHolder::Type ContentCaptureSender::GetNodeHolderType() const { if (content_capture::features::ShouldUseNodeID()) return cc::NodeHolder::Type::kID; @@ -64,6 +73,14 @@ void ContentCaptureSender::DidRemoveContent(const std::vector<int64_t>& data) { GetContentCaptureReceiver()->DidRemoveContent(data); } +void ContentCaptureSender::StartCapture() { + render_frame()->GetWebFrame()->SetContentCaptureClient(this); +} + +void ContentCaptureSender::StopCapture() { + render_frame()->GetWebFrame()->SetContentCaptureClient(nullptr); +} + void ContentCaptureSender::OnDestruct() { base::ThreadTaskRunnerHandle::Get()->DeleteSoon(FROM_HERE, this); } diff --git a/chromium/components/content_capture/renderer/content_capture_sender.h b/chromium/components/content_capture/renderer/content_capture_sender.h index 120cde15696..5d653a0ade4 100644 --- a/chromium/components/content_capture/renderer/content_capture_sender.h +++ b/chromium/components/content_capture/renderer/content_capture_sender.h @@ -9,8 +9,13 @@ #include "components/content_capture/common/content_capture.mojom.h" #include "content/public/renderer/render_frame_observer.h" +#include "mojo/public/cpp/bindings/associated_binding.h" #include "third_party/blink/public/web/web_content_capture_client.h" +namespace blink { +class AssociatedInterfaceRegistry; +} + namespace content_capture { struct ContentCaptureData; @@ -21,11 +26,15 @@ struct ContentCaptureData; // the ContentCapture in blink by setting WebContentCaptureClient to // WebLocalFrame. class ContentCaptureSender : public content::RenderFrameObserver, - public blink::WebContentCaptureClient { + public blink::WebContentCaptureClient, + public mojom::ContentCaptureSender { public: - explicit ContentCaptureSender(content::RenderFrame* render_frame); + explicit ContentCaptureSender(content::RenderFrame* render_frame, + blink::AssociatedInterfaceRegistry* registry); ~ContentCaptureSender() override; + void BindRequest(mojom::ContentCaptureSenderAssociatedRequest request); + // blink::WebContentCaptureClient: cc::NodeHolder::Type GetNodeHolderType() const override; void GetTaskTimingParameters(base::TimeDelta& short_delay, @@ -35,6 +44,10 @@ class ContentCaptureSender : public content::RenderFrameObserver, bool first_data) override; void DidRemoveContent(const std::vector<int64_t>& data) override; + // mojom::ContentCaptureSender + void StartCapture() override; + void StopCapture() override; + // content::RenderFrameObserver: void OnDestruct() override; @@ -44,6 +57,7 @@ class ContentCaptureSender : public content::RenderFrameObserver, mojom::ContentCaptureReceiverAssociatedPtr content_capture_receiver_ = nullptr; + mojo::AssociatedBinding<mojom::ContentCaptureSender> binding_; DISALLOW_COPY_AND_ASSIGN(ContentCaptureSender); }; |