Commit 28a2f552 authored by Bo Liu's avatar Bo Liu Committed by Commit Bot

weblayer: Implement showContextMenu callback

Add TabCallback.showContextMenu. Define a new ContextMenuParams type
which is an immutable struct-like class to hold all context menu
parameters.

Pass the parameters as separate ObjectWrappers through aidl to avoid
serialization. This could be costly for long uri or data.

Only passing a basic list of parameters while discussion of what will
be needed is happening.

Bug: 1035469
Change-Id: I693ee8788d93a7ce828fcfda0e52f29dcda5b222
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2059049
Commit-Queue: Bo <boliu@chromium.org>
Reviewed-by: default avatarJohn Abd-El-Malek <jam@chromium.org>
Cr-Commit-Position: refs/heads/master@{#743746}
parent 54ff0ea1
...@@ -145,6 +145,8 @@ jumbo_static_library("weblayer_lib") { ...@@ -145,6 +145,8 @@ jumbo_static_library("weblayer_lib") {
"browser/urlbar/autocomplete_scheme_classifier_impl.h", "browser/urlbar/autocomplete_scheme_classifier_impl.h",
"browser/urlbar/url_bar_controller_impl.cc", "browser/urlbar/url_bar_controller_impl.cc",
"browser/urlbar/url_bar_controller_impl.h", "browser/urlbar/url_bar_controller_impl.h",
"browser/web_contents_view_delegate_impl.cc",
"browser/web_contents_view_delegate_impl.h",
"browser/weblayer_browser_interface_binders.cc", "browser/weblayer_browser_interface_binders.cc",
"browser/weblayer_browser_interface_binders.h", "browser/weblayer_browser_interface_binders.h",
"browser/weblayer_content_browser_overlay_manifest.cc", "browser/weblayer_content_browser_overlay_manifest.cc",
...@@ -323,6 +325,7 @@ jumbo_static_library("weblayer_lib") { ...@@ -323,6 +325,7 @@ jumbo_static_library("weblayer_lib") {
"//components/android_system_error_page", "//components/android_system_error_page",
"//components/autofill/android:provider", "//components/autofill/android:provider",
"//components/crash/android:crashpad_main", "//components/crash/android:crashpad_main",
"//components/embedder_support/android:context_menu",
"//components/embedder_support/android/metrics", "//components/embedder_support/android/metrics",
"//components/javascript_dialogs", "//components/javascript_dialogs",
"//components/metrics", "//components/metrics",
......
...@@ -5,8 +5,10 @@ ...@@ -5,8 +5,10 @@
package org.chromium.weblayer.test; package org.chromium.weblayer.test;
import android.net.Uri; import android.net.Uri;
import android.support.test.InstrumentationRegistry;
import android.support.test.filters.SmallTest; import android.support.test.filters.SmallTest;
import org.junit.Assert;
import org.junit.Rule; import org.junit.Rule;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
...@@ -15,6 +17,8 @@ import org.chromium.base.test.util.CallbackHelper; ...@@ -15,6 +17,8 @@ import org.chromium.base.test.util.CallbackHelper;
import org.chromium.content_public.browser.test.util.Criteria; import org.chromium.content_public.browser.test.util.Criteria;
import org.chromium.content_public.browser.test.util.CriteriaHelper; import org.chromium.content_public.browser.test.util.CriteriaHelper;
import org.chromium.content_public.browser.test.util.TestThreadUtils; import org.chromium.content_public.browser.test.util.TestThreadUtils;
import org.chromium.content_public.browser.test.util.TestTouchUtils;
import org.chromium.weblayer.ContextMenuParams;
import org.chromium.weblayer.Tab; import org.chromium.weblayer.Tab;
import org.chromium.weblayer.TabCallback; import org.chromium.weblayer.TabCallback;
import org.chromium.weblayer.shell.InstrumentationActivity; import org.chromium.weblayer.shell.InstrumentationActivity;
...@@ -102,4 +106,34 @@ public class TabCallbackTest { ...@@ -102,4 +106,34 @@ public class TabCallbackTest {
}); });
callbackHelper.waitForFirst(); callbackHelper.waitForFirst();
} }
// Requires implementation M82.
@Test
@SmallTest
public void testShowContextMenu() throws TimeoutException {
String pageUrl = mActivityTestRule.getTestDataURL("download.html");
InstrumentationActivity activity = mActivityTestRule.launchShellWithUrl(pageUrl);
ContextMenuParams params[] = new ContextMenuParams[1];
CallbackHelper callbackHelper = new CallbackHelper();
TestThreadUtils.runOnUiThreadBlocking(() -> {
Tab tab = activity.getTab();
TabCallback callback = new TabCallback() {
@Override
public void showContextMenu(ContextMenuParams param) {
params[0] = param;
callbackHelper.notifyCalled();
}
};
tab.registerTabCallback(callback);
});
TestTouchUtils.longClickView(
InstrumentationRegistry.getInstrumentation(), activity.getWindow().getDecorView());
callbackHelper.waitForFirst();
Assert.assertEquals(Uri.parse(pageUrl), params[0].pageUri);
Assert.assertEquals(
Uri.parse(mActivityTestRule.getTestDataURL("lorem_ipsum.txt")), params[0].linkUri);
Assert.assertEquals("anchor text", params[0].linkText);
}
} }
...@@ -48,6 +48,7 @@ ...@@ -48,6 +48,7 @@
#include "weblayer/browser/profile_impl.h" #include "weblayer/browser/profile_impl.h"
#include "weblayer/browser/system_network_context_manager.h" #include "weblayer/browser/system_network_context_manager.h"
#include "weblayer/browser/tab_impl.h" #include "weblayer/browser/tab_impl.h"
#include "weblayer/browser/web_contents_view_delegate_impl.h"
#include "weblayer/browser/weblayer_browser_interface_binders.h" #include "weblayer/browser/weblayer_browser_interface_binders.h"
#include "weblayer/browser/weblayer_content_browser_overlay_manifest.h" #include "weblayer/browser/weblayer_content_browser_overlay_manifest.h"
#include "weblayer/browser/weblayer_security_blocking_page_factory.h" #include "weblayer/browser/weblayer_security_blocking_page_factory.h"
...@@ -207,7 +208,7 @@ std::string ContentBrowserClientImpl::GetAcceptLangs( ...@@ -207,7 +208,7 @@ std::string ContentBrowserClientImpl::GetAcceptLangs(
content::WebContentsViewDelegate* content::WebContentsViewDelegate*
ContentBrowserClientImpl::GetWebContentsViewDelegate( ContentBrowserClientImpl::GetWebContentsViewDelegate(
content::WebContents* web_contents) { content::WebContents* web_contents) {
return nullptr; return new WebContentsViewDelegateImpl(web_contents);
} }
content::DevToolsManagerDelegate* content::DevToolsManagerDelegate*
......
...@@ -76,6 +76,7 @@ android_library("java") { ...@@ -76,6 +76,7 @@ android_library("java") {
"//components/crash/android:java", "//components/crash/android:java",
"//components/download/internal/common:internal_java", "//components/download/internal/common:internal_java",
"//components/embedder_support/android:application_java", "//components/embedder_support/android:application_java",
"//components/embedder_support/android:context_menu_java",
"//components/embedder_support/android:web_contents_delegate_java", "//components/embedder_support/android:web_contents_delegate_java",
"//components/find_in_page/android:java", "//components/find_in_page/android:java",
"//components/javascript_dialogs/android:java", "//components/javascript_dialogs/android:java",
......
...@@ -7,6 +7,7 @@ package org.chromium.weblayer_private; ...@@ -7,6 +7,7 @@ package org.chromium.weblayer_private;
import android.graphics.RectF; import android.graphics.RectF;
import android.os.Build; import android.os.Build;
import android.os.RemoteException; import android.os.RemoteException;
import android.text.TextUtils;
import android.util.AndroidRuntimeException; import android.util.AndroidRuntimeException;
import android.view.KeyEvent; import android.view.KeyEvent;
import android.view.MotionEvent; import android.view.MotionEvent;
...@@ -20,6 +21,7 @@ import org.chromium.components.autofill.AutofillProvider; ...@@ -20,6 +21,7 @@ import org.chromium.components.autofill.AutofillProvider;
import org.chromium.components.autofill.AutofillProviderImpl; import org.chromium.components.autofill.AutofillProviderImpl;
import org.chromium.components.browser_ui.util.BrowserControlsVisibilityDelegate; import org.chromium.components.browser_ui.util.BrowserControlsVisibilityDelegate;
import org.chromium.components.browser_ui.util.ComposedBrowserControlsVisibilityDelegate; import org.chromium.components.browser_ui.util.ComposedBrowserControlsVisibilityDelegate;
import org.chromium.components.embedder_support.contextmenu.ContextMenuParams;
import org.chromium.components.find_in_page.FindInPageBridge; import org.chromium.components.find_in_page.FindInPageBridge;
import org.chromium.components.find_in_page.FindMatchRectsDetails; import org.chromium.components.find_in_page.FindMatchRectsDetails;
import org.chromium.components.find_in_page.FindResultBar; import org.chromium.components.find_in_page.FindResultBar;
...@@ -488,6 +490,19 @@ public final class TabImpl extends ITab.Stub { ...@@ -488,6 +490,19 @@ public final class TabImpl extends ITab.Stub {
mBrowserControlsDelegates.get(reason).set(constraint); mBrowserControlsDelegates.get(reason).set(constraint);
} }
private static String nonEmptyOrNull(String s) {
return TextUtils.isEmpty(s) ? null : s;
}
@CalledByNative
private void showContextMenu(ContextMenuParams params) throws RemoteException {
if (WebLayerFactoryImpl.getClientMajorVersion() < 82) return;
mClient.showContextMenu(ObjectWrapper.wrap(params.getPageUrl()),
ObjectWrapper.wrap(nonEmptyOrNull(params.getLinkUrl())),
ObjectWrapper.wrap(nonEmptyOrNull(params.getLinkText())),
ObjectWrapper.wrap(nonEmptyOrNull(params.getTitleText())));
}
private void onBrowserControlsStateUpdated(int state) { private void onBrowserControlsStateUpdated(int state) {
TabImplJni.get().updateBrowserControlsState(mNativeTab, state); TabImplJni.get().updateBrowserControlsState(mNativeTab, state);
// If something has overridden the FIP's SHOWN constraint, cancel FIP. This causes FIP to // If something has overridden the FIP's SHOWN constraint, cancel FIP. This causes FIP to
......
...@@ -4,6 +4,8 @@ ...@@ -4,6 +4,8 @@
package org.chromium.weblayer_private.interfaces; package org.chromium.weblayer_private.interfaces;
import org.chromium.weblayer_private.interfaces.IObjectWrapper;
/** /**
* Interface used by Tab to inform the client of changes. This largely duplicates the * Interface used by Tab to inform the client of changes. This largely duplicates the
* TabCallback interface, but is a singleton to avoid unnecessary IPC. * TabCallback interface, but is a singleton to avoid unnecessary IPC.
...@@ -16,4 +18,8 @@ interface ITabClient { ...@@ -16,4 +18,8 @@ interface ITabClient {
void onRenderProcessGone() = 2; void onRenderProcessGone() = 2;
void onCloseTab() = 3; void onCloseTab() = 3;
// Added in M82.
void showContextMenu(in IObjectWrapper pageUrl, in IObjectWrapper linkUrl,
in IObjectWrapper linkText, in IObjectWrapper titleOrAltText) = 4;
} }
...@@ -47,6 +47,7 @@ ...@@ -47,6 +47,7 @@
#include "base/json/json_writer.h" #include "base/json/json_writer.h"
#include "base/trace_event/trace_event.h" #include "base/trace_event/trace_event.h"
#include "components/autofill/android/autofill_provider_android.h" #include "components/autofill/android/autofill_provider_android.h"
#include "components/embedder_support/android/contextmenu/context_menu_builder.h"
#include "components/embedder_support/android/delegate/color_chooser_android.h" #include "components/embedder_support/android/delegate/color_chooser_android.h"
#include "components/javascript_dialogs/tab_modal_dialog_manager.h" // nogncheck #include "components/javascript_dialogs/tab_modal_dialog_manager.h" // nogncheck
#include "ui/android/view_android.h" #include "ui/android/view_android.h"
...@@ -307,6 +308,14 @@ bool TabImpl::IsActive() { ...@@ -307,6 +308,14 @@ bool TabImpl::IsActive() {
return browser_->GetActiveTab() == this; return browser_->GetActiveTab() == this;
} }
void TabImpl::ShowContextMenu(const content::ContextMenuParams& params) {
#if defined(OS_ANDROID)
Java_TabImpl_showContextMenu(
base::android::AttachCurrentThread(), java_impl_,
context_menu::BuildJavaContextMenuParams(params));
#endif
}
#if defined(OS_ANDROID) #if defined(OS_ANDROID)
// static // static
void TabImpl::DisableAutofillSystemIntegrationForTesting() { void TabImpl::DisableAutofillSystemIntegrationForTesting() {
......
...@@ -30,6 +30,7 @@ class AutofillProvider; ...@@ -30,6 +30,7 @@ class AutofillProvider;
namespace content { namespace content {
class WebContents; class WebContents;
struct ContextMenuParams;
} }
namespace sessions { namespace sessions {
...@@ -77,6 +78,8 @@ class TabImpl : public Tab, ...@@ -77,6 +78,8 @@ class TabImpl : public Tab,
bool IsActive(); bool IsActive();
void ShowContextMenu(const content::ContextMenuParams& params);
#if defined(OS_ANDROID) #if defined(OS_ANDROID)
base::android::ScopedJavaGlobalRef<jobject> GetJavaTab() { base::android::ScopedJavaGlobalRef<jobject> GetJavaTab() {
return java_impl_; return java_impl_;
......
// Copyright 2020 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 "weblayer/browser/web_contents_view_delegate_impl.h"
#include "weblayer/browser/tab_impl.h"
namespace weblayer {
WebContentsViewDelegateImpl::WebContentsViewDelegateImpl(
content::WebContents* web_contents)
: web_contents_(web_contents) {}
WebContentsViewDelegateImpl::~WebContentsViewDelegateImpl() = default;
void WebContentsViewDelegateImpl::ShowContextMenu(
content::RenderFrameHost* render_frame_host,
const content::ContextMenuParams& params) {
TabImpl* tab = TabImpl::FromWebContents(web_contents_);
if (tab)
tab->ShowContextMenu(params);
}
content::WebContentsViewDelegate* CreateWebContentsViewDelegate(
content::WebContents* web_contents) {
return new WebContentsViewDelegateImpl(web_contents);
}
} // namespace weblayer
// Copyright 2020 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 WEBLAYER_BROWSER_WEB_CONTENTS_VIEW_DELEGATE_IMPL_H_
#define WEBLAYER_BROWSER_WEB_CONTENTS_VIEW_DELEGATE_IMPL_H_
#include "base/macros.h"
#include "content/public/browser/web_contents_view_delegate.h"
namespace content {
class WebContents;
}
namespace weblayer {
class WebContentsViewDelegateImpl : public content::WebContentsViewDelegate {
public:
explicit WebContentsViewDelegateImpl(content::WebContents* web_contents);
~WebContentsViewDelegateImpl() override;
// WebContentsViewDelegate overrides.
void ShowContextMenu(content::RenderFrameHost* render_frame_host,
const content::ContextMenuParams& params) override;
private:
content::WebContents* web_contents_;
DISALLOW_COPY_AND_ASSIGN(WebContentsViewDelegateImpl);
};
} // namespace weblayer
#endif // WEBLAYER_BROWSER_WEB_CONTENTS_VIEW_DELEGATE_IMPL_H_
...@@ -30,6 +30,7 @@ android_library("java") { ...@@ -30,6 +30,7 @@ android_library("java") {
"org/chromium/weblayer/BrowsingDataType.java", "org/chromium/weblayer/BrowsingDataType.java",
"org/chromium/weblayer/Callback.java", "org/chromium/weblayer/Callback.java",
"org/chromium/weblayer/ChildProcessService.java", "org/chromium/weblayer/ChildProcessService.java",
"org/chromium/weblayer/ContextMenuParams.java",
"org/chromium/weblayer/CrashReporterCallback.java", "org/chromium/weblayer/CrashReporterCallback.java",
"org/chromium/weblayer/CrashReporterController.java", "org/chromium/weblayer/CrashReporterController.java",
"org/chromium/weblayer/Download.java", "org/chromium/weblayer/Download.java",
......
// Copyright 2020 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.weblayer;
import android.net.Uri;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
/**
* Parameters for constructing context menu.
*/
public class ContextMenuParams {
/**
* The Uri associated with the main frame of the page that triggered the context menu.
*/
@NonNull
public final Uri pageUri;
/**
* The link Uri, if any.
*/
@Nullable
public final Uri linkUri;
/**
* The link text, if any.
*/
@Nullable
public final String linkText;
/**
* The title or alt attribute (if title is not available).
*/
@Nullable
public final String titleOrAltText;
public ContextMenuParams(Uri pageUri, Uri linkUri, String linkText, String titleOrAltText) {
this.pageUri = pageUri;
this.linkUri = linkUri;
this.linkText = linkText;
this.titleOrAltText = titleOrAltText;
}
}
...@@ -275,6 +275,21 @@ public class Tab { ...@@ -275,6 +275,21 @@ public class Tab {
callback.onRenderProcessGone(); callback.onRenderProcessGone();
} }
} }
@Override
public void showContextMenu(IObjectWrapper pageUrl, IObjectWrapper linkUrl,
IObjectWrapper linkText, IObjectWrapper titleOrAltText) {
StrictModeWorkaround.apply();
String pageUrlString = ObjectWrapper.unwrap(pageUrl, String.class);
String linkUrlString = ObjectWrapper.unwrap(linkUrl, String.class);
ContextMenuParams params = new ContextMenuParams(Uri.parse(pageUrlString),
linkUrlString != null ? Uri.parse(linkUrlString) : null,
ObjectWrapper.unwrap(linkText, String.class),
ObjectWrapper.unwrap(titleOrAltText, String.class));
for (TabCallback callback : mCallbacks) {
callback.showContextMenu(params);
}
}
} }
private static final class DownloadCallbackClientImpl extends IDownloadCallbackClient.Stub { private static final class DownloadCallbackClientImpl extends IDownloadCallbackClient.Stub {
......
...@@ -24,4 +24,10 @@ public abstract class TabCallback { ...@@ -24,4 +24,10 @@ public abstract class TabCallback {
* reclaim memory. * reclaim memory.
*/ */
public void onRenderProcessGone() {} public void onRenderProcessGone() {}
/**
* Triggered when a context menu should be displayed.
* Added in M82.
*/
public void showContextMenu(ContextMenuParams params) {}
} }
...@@ -4,6 +4,9 @@ ...@@ -4,6 +4,9 @@
package org.chromium.weblayer.shell; package org.chromium.weblayer.shell;
import android.content.ClipData;
import android.content.ClipboardManager;
import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.net.Uri; import android.net.Uri;
import android.os.Bundle; import android.os.Bundle;
...@@ -12,8 +15,11 @@ import android.support.v4.app.FragmentActivity; ...@@ -12,8 +15,11 @@ import android.support.v4.app.FragmentActivity;
import android.support.v4.app.FragmentManager; import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentTransaction; import android.support.v4.app.FragmentTransaction;
import android.text.TextUtils; import android.text.TextUtils;
import android.view.ContextMenu;
import android.view.KeyEvent; import android.view.KeyEvent;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View; import android.view.View;
import android.view.ViewGroup.LayoutParams; import android.view.ViewGroup.LayoutParams;
import android.view.WindowManager; import android.view.WindowManager;
...@@ -27,6 +33,7 @@ import android.widget.TextView; ...@@ -27,6 +33,7 @@ import android.widget.TextView;
import android.widget.ViewSwitcher; import android.widget.ViewSwitcher;
import org.chromium.weblayer.Browser; import org.chromium.weblayer.Browser;
import org.chromium.weblayer.ContextMenuParams;
import org.chromium.weblayer.DownloadCallback; import org.chromium.weblayer.DownloadCallback;
import org.chromium.weblayer.ErrorPageCallback; import org.chromium.weblayer.ErrorPageCallback;
import org.chromium.weblayer.FindInPageCallback; import org.chromium.weblayer.FindInPageCallback;
...@@ -50,6 +57,60 @@ import java.util.List; ...@@ -50,6 +57,60 @@ import java.util.List;
* Activity for managing the Demo Shell. * Activity for managing the Demo Shell.
*/ */
public class WebLayerShellActivity extends FragmentActivity { public class WebLayerShellActivity extends FragmentActivity {
private static class ContextMenuCreator
implements View.OnCreateContextMenuListener, MenuItem.OnMenuItemClickListener {
private static final int MENU_ID_COPY_LINK_URI = 1;
private static final int MENU_ID_COPY_LINK_TEXT = 2;
private ContextMenuParams mParams;
private Context mContext;
public ContextMenuCreator(ContextMenuParams params) {
mParams = params;
}
@Override
public void onCreateContextMenu(
ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) {
mContext = v.getContext();
menu.add(mParams.pageUri.toString());
if (mParams.linkUri != null) {
MenuItem copyLinkUriItem =
menu.add(Menu.NONE, MENU_ID_COPY_LINK_URI, Menu.NONE, "Copy link address");
copyLinkUriItem.setOnMenuItemClickListener(this);
}
if (!TextUtils.isEmpty(mParams.linkText)) {
MenuItem copyLinkTextItem =
menu.add(Menu.NONE, MENU_ID_COPY_LINK_TEXT, Menu.NONE, "Copy link text");
copyLinkTextItem.setOnMenuItemClickListener(this);
}
if (!TextUtils.isEmpty(mParams.titleOrAltText)) {
TextView altTextView = new TextView(mContext);
altTextView.setText(mParams.titleOrAltText);
menu.setHeaderView(altTextView);
}
v.setOnCreateContextMenuListener(null);
}
@Override
public boolean onMenuItemClick(MenuItem item) {
ClipboardManager clipboard =
(ClipboardManager) mContext.getSystemService(Context.CLIPBOARD_SERVICE);
switch (item.getItemId()) {
case MENU_ID_COPY_LINK_URI:
clipboard.setPrimaryClip(
ClipData.newPlainText("link address", mParams.linkUri.toString()));
break;
case MENU_ID_COPY_LINK_TEXT:
clipboard.setPrimaryClip(ClipData.newPlainText("link text", mParams.linkText));
break;
default:
break;
}
return true;
}
}
private static final String TAG = "WebLayerShell"; private static final String TAG = "WebLayerShell";
private static final String KEY_MAIN_VIEW_ID = "mainViewId"; private static final String KEY_MAIN_VIEW_ID = "mainViewId";
private static final float DEFAULT_TEXT_SIZE = 15.0F; private static final float DEFAULT_TEXT_SIZE = 15.0F;
...@@ -263,6 +324,13 @@ public class WebLayerShellActivity extends FragmentActivity { ...@@ -263,6 +324,13 @@ public class WebLayerShellActivity extends FragmentActivity {
public void onVisibleUriChanged(Uri uri) { public void onVisibleUriChanged(Uri uri) {
mUrlViewContainer.setDisplayedChild(NONEDITABLE_URL_TEXT_VIEW); mUrlViewContainer.setDisplayedChild(NONEDITABLE_URL_TEXT_VIEW);
} }
@Override
public void showContextMenu(ContextMenuParams params) {
View weblayerView = getSupportFragmentManager().getFragments().get(0).getView();
weblayerView.setOnCreateContextMenuListener(new ContextMenuCreator(params));
weblayerView.showContextMenu();
}
}); });
tab.getNavigationController().registerNavigationCallback(new NavigationCallback() { tab.getNavigationController().registerNavigationCallback(new NavigationCallback() {
@Override @Override
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment