ContentCapture: Update favicon to consumer
This patch forwards favicon to ContentCaptureConsumer, also adds new test support class to help on variours test cases. Bug: 1168827 Change-Id: I8ee228876061a834c6a816af917e489e904dd796 Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2990495 Reviewed-by: Xianzhu Wang <wangxianzhu@chromium.org> Reviewed-by: Kentaro Hara <haraken@chromium.org> Reviewed-by: Colin Blundell <blundell@chromium.org> Commit-Queue: Michael Bai <michaelbai@chromium.org> Cr-Commit-Position: refs/heads/master@{#896994}
This commit is contained in:

committed by
Chromium LUCI CQ

parent
e604a20d92
commit
343ed83408
android_webview
components/content_capture
android
DEPSonscreen_content_provider_android.cconscreen_content_provider_android.h
java
src
junit
src
org
chromium
components
content_capture
test_support
browser
weblayer/browser/java/org/chromium/weblayer_private/test
@ -11,6 +11,8 @@ import android.view.View;
|
||||
import androidx.test.filters.LargeTest;
|
||||
import androidx.test.filters.SmallTest;
|
||||
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONTokener;
|
||||
import org.junit.After;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Before;
|
||||
@ -26,11 +28,13 @@ import org.chromium.components.content_capture.ContentCaptureConsumer;
|
||||
import org.chromium.components.content_capture.ContentCaptureData;
|
||||
import org.chromium.components.content_capture.ContentCaptureDataBase;
|
||||
import org.chromium.components.content_capture.ContentCaptureFrame;
|
||||
import org.chromium.components.content_capture.ContentCaptureTestSupport;
|
||||
import org.chromium.components.content_capture.FrameSession;
|
||||
import org.chromium.components.content_capture.OnscreenContentProvider;
|
||||
import org.chromium.components.content_capture.UrlAllowlist;
|
||||
import org.chromium.content_public.browser.test.util.TestThreadUtils;
|
||||
import org.chromium.net.test.util.TestWebServer;
|
||||
import org.chromium.url.GURL;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
@ -55,6 +59,7 @@ public class AwContentCaptureTest {
|
||||
public static final int CONTENT_REMOVED = 3;
|
||||
public static final int SESSION_REMOVED = 4;
|
||||
public static final int TITLE_UPDATED = 5;
|
||||
public static final int FAVICON_UPDATED = 6;
|
||||
|
||||
public TestAwContentCaptureConsumer() {
|
||||
mCapturedContentIds = new HashSet<Long>();
|
||||
@ -113,6 +118,13 @@ public class AwContentCaptureTest {
|
||||
mCallbackHelper.notifyCalled();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFaviconUpdated(ContentCaptureFrame contentCaptureFrame) {
|
||||
mFaviconUpdatedFrame = contentCaptureFrame;
|
||||
mCallbacks.add(FAVICON_UPDATED);
|
||||
mCallbackHelper.notifyCalled();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shouldCapture(String[] urls) {
|
||||
if (mUrlAllowlist == null) return true;
|
||||
@ -131,6 +143,10 @@ public class AwContentCaptureTest {
|
||||
return mUpdatedContent;
|
||||
}
|
||||
|
||||
public ContentCaptureFrame getFaviconUpdatedFrame() {
|
||||
return mFaviconUpdatedFrame;
|
||||
}
|
||||
|
||||
public FrameSession getCurrentFrameSession() {
|
||||
return mCurrentFrameSession;
|
||||
}
|
||||
@ -191,6 +207,7 @@ public class AwContentCaptureTest {
|
||||
private volatile FrameSession mRemovedSession;
|
||||
private volatile long[] mRemovedIds;
|
||||
private volatile ContentCaptureFrame mTitleUpdatedFrame;
|
||||
private volatile ContentCaptureFrame mFaviconUpdatedFrame;
|
||||
private volatile ArrayList<Integer> mCallbacks = new ArrayList<Integer>();
|
||||
|
||||
private CallbackHelper mCallbackHelper = new CallbackHelper();
|
||||
@ -402,13 +419,13 @@ public class AwContentCaptureTest {
|
||||
ContentCaptureFrame c = data;
|
||||
Rect r = c.getBounds();
|
||||
session.add(ContentCaptureFrame.createContentCaptureFrame(
|
||||
c.getId(), c.getUrl(), r.left, r.top, r.width(), r.height(), null));
|
||||
c.getId(), c.getUrl(), r.left, r.top, r.width(), r.height(), null, null));
|
||||
return session;
|
||||
}
|
||||
|
||||
private FrameSession createFrameSession(String url) {
|
||||
FrameSession session = new FrameSession(1);
|
||||
session.add(ContentCaptureFrame.createContentCaptureFrame(0, url, 0, 0, 0, 0, null));
|
||||
session.add(ContentCaptureFrame.createContentCaptureFrame(0, url, 0, 0, 0, 0, null, null));
|
||||
return session;
|
||||
}
|
||||
|
||||
@ -830,4 +847,146 @@ public class AwContentCaptureTest {
|
||||
runScript("document.title='hello world'");
|
||||
}, toIntArray(TestAwContentCaptureConsumer.TITLE_UPDATED));
|
||||
}
|
||||
|
||||
@Test
|
||||
@SmallTest
|
||||
@Feature({"AndroidWebView"})
|
||||
public void testFaviconRetrievedAtFirstContentCapture() throws Throwable {
|
||||
// Starts with a empty document, so no content shall be streamed.
|
||||
final String response = "<html><head>"
|
||||
+ "<link rel=\"apple-touch-icon\" href=\"image.png\">"
|
||||
+ "</head><body>"
|
||||
+ "<p id='place_holder'></p>"
|
||||
+ "</body></html>";
|
||||
final String url = mWebServer.setResponse(MAIN_FRAME_FILE, response, null);
|
||||
int count = mContentsClient.getTouchIconHelper().getCallCount();
|
||||
loadUrlSync(url);
|
||||
// To simulate favicon being retrieved by WebContents before first Content is streamed,
|
||||
// wait favicon being available in WebContents, then insert the text to document.
|
||||
mContentsClient.getTouchIconHelper().waitForCallback(count);
|
||||
Assert.assertEquals(1, mContentsClient.getTouchIconHelper().getTouchIconsCount());
|
||||
runAndVerifyCallbacks(() -> {
|
||||
runScript("document.getElementById('place_holder').innerHTML = 'world';");
|
||||
}, toIntArray(TestAwContentCaptureConsumer.CONTENT_CAPTURED));
|
||||
GURL gurl = new GURL(url);
|
||||
String origin = gurl.getOrigin().getSpec();
|
||||
// Blink attaches the default favicon if it is not specified in page.
|
||||
final String expectedJson = String.format("["
|
||||
+ " {"
|
||||
+ " \"type\" : \"favicon\","
|
||||
+ " \"url\" : \"%sfavicon.ico\""
|
||||
+ " },"
|
||||
+ " {"
|
||||
+ " \"type\" : \"touch icon\","
|
||||
+ " \"url\" : \"%simage.png\""
|
||||
+ " }"
|
||||
+ "]",
|
||||
origin, origin);
|
||||
verifyFaviconResult(expectedJson, mConsumer.getCapturedContent().getFavicon());
|
||||
}
|
||||
|
||||
@Test
|
||||
@SmallTest
|
||||
@Feature({"AndroidWebView"})
|
||||
public void testFaviconRetrievedAfterFirstContentCapture() throws Throwable {
|
||||
final String response = "<html><head'>"
|
||||
+ "</head><body>"
|
||||
+ "<p id='place_holder'>world</p>"
|
||||
+ "</body></html>";
|
||||
final String url = mWebServer.setResponse(MAIN_FRAME_FILE, response, null);
|
||||
// Direct ContentCaptureReveiver and OnscreenContentProvider not to get the favicon
|
||||
// from Webontents, because there is no way to control the time of favicon update.
|
||||
TestThreadUtils.runOnUiThreadBlocking(
|
||||
() -> { ContentCaptureTestSupport.disableGetFaviconFromWebContents(); });
|
||||
runAndVerifyCallbacks(() -> {
|
||||
loadUrlSync(url);
|
||||
}, toIntArray(TestAwContentCaptureConsumer.CONTENT_CAPTURED));
|
||||
GURL gurl = new GURL(url);
|
||||
String origin = gurl.getOrigin().getSpec();
|
||||
final String expectedJson = String.format("["
|
||||
+ " {"
|
||||
+ " \"type\" : \"favicon\","
|
||||
+ " \"url\" : \"%sfavicon.ico\""
|
||||
+ " },"
|
||||
+ " {"
|
||||
+ " \"type\" : \"touch icon\","
|
||||
+ " \"url\" : \"%simage.png\""
|
||||
+ " }"
|
||||
+ "]",
|
||||
origin, origin);
|
||||
// Simulates favicon update by calling OnscreenContentProvider's test method.
|
||||
runAndVerifyCallbacks(() -> {
|
||||
TestThreadUtils.runOnUiThreadBlocking(() -> {
|
||||
ContentCaptureTestSupport.simulateDidUpdateFaviconURL(
|
||||
mAwContents.getWebContents(), expectedJson);
|
||||
});
|
||||
}, toIntArray(TestAwContentCaptureConsumer.FAVICON_UPDATED));
|
||||
verifyFaviconResult(expectedJson, mConsumer.getFaviconUpdatedFrame().getFavicon());
|
||||
}
|
||||
|
||||
@Test
|
||||
@SmallTest
|
||||
@Feature({"AndroidWebView"})
|
||||
public void testFavicon() throws Throwable {
|
||||
final String response = "<html><head>"
|
||||
+ "<link rel=icon href=mac.icns sizes=\"128x128 512x512 8192x8192 32768x32768\">"
|
||||
+ "</head><body>"
|
||||
+ "<p>world</p>"
|
||||
+ "</body></html>";
|
||||
final String url = mWebServer.setResponse(MAIN_FRAME_FILE, response, null);
|
||||
|
||||
runAndVerifyCallbacks(() -> {
|
||||
loadUrlSync(url);
|
||||
}, toIntArray(TestAwContentCaptureConsumer.CONTENT_CAPTURED));
|
||||
Long frameId = null;
|
||||
Set<Long> capturedContentIds = null;
|
||||
// Verify only on-screen content is captured.
|
||||
verifyCapturedContent(null, frameId, url, null, toStringSet("world"), capturedContentIds,
|
||||
mConsumer.getParentFrame(), mConsumer.getCapturedContent());
|
||||
// The favicon could be from either first capture or FaviconUpdated callback.
|
||||
String favicon = mConsumer.getCapturedContent().getFavicon();
|
||||
if (favicon == null) {
|
||||
// Update the title and verify the result.
|
||||
runAndVerifyCallbacks(
|
||||
() -> {}, toIntArray(TestAwContentCaptureConsumer.FAVICON_UPDATED));
|
||||
favicon = mConsumer.getFaviconUpdatedFrame().getFavicon();
|
||||
}
|
||||
GURL gurl = new GURL(url);
|
||||
String origin = gurl.getOrigin().getSpec();
|
||||
final String expectedJson = String.format("["
|
||||
+ " {"
|
||||
+ " \"sizes\" : "
|
||||
+ " ["
|
||||
+ " {"
|
||||
+ " \"height\" : 128,"
|
||||
+ " \"width\" : 128"
|
||||
+ " },"
|
||||
+ " {"
|
||||
+ " \"height\" : 512,"
|
||||
+ " \"width\" : 512"
|
||||
+ " },"
|
||||
+ " {"
|
||||
+ " \"height\" : 8192,"
|
||||
+ " \"width\" : 8192"
|
||||
+ " },"
|
||||
+ " {"
|
||||
+ " \"height\" : 32768,"
|
||||
+ " \"width\" : 32768"
|
||||
+ " }"
|
||||
+ " ],"
|
||||
+ " \"type\" : \"favicon\","
|
||||
+ " \"url\" : \"%smac.icns\""
|
||||
+ " }"
|
||||
+ " ]",
|
||||
origin);
|
||||
verifyFaviconResult(expectedJson, favicon);
|
||||
}
|
||||
|
||||
private static void verifyFaviconResult(String expectedJson, String resultJson)
|
||||
throws Throwable {
|
||||
JSONArray expectedResult = (JSONArray) new JSONTokener(expectedJson).nextValue();
|
||||
JSONArray actualResult = (JSONArray) new JSONTokener(resultJson).nextValue();
|
||||
Assert.assertEquals(String.format("Actual:%s\n Expected:\n%s\n", resultJson, expectedJson),
|
||||
expectedResult.toString(), actualResult.toString());
|
||||
}
|
||||
}
|
||||
|
@ -60,6 +60,7 @@ android_apk("webview_instrumentation_apk") {
|
||||
"//base:base_java",
|
||||
"//base:base_java_test_support",
|
||||
"//components/android_autofill/browser/test_support:component_autofill_provider_java_test_support",
|
||||
"//components/content_capture/android/test_support:java",
|
||||
"//components/embedder_support/android:util_java",
|
||||
"//components/heap_profiling/multi_process:heap_profiling_java_test_support",
|
||||
"//components/policy/android:policy_java_test_support",
|
||||
@ -162,6 +163,7 @@ shared_library("libstandalonelibwebviewchromium") {
|
||||
"//android_webview/public",
|
||||
"//base",
|
||||
"//components/android_autofill/browser/test_support:component_autofill_provider_native_test_support",
|
||||
"//components/content_capture/android/test_support",
|
||||
"//components/heap_profiling/multi_process:test_support",
|
||||
"//content/public/test/android:content_native_test_support",
|
||||
"//gpu/vulkan",
|
||||
@ -202,6 +204,7 @@ instrumentation_test_apk("webview_instrumentation_test_apk") {
|
||||
"//components/component_updater/android:component_provider_service_aidl_java",
|
||||
"//components/component_updater/android:embedded_component_loader_java",
|
||||
"//components/content_capture/android:java",
|
||||
"//components/content_capture/android/test_support:java",
|
||||
"//components/embedder_support/android:util_java",
|
||||
"//components/embedder_support/android:web_contents_delegate_java",
|
||||
"//components/heap_profiling/multi_process:heap_profiling_java_test_support",
|
||||
|
@ -2,5 +2,6 @@ include_rules = [
|
||||
"+components/content_capture/android/jni_headers",
|
||||
"+content/public/android",
|
||||
"+content/public/browser",
|
||||
"+third_party/blink/public/mojom/favicon",
|
||||
"+third_party/re2",
|
||||
]
|
||||
]
|
||||
|
@ -44,6 +44,12 @@ public interface ContentCaptureConsumer {
|
||||
*/
|
||||
void onTitleUpdated(ContentCaptureFrame mainFrame);
|
||||
|
||||
/**
|
||||
* Invoked when the favicon is updated.
|
||||
* @param mainFrame the frame whose favicon is updated.
|
||||
*/
|
||||
void onFaviconUpdated(ContentCaptureFrame mainFrame);
|
||||
|
||||
/**
|
||||
* @param urls
|
||||
* @return if the urls shall be captured.
|
||||
|
@ -16,19 +16,21 @@ import org.chromium.base.annotations.CalledByNative;
|
||||
public class ContentCaptureFrame extends ContentCaptureDataBase {
|
||||
private final String mUrl;
|
||||
private final String mTitle;
|
||||
private final String mFavicon;
|
||||
|
||||
@CalledByNative
|
||||
@VisibleForTesting
|
||||
public static ContentCaptureFrame createContentCaptureFrame(
|
||||
long id, String value, int x, int y, int width, int height, String title) {
|
||||
return new ContentCaptureFrame(id, value, x, y, width, height, title);
|
||||
public static ContentCaptureFrame createContentCaptureFrame(long id, String value, int x, int y,
|
||||
int width, int height, String title, String favicon) {
|
||||
return new ContentCaptureFrame(id, value, x, y, width, height, title, favicon);
|
||||
}
|
||||
|
||||
private ContentCaptureFrame(
|
||||
long id, String value, int x, int y, int width, int height, String title) {
|
||||
private ContentCaptureFrame(long id, String value, int x, int y, int width, int height,
|
||||
String title, String favicon) {
|
||||
super(id, new Rect(x, y, x + width, y + height));
|
||||
mUrl = value;
|
||||
mTitle = title;
|
||||
mFavicon = favicon;
|
||||
}
|
||||
|
||||
public String getUrl() {
|
||||
@ -39,6 +41,10 @@ public class ContentCaptureFrame extends ContentCaptureDataBase {
|
||||
return mTitle;
|
||||
}
|
||||
|
||||
public String getFavicon() {
|
||||
return mFavicon;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder sb = new StringBuilder(super.toString());
|
||||
|
@ -43,6 +43,11 @@ public class ExperimentContentCaptureConsumer implements ContentCaptureConsumer
|
||||
if (sDump) Log.d(TAG, "onTitleUpdated");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFaviconUpdated(ContentCaptureFrame mainFrame) {
|
||||
if (sDump) Log.d(TAG, "onFaviconUpdated");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shouldCapture(String[] urls) {
|
||||
return true;
|
||||
|
@ -156,7 +156,18 @@ public class OnscreenContentProvider {
|
||||
consumer.onTitleUpdated(mainFrame);
|
||||
}
|
||||
}
|
||||
if (sDump.booleanValue()) Log.i(TAG, "Updated Title: %s", mainFrame);
|
||||
if (sDump.booleanValue()) Log.i(TAG, "Updated Title: %s", mainFrame.getTitle());
|
||||
}
|
||||
|
||||
@CalledByNative
|
||||
private void didUpdateFavicon(ContentCaptureFrame mainFrame) {
|
||||
String[] urls = buildUrls(null, mainFrame);
|
||||
for (ContentCaptureConsumer consumer : mContentCaptureConsumers) {
|
||||
if (consumer.shouldCapture(urls)) {
|
||||
consumer.onFaviconUpdated(mainFrame);
|
||||
}
|
||||
}
|
||||
if (sDump.booleanValue()) Log.i(TAG, "Updated Favicon: %s", mainFrame.getFavicon());
|
||||
}
|
||||
|
||||
@CalledByNative
|
||||
|
@ -82,6 +82,9 @@ public class PlatformContentCaptureConsumer implements ContentCaptureConsumer {
|
||||
.executeOnExecutor(AsyncTask.SERIAL_EXECUTOR);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFaviconUpdated(ContentCaptureFrame mainFrame) {}
|
||||
|
||||
@Override
|
||||
public void onContentRemoved(FrameSession frame, long[] removedIds) {
|
||||
if (frame.isEmpty() || mPlatformSession == null) return;
|
||||
|
@ -291,7 +291,7 @@ public class PlatformAPIWrapperTest {
|
||||
FrameSession frameSession = new FrameSession(1);
|
||||
frameSession.add(ContentCaptureFrame.createContentCaptureFrame(MAIN_ID, MAIN_URL,
|
||||
MAIN_FRAME_RECT.left, MAIN_FRAME_RECT.top, MAIN_FRAME_RECT.width(),
|
||||
MAIN_FRAME_RECT.height(), MAIN_TITLE));
|
||||
MAIN_FRAME_RECT.height(), MAIN_TITLE, null));
|
||||
return frameSession;
|
||||
}
|
||||
|
||||
@ -300,7 +300,7 @@ public class PlatformAPIWrapperTest {
|
||||
frameSessionForRemoveTask.add(0,
|
||||
ContentCaptureFrame.createContentCaptureFrame(CHILD_FRAME_ID, CHILD_URL,
|
||||
CHILD_FRAME_RECT.left, CHILD_FRAME_RECT.top, CHILD_FRAME_RECT.width(),
|
||||
CHILD_FRAME_RECT.height(), CHILD_TITLE));
|
||||
CHILD_FRAME_RECT.height(), CHILD_TITLE, null));
|
||||
return frameSessionForRemoveTask;
|
||||
}
|
||||
|
||||
@ -309,7 +309,7 @@ public class PlatformAPIWrapperTest {
|
||||
FrameSession frameSession = createFrameSession();
|
||||
ContentCaptureFrame data = ContentCaptureFrame.createContentCaptureFrame(CHILD_FRAME_ID,
|
||||
CHILD_URL, CHILD_FRAME_RECT.left, CHILD_FRAME_RECT.top, CHILD_FRAME_RECT.width(),
|
||||
CHILD_FRAME_RECT.height(), CHILD_TITLE);
|
||||
CHILD_FRAME_RECT.height(), CHILD_TITLE, null);
|
||||
ContentCaptureData.createContentCaptureData(data, CHILD1_ID, CHILD1_TEXT, CHILD1_RECT.left,
|
||||
CHILD1_RECT.top, CHILD1_RECT.width(), CHILD1_RECT.height());
|
||||
ContentCaptureData.createContentCaptureData(data, CHILD2_ID, CHILD2_TEXT, CHILD2_RECT.left,
|
||||
@ -321,7 +321,7 @@ public class PlatformAPIWrapperTest {
|
||||
// Modifies child2
|
||||
ContentCaptureFrame changeTextData = ContentCaptureFrame.createContentCaptureFrame(
|
||||
CHILD_FRAME_ID, CHILD_URL, CHILD_FRAME_RECT.left, CHILD_FRAME_RECT.top,
|
||||
CHILD_FRAME_RECT.width(), CHILD_FRAME_RECT.height(), CHILD_TITLE);
|
||||
CHILD_FRAME_RECT.width(), CHILD_FRAME_RECT.height(), CHILD_TITLE, null);
|
||||
ContentCaptureData.createContentCaptureData(changeTextData, CHILD2_ID, CHILD2_NEW_TEXT,
|
||||
CHILD2_RECT.left, CHILD2_RECT.top, CHILD2_RECT.width(), CHILD2_RECT.height());
|
||||
return new ContentUpdateTask(createFrameSession(), changeTextData, mRootPlatformSession);
|
||||
@ -340,7 +340,7 @@ public class PlatformAPIWrapperTest {
|
||||
private TitleUpdateTask createTitleUpdateTask() {
|
||||
ContentCaptureFrame mainFrame = ContentCaptureFrame.createContentCaptureFrame(MAIN_ID,
|
||||
MAIN_URL, MAIN_FRAME_RECT.left, MAIN_FRAME_RECT.top, MAIN_FRAME_RECT.width(),
|
||||
MAIN_FRAME_RECT.height(), UPDATED_MAIN_TITLE);
|
||||
MAIN_FRAME_RECT.height(), UPDATED_MAIN_TITLE, null);
|
||||
return new TitleUpdateTask(mainFrame, mRootPlatformSession);
|
||||
}
|
||||
|
||||
|
@ -55,10 +55,14 @@ ScopedJavaLocalRef<jobject> ToJavaObjectOfContentCaptureFrame(
|
||||
if (!data.title.empty())
|
||||
jtitle = ConvertUTF16ToJavaString(env, data.title);
|
||||
|
||||
ScopedJavaLocalRef<jstring> jfavicon;
|
||||
if (!data.favicon.empty())
|
||||
jfavicon = ConvertUTF8ToJavaString(env, data.favicon);
|
||||
|
||||
ScopedJavaLocalRef<jobject> jdata =
|
||||
Java_ContentCaptureFrame_createContentCaptureFrame(
|
||||
env, data.id, jurl, data.bounds.x(), data.bounds.y() + offset_y,
|
||||
data.bounds.width(), data.bounds.height(), jtitle);
|
||||
data.bounds.width(), data.bounds.height(), jtitle, jfavicon);
|
||||
if (jdata.is_null())
|
||||
return jdata;
|
||||
for (const auto& child : data.children) {
|
||||
@ -190,6 +194,22 @@ void OnscreenContentProviderAndroid::DidUpdateTitle(
|
||||
Java_OnscreenContentProvider_didUpdateTitle(env, java_ref_, jdata);
|
||||
}
|
||||
|
||||
void OnscreenContentProviderAndroid::DidUpdateFavicon(
|
||||
const ContentCaptureFrame& main_frame) {
|
||||
JNIEnv* env = AttachCurrentThread();
|
||||
DCHECK(java_ref_.obj());
|
||||
|
||||
auto* web_contents = GetWebContents();
|
||||
DCHECK(web_contents);
|
||||
const int offset_y = Java_OnscreenContentProvider_getOffsetY(
|
||||
env, java_ref_, web_contents->GetJavaWebContents());
|
||||
ScopedJavaLocalRef<jobject> jdata =
|
||||
ToJavaObjectOfContentCaptureFrame(env, main_frame, offset_y);
|
||||
if (jdata.is_null())
|
||||
return;
|
||||
Java_OnscreenContentProvider_didUpdateFavicon(env, java_ref_, jdata);
|
||||
}
|
||||
|
||||
bool OnscreenContentProviderAndroid::ShouldCapture(const GURL& url) {
|
||||
// Capture all urls for experiment, the url will be checked
|
||||
// before the content is sent to the consumers.
|
||||
|
@ -32,6 +32,7 @@ class OnscreenContentProviderAndroid : public ContentCaptureConsumer {
|
||||
const std::vector<int64_t>& data) override;
|
||||
void DidRemoveSession(const ContentCaptureSession& session) override;
|
||||
void DidUpdateTitle(const ContentCaptureFrame& main_frame) override;
|
||||
void DidUpdateFavicon(const ContentCaptureFrame& main_frame) override;
|
||||
bool ShouldCapture(const GURL& url) override;
|
||||
|
||||
base::android::ScopedJavaLocalRef<jobject> GetJavaObject();
|
||||
|
30
components/content_capture/android/test_support/BUILD.gn
Normal file
30
components/content_capture/android/test_support/BUILD.gn
Normal file
@ -0,0 +1,30 @@
|
||||
# Copyright 2021 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.
|
||||
|
||||
import("//build/config/android/config.gni")
|
||||
import("//build/config/android/rules.gni")
|
||||
|
||||
testonly = true
|
||||
|
||||
source_set("test_support") {
|
||||
sources = [ "content_capture_test_support_android.cc" ]
|
||||
deps = [
|
||||
":jni_headers",
|
||||
"//components/content_capture/browser",
|
||||
]
|
||||
}
|
||||
|
||||
android_library("java") {
|
||||
deps = [
|
||||
"//base:base_java",
|
||||
"//content/public/android:content_java",
|
||||
"//third_party/androidx:androidx_annotation_annotation_java",
|
||||
]
|
||||
annotation_processor_deps = [ "//base/android/jni_generator:jni_processor" ]
|
||||
sources = [ "java/src/org/chromium/components/content_capture/ContentCaptureTestSupport.java" ]
|
||||
}
|
||||
|
||||
generate_jni("jni_headers") {
|
||||
sources = [ "java/src/org/chromium/components/content_capture/ContentCaptureTestSupport.java" ]
|
||||
}
|
76
components/content_capture/android/test_support/content_capture_test_support_android.cc
Normal file
76
components/content_capture/android/test_support/content_capture_test_support_android.cc
Normal file
@ -0,0 +1,76 @@
|
||||
// Copyright 2021 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/test_support/jni_headers/ContentCaptureTestSupport_jni.h"
|
||||
|
||||
#include "base/android/jni_string.h"
|
||||
#include "base/json/json_reader.h"
|
||||
#include "base/notreached.h"
|
||||
#include "base/values.h"
|
||||
#include "components/content_capture/browser/content_capture_receiver.h"
|
||||
#include "components/content_capture/browser/onscreen_content_provider.h"
|
||||
#include "content/public/browser/web_contents.h"
|
||||
#include "third_party/blink/public/mojom/favicon/favicon_url.mojom.h"
|
||||
#include "ui/gfx/geometry/size.h"
|
||||
|
||||
namespace content_capture {
|
||||
|
||||
namespace {
|
||||
blink::mojom::FaviconIconType ToType(std::string type) {
|
||||
if (type == "favicon")
|
||||
return blink::mojom::FaviconIconType::kFavicon;
|
||||
else if (type == "touch icon")
|
||||
return blink::mojom::FaviconIconType::kTouchIcon;
|
||||
else if (type == "touch precomposed icon")
|
||||
return blink::mojom::FaviconIconType::kTouchPrecomposedIcon;
|
||||
NOTREACHED();
|
||||
return blink::mojom::FaviconIconType::kInvalid;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
static void JNI_ContentCaptureTestSupport_DisableGetFaviconFromWebContents(
|
||||
JNIEnv* env) {
|
||||
ContentCaptureReceiver::DisableGetFaviconFromWebContentsForTesting();
|
||||
}
|
||||
|
||||
static void JNI_ContentCaptureTestSupport_SimulateDidUpdateFaviconURL(
|
||||
JNIEnv* env,
|
||||
const base::android::JavaParamRef<jobject>& jwebContents,
|
||||
const base::android::JavaParamRef<jstring>& jfaviconJson) {
|
||||
content::WebContents* web_contents =
|
||||
content::WebContents::FromJavaWebContents(jwebContents);
|
||||
CHECK(web_contents);
|
||||
OnscreenContentProvider* provider =
|
||||
OnscreenContentProvider::FromWebContents(web_contents);
|
||||
CHECK(provider);
|
||||
|
||||
std::string json = base::android::ConvertJavaStringToUTF8(env, jfaviconJson);
|
||||
absl::optional<base::Value> root = base::JSONReader::Read(json);
|
||||
CHECK(root);
|
||||
CHECK(root->is_list());
|
||||
std::vector<blink::mojom::FaviconURLPtr> favicon_urls;
|
||||
for (const base::Value& icon : root->GetList()) {
|
||||
std::vector<gfx::Size> sizes;
|
||||
// The sizes is optional.
|
||||
if (auto* icon_sizes = icon.FindKey("sizes")) {
|
||||
for (const base::Value& size : icon_sizes->GetList()) {
|
||||
CHECK(size.FindKey("width"));
|
||||
CHECK(size.FindKey("height"));
|
||||
sizes.emplace_back(size.FindKey("width")->GetInt(),
|
||||
size.FindKey("height")->GetInt());
|
||||
}
|
||||
}
|
||||
CHECK(icon.FindKey("url"));
|
||||
CHECK(icon.FindKey("type"));
|
||||
favicon_urls.push_back(blink::mojom::FaviconURL::New(
|
||||
GURL(*icon.FindKey("url")->GetIfString()),
|
||||
ToType(*icon.FindKey("type")->GetIfString()), sizes));
|
||||
}
|
||||
CHECK(!favicon_urls.empty());
|
||||
provider->NotifyFaviconURLUpdatedForTesting(web_contents->GetMainFrame(),
|
||||
favicon_urls);
|
||||
}
|
||||
|
||||
} // namespace content_capture
|
28
components/content_capture/android/test_support/java/src/org/chromium/components/content_capture/ContentCaptureTestSupport.java
Normal file
28
components/content_capture/android/test_support/java/src/org/chromium/components/content_capture/ContentCaptureTestSupport.java
Normal file
@ -0,0 +1,28 @@
|
||||
// Copyright 2021 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.JNINamespace;
|
||||
import org.chromium.base.annotations.NativeMethods;
|
||||
import org.chromium.content_public.browser.WebContents;
|
||||
|
||||
/**
|
||||
* This is the test support class to help setup various test conditions.
|
||||
*/
|
||||
@JNINamespace("content_capture")
|
||||
public class ContentCaptureTestSupport {
|
||||
public static void disableGetFaviconFromWebContents() {
|
||||
ContentCaptureTestSupportJni.get().disableGetFaviconFromWebContents();
|
||||
}
|
||||
|
||||
public static void simulateDidUpdateFaviconURL(WebContents webContents, String faviconJson) {
|
||||
ContentCaptureTestSupportJni.get().simulateDidUpdateFaviconURL(webContents, faviconJson);
|
||||
}
|
||||
|
||||
@NativeMethods
|
||||
interface Natives {
|
||||
void disableGetFaviconFromWebContents();
|
||||
void simulateDidUpdateFaviconURL(WebContents webContents, String faviconJson);
|
||||
}
|
||||
}
|
@ -55,6 +55,8 @@ class ContentCaptureConsumer {
|
||||
virtual void DidRemoveSession(const ContentCaptureSession& session) = 0;
|
||||
// Invoked when the given |main_frame|'s title updated.
|
||||
virtual void DidUpdateTitle(const ContentCaptureFrame& main_frame) = 0;
|
||||
// Invoked when the given |main_frame|'s favicon updated.
|
||||
virtual void DidUpdateFavicon(const ContentCaptureFrame& main_frame) = 0;
|
||||
|
||||
// Return if the |url| shall be captured. Even return false, the content might
|
||||
// still be streamed because of the other consumers require it. Consumer can
|
||||
|
@ -216,10 +216,15 @@ void ContentCaptureReceiver::UpdateFaviconURL(
|
||||
if (!has_session_)
|
||||
return;
|
||||
frame_content_capture_data_.favicon = ToJSON(candidates);
|
||||
auto* provider = GetOnscreenContentProvider(rfh_);
|
||||
if (!provider)
|
||||
return;
|
||||
provider->DidUpdateFavicon(this);
|
||||
}
|
||||
|
||||
void ContentCaptureReceiver::RetrieveFaviconURL() {
|
||||
if (!rfh()->IsActive() || rfh()->GetMainFrame() != rfh()) {
|
||||
if (!rfh()->IsActive() || rfh()->GetMainFrame() != rfh() ||
|
||||
disable_get_favicon_from_web_contents_for_testing()) {
|
||||
frame_content_capture_data_.favicon = std::string();
|
||||
} else {
|
||||
frame_content_capture_data_.favicon = ToJSON(
|
||||
@ -267,4 +272,19 @@ const ContentCaptureFrame& ContentCaptureReceiver::GetContentCaptureFrame() {
|
||||
return frame_content_capture_data_;
|
||||
}
|
||||
|
||||
// static
|
||||
bool
|
||||
ContentCaptureReceiver::disable_get_favicon_from_web_contents_for_testing_ =
|
||||
false;
|
||||
|
||||
void ContentCaptureReceiver::DisableGetFaviconFromWebContentsForTesting() {
|
||||
disable_get_favicon_from_web_contents_for_testing_ = true;
|
||||
}
|
||||
|
||||
// static
|
||||
bool ContentCaptureReceiver::
|
||||
disable_get_favicon_from_web_contents_for_testing() {
|
||||
return disable_get_favicon_from_web_contents_for_testing_;
|
||||
}
|
||||
|
||||
} // namespace content_capture
|
||||
|
@ -59,6 +59,9 @@ class ContentCaptureReceiver : public mojom::ContentCaptureReceiver {
|
||||
void UpdateFaviconURL(
|
||||
const std::vector<blink::mojom::FaviconURLPtr>& candidates);
|
||||
|
||||
static void DisableGetFaviconFromWebContentsForTesting();
|
||||
static bool disable_get_favicon_from_web_contents_for_testing();
|
||||
|
||||
private:
|
||||
FRIEND_TEST_ALL_PREFIXES(ContentCaptureReceiverTest, RenderFrameHostGone);
|
||||
FRIEND_TEST_ALL_PREFIXES(ContentCaptureReceiverTest, TitleUpdateTaskDelay);
|
||||
@ -104,6 +107,8 @@ class ContentCaptureReceiver : public mojom::ContentCaptureReceiver {
|
||||
// prevent running frequently.
|
||||
unsigned exponential_delay_ = 1;
|
||||
|
||||
static bool disable_get_favicon_from_web_contents_for_testing_;
|
||||
|
||||
mojo::AssociatedRemote<mojom::ContentCaptureSender> content_capture_sender_;
|
||||
DISALLOW_COPY_AND_ASSIGN(ContentCaptureReceiver);
|
||||
};
|
||||
|
@ -113,6 +113,8 @@ class ContentCaptureConsumerHelper : public ContentCaptureConsumer {
|
||||
updated_title_ = main_frame.title;
|
||||
}
|
||||
|
||||
void DidUpdateFavicon(const ContentCaptureFrame& main_frame) override {}
|
||||
|
||||
bool ShouldCapture(const GURL& url) override { return false; }
|
||||
|
||||
const ContentCaptureSession& parent_session() const {
|
||||
|
@ -217,6 +217,16 @@ void OnscreenContentProvider::DidUpdateTitle(
|
||||
void OnscreenContentProvider::DidUpdateFaviconURL(
|
||||
content::RenderFrameHost* render_frame_host,
|
||||
const std::vector<blink::mojom::FaviconURLPtr>& candidates) {
|
||||
if (ContentCaptureReceiver::
|
||||
disable_get_favicon_from_web_contents_for_testing()) {
|
||||
return;
|
||||
}
|
||||
NotifyFaviconURLUpdated(render_frame_host, candidates);
|
||||
}
|
||||
|
||||
void OnscreenContentProvider::NotifyFaviconURLUpdated(
|
||||
content::RenderFrameHost* render_frame_host,
|
||||
const std::vector<blink::mojom::FaviconURLPtr>& candidates) {
|
||||
// Only set the favicons for the mainframe.
|
||||
if (render_frame_host != web_contents()->GetMainFrame())
|
||||
return;
|
||||
@ -226,6 +236,18 @@ void OnscreenContentProvider::DidUpdateFaviconURL(
|
||||
}
|
||||
}
|
||||
|
||||
void OnscreenContentProvider::DidUpdateFavicon(
|
||||
ContentCaptureReceiver* content_capture_receiver) {
|
||||
ContentCaptureSession session;
|
||||
BuildContentCaptureSession(content_capture_receiver,
|
||||
/*ancestor_only=*/false, &session);
|
||||
|
||||
// Shall only update mainframe's title.
|
||||
DCHECK(session.size() == 1);
|
||||
for (auto* consumer : consumers_)
|
||||
consumer->DidUpdateFavicon(*session.begin());
|
||||
}
|
||||
|
||||
void OnscreenContentProvider::BuildContentCaptureSession(
|
||||
ContentCaptureReceiver* content_capture_receiver,
|
||||
bool ancestor_only,
|
||||
|
@ -58,6 +58,7 @@ class OnscreenContentProvider : public content::WebContentsObserver,
|
||||
const std::vector<int64_t>& data);
|
||||
void DidRemoveSession(ContentCaptureReceiver* content_capture_receiver);
|
||||
void DidUpdateTitle(ContentCaptureReceiver* content_capture_receiver);
|
||||
void DidUpdateFavicon(ContentCaptureReceiver* content_capture_receiver);
|
||||
|
||||
// content::WebContentsObserver:
|
||||
void RenderFrameCreated(content::RenderFrameHost* render_frame_host) override;
|
||||
@ -75,6 +76,12 @@ class OnscreenContentProvider : public content::WebContentsObserver,
|
||||
return weak_ptr_factory_.GetWeakPtr();
|
||||
}
|
||||
|
||||
void NotifyFaviconURLUpdatedForTesting(
|
||||
content::RenderFrameHost* render_frame_host,
|
||||
const std::vector<blink::mojom::FaviconURLPtr>& candidates) {
|
||||
NotifyFaviconURLUpdated(render_frame_host, candidates);
|
||||
}
|
||||
|
||||
#ifdef UNIT_TEST
|
||||
ContentCaptureReceiver* ContentCaptureReceiverForFrameForTesting(
|
||||
content::RenderFrameHost* render_frame_host) const {
|
||||
@ -110,6 +117,10 @@ class OnscreenContentProvider : public content::WebContentsObserver,
|
||||
|
||||
bool ShouldCapture(const GURL& url);
|
||||
|
||||
void NotifyFaviconURLUpdated(
|
||||
content::RenderFrameHost* render_frame_host,
|
||||
const std::vector<blink::mojom::FaviconURLPtr>& candidates);
|
||||
|
||||
std::map<content::RenderFrameHost*, std::unique_ptr<ContentCaptureReceiver>>
|
||||
frame_map_;
|
||||
|
||||
|
@ -19,6 +19,7 @@ public class TestContentCaptureConsumer implements ContentCaptureConsumer {
|
||||
public static final int CONTENT_REMOVED = 3;
|
||||
public static final int SESSION_REMOVED = 4;
|
||||
public static final int TITLE_UPDATED = 5;
|
||||
public static final int FAVICON_UPDATED = 6;
|
||||
|
||||
public TestContentCaptureConsumer(Runnable onNewEvents, ArrayList<Integer> eventsObserved) {
|
||||
mOnNewEvents = onNewEvents;
|
||||
@ -56,6 +57,12 @@ public class TestContentCaptureConsumer implements ContentCaptureConsumer {
|
||||
mOnNewEvents.run();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFaviconUpdated(ContentCaptureFrame mainFrame) {
|
||||
mEventsObserved.add(FAVICON_UPDATED);
|
||||
mOnNewEvents.run();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shouldCapture(String[] urls) {
|
||||
return true;
|
||||
|
Reference in New Issue
Block a user