diff options
Diffstat (limited to 'chromium/weblayer/browser/java/org')
40 files changed, 960 insertions, 203 deletions
diff --git a/chromium/weblayer/browser/java/org/chromium/weblayer_private/ActionModeCallback.java b/chromium/weblayer/browser/java/org/chromium/weblayer_private/ActionModeCallback.java index 6b1103e7324..4927ae672df 100644 --- a/chromium/weblayer/browser/java/org/chromium/weblayer_private/ActionModeCallback.java +++ b/chromium/weblayer/browser/java/org/chromium/weblayer_private/ActionModeCallback.java @@ -7,31 +7,68 @@ package org.chromium.weblayer_private; import android.app.SearchManager; import android.content.Intent; import android.content.pm.PackageManager; +import android.os.RemoteException; import android.view.ActionMode; import android.view.Menu; import android.view.MenuItem; +import androidx.annotation.Nullable; + import org.chromium.base.PackageManagerUtils; import org.chromium.content_public.browser.ActionModeCallbackHelper; import org.chromium.content_public.browser.SelectionPopupController; import org.chromium.content_public.browser.WebContents; +import org.chromium.weblayer_private.interfaces.APICallException; +import org.chromium.weblayer_private.interfaces.ActionModeItemType; +import org.chromium.weblayer_private.interfaces.ITabClient; +import org.chromium.weblayer_private.interfaces.ObjectWrapper; /** * A class that handles selection action mode for WebLayer. */ public final class ActionModeCallback implements ActionMode.Callback { private final ActionModeCallbackHelper mHelper; + // Can be null during init. + private @Nullable ITabClient mTabClient; + + // Bitfield of @ActionModeItemType values. + private int mActionModeOverride; + + // Convert from content ActionModeCallbackHelper.MENU_ITEM_* values to + // @ActionModeItemType values. + private static int contentToWebLayerType(int contentType) { + switch (contentType) { + case ActionModeCallbackHelper.MENU_ITEM_SHARE: + return ActionModeItemType.SHARE; + case ActionModeCallbackHelper.MENU_ITEM_WEB_SEARCH: + return ActionModeItemType.WEB_SEARCH; + case ActionModeCallbackHelper.MENU_ITEM_PROCESS_TEXT: + case 0: + return 0; + default: + assert false; + return 0; + } + } public ActionModeCallback(WebContents webContents) { mHelper = SelectionPopupController.fromWebContents(webContents).getActionModeCallbackHelper(); } + public void setTabClient(ITabClient tabClient) { + mTabClient = tabClient; + } + + public void setOverride(int actionModeItemTypes) { + mActionModeOverride = actionModeItemTypes; + } + @Override public final boolean onCreateActionMode(ActionMode mode, Menu menu) { int allowedActionModes = ActionModeCallbackHelper.MENU_ITEM_PROCESS_TEXT | ActionModeCallbackHelper.MENU_ITEM_SHARE; - if (isWebSearchAvailable()) { + if ((mActionModeOverride & ActionModeItemType.WEB_SEARCH) != 0 || isWebSearchAvailable()) { allowedActionModes |= ActionModeCallbackHelper.MENU_ITEM_WEB_SEARCH; } mHelper.setAllowedMenuItems(allowedActionModes); @@ -53,7 +90,19 @@ public final class ActionModeCallback implements ActionMode.Callback { @Override public final boolean onActionItemClicked(ActionMode mode, MenuItem item) { - return mHelper.onActionItemClicked(mode, item); + int menuItemType = contentToWebLayerType(mHelper.getAllowedMenuItemIfAny(mode, item)); + if ((menuItemType & mActionModeOverride) == 0) { + return mHelper.onActionItemClicked(mode, item); + } + assert WebLayerFactoryImpl.getClientMajorVersion() >= 88; + try { + mTabClient.onActionItemClicked( + menuItemType, ObjectWrapper.wrap(mHelper.getSelectedText())); + } catch (RemoteException e) { + throw new APICallException(e); + } + mode.finish(); + return true; } @Override diff --git a/chromium/weblayer/browser/java/org/chromium/weblayer_private/BrowserControlsContainerView.java b/chromium/weblayer/browser/java/org/chromium/weblayer_private/BrowserControlsContainerView.java index f842c3a3c6e..131769baba0 100644 --- a/chromium/weblayer/browser/java/org/chromium/weblayer_private/BrowserControlsContainerView.java +++ b/chromium/weblayer/browser/java/org/chromium/weblayer_private/BrowserControlsContainerView.java @@ -161,6 +161,11 @@ class BrowserControlsContainerView extends FrameLayout { * Requests that the browser controls visibility state be changed. */ void setAnimationConstraint(@BrowserControlsState int constraint); + + /** + * Called when the offset of the controls changes. + */ + void onOffsetsChanged(boolean isTop, int controlsOffset); } BrowserControlsContainerView(Context context, ContentViewRenderView contentViewRenderView, @@ -371,7 +376,12 @@ class BrowserControlsContainerView extends FrameLayout { mLastHeight = height; if (mLastWidth > 0 && mLastHeight > 0 && mViewResourceAdapter == null) { createAdapterAndLayer(); - if (mLastShownAmountWithView == DEFAULT_LAST_SHOWN_AMOUNT && mSavedState != null) { + if (mIsFullscreen) { + // This calls setControlsOffset() as onOffsetsChanged() does (mostly) nothing when + // fullscreen. + setControlsOffset(mIsTop ? -mLastHeight : mLastHeight, 0); + } else if (mLastShownAmountWithView == DEFAULT_LAST_SHOWN_AMOUNT + && mSavedState != null) { // If there wasn't a View before and we have non-empty saved state from a previous // BrowserControlsContainerView instance, apply those saved offsets now. We can't // rely on BrowserControlsOffsetManager to notify us of the correct location as we @@ -498,6 +508,7 @@ class BrowserControlsContainerView extends FrameLayout { BrowserControlsContainerViewJni.get().setBottomControlsOffset( mNativeBrowserControlsContainerView); } + mDelegate.onOffsetsChanged(mIsTop, mControlsOffset); } private void reportHeightChange() { diff --git a/chromium/weblayer/browser/java/org/chromium/weblayer_private/BrowserImpl.java b/chromium/weblayer/browser/java/org/chromium/weblayer_private/BrowserImpl.java index 689b89e8df8..534373f07d3 100644 --- a/chromium/weblayer/browser/java/org/chromium/weblayer_private/BrowserImpl.java +++ b/chromium/weblayer/browser/java/org/chromium/weblayer_private/BrowserImpl.java @@ -71,12 +71,20 @@ public class BrowserImpl extends IBrowser.Stub implements View.OnAttachStateChan private final UrlBarControllerImpl mUrlBarController; private boolean mFragmentStarted; private boolean mFragmentResumed; - private boolean mFragmentStoppedForConfigurationChange; + + // Tracks whether the fragment is in the middle of a configuration change and was attached when + // the configuration change started. This is set to true in onFragmentStop() and false when + // isViewAttachedToWindow() is true in either onViewAttachedToWindow() or onFragmentStarted(). + // It's important to only set this to false when isViewAttachedToWindow() is true, as otherwise + // the WebContents may be prematurely hidden. + private boolean mInConfigurationChangeAndWasAttached; + // Cache the value instead of querying system every time. private Boolean mPasswordEchoEnabled; private Boolean mDarkThemeEnabled; private Float mFontScale; private boolean mViewAttachedToWindow; + private boolean mNotifyOnBrowserControlsOffsetsChanged; // Created in the constructor from saved state and used in setClient(). private PersistenceInfo mPersistenceInfo; @@ -156,7 +164,7 @@ public class BrowserImpl extends IBrowser.Stub implements View.OnAttachStateChan mWindowAndroid = windowAndroid; mEmbedderActivityContext = embedderAppContext; mViewController = new BrowserViewController( - windowAndroid, this, mViewControllerState, mFragmentStoppedForConfigurationChange); + windowAndroid, this, mViewControllerState, mInConfigurationChangeAndWasAttached); mLocaleReceiver = new LocaleChangedBroadcastReceiver(windowAndroid.getContext().get()); mPasswordEchoEnabled = null; } @@ -231,8 +239,10 @@ public class BrowserImpl extends IBrowser.Stub implements View.OnAttachStateChan @Override public TabImpl createTab() { - TabImpl tab = new TabImpl(mProfile, mWindowAndroid); - addTab(tab); + TabImpl tab = new TabImpl(this, mProfile, mWindowAndroid); + // This needs |alwaysAdd| set to true as the Tab is created with the Browser already set to + // this. + addTab(tab, /* alwaysAdd */ true); return tab; } @@ -282,14 +292,17 @@ public class BrowserImpl extends IBrowser.Stub implements View.OnAttachStateChan @Override public void addTab(ITab iTab) { StrictModeWorkaround.apply(); - TabImpl tab = (TabImpl) iTab; - if (tab.getBrowser() == this) return; + addTab((TabImpl) iTab, /* alwaysAdd */ false); + } + + private void addTab(TabImpl tab, boolean alwaysAdd) { + if (!alwaysAdd && tab.getBrowser() == this) return; BrowserImplJni.get().addTab(mNativeBrowser, tab.getNativeTab()); } @CalledByNative private void createJavaTabForNativeTab(long nativeTab) { - new TabImpl(mProfile, mWindowAndroid, nativeTab); + new TabImpl(this, mProfile, mWindowAndroid, nativeTab); } private void checkPreferences() { @@ -356,36 +369,23 @@ public class BrowserImpl extends IBrowser.Stub implements View.OnAttachStateChan } @CalledByNative - private void onTabAdded(TabImpl tab) { + private void onTabAdded(TabImpl tab) throws RemoteException { tab.attachToBrowser(this); - try { - if (mClient != null) mClient.onTabAdded(tab); - } catch (RemoteException e) { - throw new APICallException(e); - } + if (mClient != null) mClient.onTabAdded(tab); } @CalledByNative - private void onActiveTabChanged(TabImpl tab) { + private void onActiveTabChanged(TabImpl tab) throws RemoteException { if (mViewController != null) mViewController.setActiveTab(tab); - if (mInDestroy) return; - try { - if (mClient != null) { - mClient.onActiveTabChanged(tab != null ? tab.getId() : 0); - } - } catch (RemoteException e) { - throw new APICallException(e); + if (!mInDestroy && mClient != null) { + mClient.onActiveTabChanged(tab != null ? tab.getId() : 0); } } @CalledByNative - private void onTabRemoved(TabImpl tab) { + private void onTabRemoved(TabImpl tab) throws RemoteException { if (mInDestroy) return; - try { - if (mClient != null) mClient.onTabRemoved(tab.getId()); - } catch (RemoteException e) { - throw new APICallException(e); - } + if (mClient != null) mClient.onTabRemoved(tab.getId()); // This doesn't reset state on TabImpl as |browser| is either about to be // destroyed, or switching to a different fragment. } @@ -471,6 +471,21 @@ public class BrowserImpl extends IBrowser.Stub implements View.OnAttachStateChan } @Override + public void setBrowserControlsOffsetsEnabled(boolean enable) { + mNotifyOnBrowserControlsOffsetsChanged = enable; + } + + public void onBrowserControlsOffsetsChanged(TabImpl tab, boolean isTop, int controlsOffset) { + if (mNotifyOnBrowserControlsOffsetsChanged && tab == getActiveTab()) { + try { + mClient.onBrowserControlsOffsetsChanged(isTop, controlsOffset); + } catch (RemoteException e) { + throw new APICallException(e); + } + } + } + + @Override public boolean isRestoringPreviousState() { return BrowserImplJni.get().isRestoringPreviousState(mNativeBrowser); } @@ -504,15 +519,17 @@ public class BrowserImpl extends IBrowser.Stub implements View.OnAttachStateChan } public void onFragmentStart() { - mFragmentStoppedForConfigurationChange = false; mFragmentStarted = true; + if (mViewAttachedToWindow) { + mInConfigurationChangeAndWasAttached = false; + } BrowserImplJni.get().onFragmentStart(mNativeBrowser); updateAllTabs(); checkPreferences(); } public void onFragmentStop(boolean forConfigurationChange) { - mFragmentStoppedForConfigurationChange = forConfigurationChange; + mInConfigurationChangeAndWasAttached = forConfigurationChange; mFragmentStarted = false; updateAllTabs(); } @@ -536,8 +553,8 @@ public class BrowserImpl extends IBrowser.Stub implements View.OnAttachStateChan return mFragmentResumed; } - public boolean isFragmentStoppedForConfigurationChange() { - return mFragmentStoppedForConfigurationChange; + public boolean isInConfigurationChangeAndWasAttached() { + return mInConfigurationChangeAndWasAttached; } public boolean isViewAttachedToWindow() { @@ -547,6 +564,9 @@ public class BrowserImpl extends IBrowser.Stub implements View.OnAttachStateChan @Override public void onViewAttachedToWindow(View v) { mViewAttachedToWindow = true; + if (mFragmentStarted) { + mInConfigurationChangeAndWasAttached = false; + } updateAllTabsViewAttachedState(); } diff --git a/chromium/weblayer/browser/java/org/chromium/weblayer_private/BrowserViewController.java b/chromium/weblayer/browser/java/org/chromium/weblayer_private/BrowserViewController.java index fd9628bb755..013a665589a 100644 --- a/chromium/weblayer/browser/java/org/chromium/weblayer_private/BrowserViewController.java +++ b/chromium/weblayer/browser/java/org/chromium/weblayer_private/BrowserViewController.java @@ -270,6 +270,12 @@ public final class BrowserViewController } @Override + public void onOffsetsChanged(boolean isTop, int controlsOffset) { + if (mTab == null) return; + mTab.getBrowser().onBrowserControlsOffsetsChanged(mTab, isTop, controlsOffset); + } + + @Override public void onGestureStateChanged() { // This is called from |mGestureStateTracker|. assert mGestureStateTracker != null; diff --git a/chromium/weblayer/browser/java/org/chromium/weblayer_private/ContentViewRenderView.java b/chromium/weblayer/browser/java/org/chromium/weblayer_private/ContentViewRenderView.java index 983632cae0d..2619d519d0f 100644 --- a/chromium/weblayer/browser/java/org/chromium/weblayer_private/ContentViewRenderView.java +++ b/chromium/weblayer/browser/java/org/chromium/weblayer_private/ContentViewRenderView.java @@ -24,6 +24,7 @@ import android.widget.FrameLayout; import android.widget.RelativeLayout; import androidx.annotation.IntDef; +import androidx.annotation.Nullable; import org.chromium.base.annotations.CalledByNative; import org.chromium.base.annotations.JNINamespace; @@ -677,7 +678,7 @@ public class ContentViewRenderView ContentViewRenderViewJni.get().init(ContentViewRenderView.this, rootWindow); assert mNativeContentViewRenderView != 0; mWindowAndroid = rootWindow; - requestMode(mode, (Boolean result) -> {}); + requestMode(mode, null); mDisplayAndroidObserver = new DisplayAndroid.DisplayAndroidObserver() { @Override public void onRotationChanged(int rotation) { @@ -689,10 +690,9 @@ public class ContentViewRenderView updateBackgroundColor(); } - public void requestMode(@Mode int mode, ValueCallback<Boolean> callback) { + public void requestMode(@Mode int mode, @Nullable ValueCallback<Boolean> callback) { boolean allowSurfaceControl = !mSelectionHandlesActive; assert mode == MODE_SURFACE_VIEW || mode == MODE_TEXTURE_VIEW; - assert callback != null; if (mRequested != null && (mRequested.getMode() != mode || mRequested.getAllowSurfaceControl() != allowSurfaceControl)) { @@ -710,7 +710,7 @@ public class ContentViewRenderView listener.setRequestData(mRequested); } assert mRequested.getMode() == mode; - mRequested.addCallback(callback); + if (callback != null) mRequested.addCallback(callback); } /** @@ -805,7 +805,7 @@ public class ContentViewRenderView // requestMode will take into account the updated |mSelectionHandlesActive| // and respond appropriately, even if mode is the same. - requestMode(mCurrent.getMode(), (Boolean result) -> {}); + requestMode(mCurrent.getMode(), null); } public InsetObserverView getInsetObserverView() { diff --git a/chromium/weblayer/browser/java/org/chromium/weblayer_private/CookieManagerImpl.java b/chromium/weblayer/browser/java/org/chromium/weblayer_private/CookieManagerImpl.java index e2c56ccc4d6..ba3e80a7566 100644 --- a/chromium/weblayer/browser/java/org/chromium/weblayer_private/CookieManagerImpl.java +++ b/chromium/weblayer/browser/java/org/chromium/weblayer_private/CookieManagerImpl.java @@ -11,7 +11,6 @@ import org.chromium.base.Callback; import org.chromium.base.annotations.CalledByNative; import org.chromium.base.annotations.JNINamespace; import org.chromium.base.annotations.NativeMethods; -import org.chromium.weblayer_private.interfaces.APICallException; import org.chromium.weblayer_private.interfaces.CookieChangeCause; import org.chromium.weblayer_private.interfaces.ICookieChangedCallbackClient; import org.chromium.weblayer_private.interfaces.ICookieManager; @@ -73,13 +72,9 @@ public final class CookieManagerImpl extends ICookieManager.Stub { } @CalledByNative - private static void onCookieChange( - ICookieChangedCallbackClient callback, String cookie, int cause) { - try { - callback.onCookieChanged(cookie, mojoCauseToJavaType(cause)); - } catch (RemoteException e) { - throw new APICallException(e); - } + private static void onCookieChange(ICookieChangedCallbackClient callback, String cookie, + int cause) throws RemoteException { + callback.onCookieChanged(cookie, mojoCauseToJavaType(cause)); } @CookieChangeCause diff --git a/chromium/weblayer/browser/java/org/chromium/weblayer_private/CrashReporterControllerImpl.java b/chromium/weblayer/browser/java/org/chromium/weblayer_private/CrashReporterControllerImpl.java index 244b369f07a..4fc1c2f39dd 100644 --- a/chromium/weblayer/browser/java/org/chromium/weblayer_private/CrashReporterControllerImpl.java +++ b/chromium/weblayer/browser/java/org/chromium/weblayer_private/CrashReporterControllerImpl.java @@ -82,6 +82,14 @@ public final class CrashReporterControllerImpl extends ICrashReporterController. AsyncTask.THREAD_POOL_EXECUTOR.execute(() -> { TraceEvent.instant(TAG, "CrashReporterController: Begin uploading crash"); File minidumpFile = getCrashFileManager().getCrashFileWithLocalId(localId); + if (minidumpFile == null) { + try { + mClient.onCrashUploadFailed(localId, "invalid crash id"); + } catch (RemoteException e) { + throw new AndroidRuntimeException(e); + } + return; + } MinidumpUploader.Result result = new MinidumpUploader().upload(minidumpFile); if (result.isSuccess()) { CrashFileManager.markUploadSuccess(minidumpFile); diff --git a/chromium/weblayer/browser/java/org/chromium/weblayer_private/FaviconCallbackProxy.java b/chromium/weblayer/browser/java/org/chromium/weblayer_private/FaviconCallbackProxy.java index 3c74a6dcd1b..219560ada96 100644 --- a/chromium/weblayer/browser/java/org/chromium/weblayer_private/FaviconCallbackProxy.java +++ b/chromium/weblayer/browser/java/org/chromium/weblayer_private/FaviconCallbackProxy.java @@ -53,6 +53,7 @@ public final class FaviconCallbackProxy extends IFaviconFetcher.Stub { @CalledByNative private void onFaviconChanged(Bitmap bitmap) throws RemoteException { + mTab.onFaviconChanged(bitmap); mClient.onFaviconChanged(bitmap); } diff --git a/chromium/weblayer/browser/java/org/chromium/weblayer_private/MojoInterfaceRegistrar.java b/chromium/weblayer/browser/java/org/chromium/weblayer_private/MojoInterfaceRegistrar.java index d124792cd0d..ec49f06da6e 100644 --- a/chromium/weblayer/browser/java/org/chromium/weblayer_private/MojoInterfaceRegistrar.java +++ b/chromium/weblayer/browser/java/org/chromium/weblayer_private/MojoInterfaceRegistrar.java @@ -6,8 +6,11 @@ package org.chromium.weblayer_private; import org.chromium.base.annotations.CalledByNative; import org.chromium.content_public.browser.InterfaceRegistrar; +import org.chromium.content_public.browser.RenderFrameHost; import org.chromium.content_public.browser.WebContents; +import org.chromium.payments.mojom.PaymentRequest; import org.chromium.services.service_manager.InterfaceRegistry; +import org.chromium.weblayer_private.payments.WebLayerPaymentRequestFactory; import org.chromium.webshare.mojom.ShareService; /** @@ -17,6 +20,8 @@ class MojoInterfaceRegistrar { @CalledByNative private static void registerMojoInterfaces() { InterfaceRegistrar.Registry.addWebContentsRegistrar(new WebContentsInterfaceRegistrar()); + InterfaceRegistrar.Registry.addRenderFrameHostRegistrar( + new RenderFrameHostInterfaceRegistrar()); } private static class WebContentsInterfaceRegistrar implements InterfaceRegistrar<WebContents> { @@ -25,4 +30,14 @@ class MojoInterfaceRegistrar { registry.addInterface(ShareService.MANAGER, new WebShareServiceFactory(webContents)); } } + + private static class RenderFrameHostInterfaceRegistrar + implements InterfaceRegistrar<RenderFrameHost> { + @Override + public void registerInterfaces( + InterfaceRegistry registry, final RenderFrameHost renderFrameHost) { + registry.addInterface( + PaymentRequest.MANAGER, new WebLayerPaymentRequestFactory(renderFrameHost)); + } + } } diff --git a/chromium/weblayer/browser/java/org/chromium/weblayer_private/NavigationControllerImpl.java b/chromium/weblayer/browser/java/org/chromium/weblayer_private/NavigationControllerImpl.java index 3151068451c..0436c92162e 100644 --- a/chromium/weblayer/browser/java/org/chromium/weblayer_private/NavigationControllerImpl.java +++ b/chromium/weblayer/browser/java/org/chromium/weblayer_private/NavigationControllerImpl.java @@ -5,8 +5,10 @@ package org.chromium.weblayer_private; import android.os.RemoteException; +import android.os.SystemClock; import android.webkit.WebResourceResponse; +import org.chromium.base.TimeUtilsJni; import org.chromium.base.annotations.CalledByNative; import org.chromium.base.annotations.JNINamespace; import org.chromium.base.annotations.NativeMethods; @@ -28,6 +30,10 @@ public final class NavigationControllerImpl extends INavigationController.Stub { private long mNativeNavigationController; private INavigationControllerClient mNavigationControllerClient; + // Conversion between native TimeTicks and SystemClock.uptimeMillis(). + private long mNativeTickOffsetUs; + private boolean mNativeTickOffsetUsComputed; + public NavigationControllerImpl(TabImpl tab, INavigationControllerClient client) { mTab = tab; mNavigationControllerClient = client; @@ -215,11 +221,43 @@ public final class NavigationControllerImpl extends INavigationController.Stub { } @CalledByNative + private void onFirstContentfulPaint2( + long navigationStartTick, long firstContentfulPaintDurationMs) throws RemoteException { + if (WebLayerFactoryImpl.getClientMajorVersion() < 88) return; + + mNavigationControllerClient.onFirstContentfulPaint2( + (navigationStartTick - getNativeTickOffsetUs()) / 1000, + firstContentfulPaintDurationMs); + } + + @CalledByNative + private void onLargestContentfulPaint(long navigationStartTick, + long largestContentfulPaintDurationMs) throws RemoteException { + if (WebLayerFactoryImpl.getClientMajorVersion() < 88) return; + + mNavigationControllerClient.onLargestContentfulPaint( + (navigationStartTick - getNativeTickOffsetUs()) / 1000, + largestContentfulPaintDurationMs); + } + + @CalledByNative private void onOldPageNoLongerRendered(String uri) throws RemoteException { if (WebLayerFactoryImpl.getClientMajorVersion() < 85) return; mNavigationControllerClient.onOldPageNoLongerRendered(uri); } + private long getNativeTickOffsetUs() { + // See logic in CustomTabsConnection.java that this was based on. + if (!mNativeTickOffsetUsComputed) { + // Compute offset from time ticks to uptimeMillis. + mNativeTickOffsetUsComputed = true; + long nativeNowUs = TimeUtilsJni.get().getTimeTicksNowUs(); + long javaNowUs = SystemClock.uptimeMillis() * 1000; + mNativeTickOffsetUs = nativeNowUs - javaNowUs; + } + return mNativeTickOffsetUs; + } + private static final class NavigateParamsImpl extends INavigateParams.Stub { private boolean mReplaceCurrentEntry; private boolean mIntentProcessingDisabled; diff --git a/chromium/weblayer/browser/java/org/chromium/weblayer_private/NavigationImpl.java b/chromium/weblayer/browser/java/org/chromium/weblayer_private/NavigationImpl.java index 7815edb19f1..1430e36d1d4 100644 --- a/chromium/weblayer/browser/java/org/chromium/weblayer_private/NavigationImpl.java +++ b/chromium/weblayer/browser/java/org/chromium/weblayer_private/NavigationImpl.java @@ -161,6 +161,13 @@ public final class NavigationImpl extends INavigation.Stub { return NavigationImplJni.get().isReload(mNativeNavigationImpl); } + @Override + public void disableNetworkErrorAutoReload() { + if (!NavigationImplJni.get().disableNetworkErrorAutoReload(mNativeNavigationImpl)) { + throw new IllegalStateException(); + } + } + private void throwIfNativeDestroyed() { if (mNativeNavigationImpl == 0) { throw new IllegalStateException("Using Navigation after native destroyed"); @@ -182,6 +189,8 @@ public final class NavigationImpl extends INavigation.Stub { return LoadError.CONNECTIVITY_ERROR; case ImplLoadError.OTHER_ERROR: return LoadError.OTHER_ERROR; + case ImplLoadError.SAFE_BROWSING_ERROR: + return LoadError.SAFE_BROWSING_ERROR; default: throw new IllegalArgumentException("Unexpected load error " + loadError); } @@ -211,5 +220,6 @@ public final class NavigationImpl extends INavigation.Stub { boolean setUserAgentString(long nativeNavigationImpl, String value); boolean isPageInitiated(long nativeNavigationImpl); boolean isReload(long nativeNavigationImpl); + boolean disableNetworkErrorAutoReload(long nativeNavigationImpl); } } diff --git a/chromium/weblayer/browser/java/org/chromium/weblayer_private/PageInfoControllerDelegateImpl.java b/chromium/weblayer/browser/java/org/chromium/weblayer_private/PageInfoControllerDelegateImpl.java index 0979cab4c25..0bd9d7b7e58 100644 --- a/chromium/weblayer/browser/java/org/chromium/weblayer_private/PageInfoControllerDelegateImpl.java +++ b/chromium/weblayer/browser/java/org/chromium/weblayer_private/PageInfoControllerDelegateImpl.java @@ -12,6 +12,7 @@ import android.graphics.drawable.Drawable; import android.webkit.ValueCallback; import androidx.annotation.NonNull; +import androidx.annotation.Nullable; import org.chromium.base.Callback; import org.chromium.base.StrictModeContext; @@ -127,6 +128,12 @@ public class PageInfoControllerDelegateImpl extends PageInfoControllerDelegate { })); } + @Override + @Nullable + public Drawable getPreviewUiIcon() { + return null; + } + private static boolean isHttpOrHttps(GURL url) { String scheme = url.getScheme(); return UrlConstants.HTTP_SCHEME.equals(scheme) || UrlConstants.HTTPS_SCHEME.equals(scheme); diff --git a/chromium/weblayer/browser/java/org/chromium/weblayer_private/TabImpl.java b/chromium/weblayer/browser/java/org/chromium/weblayer_private/TabImpl.java index 96d559d7931..1fc0f36e619 100644 --- a/chromium/weblayer/browser/java/org/chromium/weblayer_private/TabImpl.java +++ b/chromium/weblayer/browser/java/org/chromium/weblayer_private/TabImpl.java @@ -4,7 +4,9 @@ package org.chromium.weblayer_private; +import android.Manifest.permission; import android.app.Activity; +import android.content.pm.PackageManager; import android.graphics.Bitmap; import android.graphics.RectF; import android.os.Build; @@ -19,6 +21,7 @@ import android.view.ViewStructure; import android.view.autofill.AutofillValue; import android.webkit.ValueCallback; +import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.annotation.VisibleForTesting; @@ -34,6 +37,7 @@ import org.chromium.components.browser_ui.util.BrowserControlsVisibilityDelegate import org.chromium.components.browser_ui.util.ComposedBrowserControlsVisibilityDelegate; import org.chromium.components.browser_ui.widget.InsetObserverView; import org.chromium.components.embedder_support.contextmenu.ContextMenuParams; +import org.chromium.components.embedder_support.util.UrlUtilities; import org.chromium.components.external_intents.InterceptNavigationDelegateImpl; import org.chromium.components.find_in_page.FindInPageBridge; import org.chromium.components.find_in_page.FindMatchRectsDetails; @@ -55,6 +59,7 @@ import org.chromium.ui.base.ViewAndroidDelegate; import org.chromium.ui.base.WindowAndroid; import org.chromium.url.GURL; import org.chromium.weblayer_private.interfaces.APICallException; +import org.chromium.weblayer_private.interfaces.IContextMenuParams; import org.chromium.weblayer_private.interfaces.IDownloadCallbackClient; import org.chromium.weblayer_private.interfaces.IErrorPageCallbackClient; import org.chromium.weblayer_private.interfaces.IFaviconFetcher; @@ -100,9 +105,8 @@ public final class TabImpl extends ITab.Stub { private FullscreenCallbackProxy mFullscreenCallbackProxy; private TabViewAndroidDelegate mViewAndroidDelegate; private GoogleAccountsCallbackProxy mGoogleAccountsCallbackProxy; - // BrowserImpl this TabImpl is in. This is null before attached to a Browser. While this is null - // before attached, there are code paths that may trigger calling methods before set. - @Nullable + // BrowserImpl this TabImpl is in. + @NonNull private BrowserImpl mBrowser; /** * The AutofillProvider that integrates with system-level autofill. This is null until @@ -138,6 +142,7 @@ public final class TabImpl extends ITab.Stub { private DisplayCutoutController mDisplayCutoutController; private boolean mPostContainerViewInitDone; + private ActionModeCallback mActionModeCallback; private WebLayerAccessibilityUtil.Observer mAccessibilityObserver; @@ -236,7 +241,8 @@ public final class TabImpl extends ITab.Stub { return sTabMap.get(tabId); } - public TabImpl(ProfileImpl profile, WindowAndroid windowAndroid) { + public TabImpl(BrowserImpl browser, ProfileImpl profile, WindowAndroid windowAndroid) { + mBrowser = browser; mId = ++sNextId; init(profile, windowAndroid, TabImplJni.get().createTab(profile.getNativeProfile(), this)); } @@ -245,8 +251,10 @@ public final class TabImpl extends ITab.Stub { * This constructor is called when the native side triggers creation of a TabImpl * (as happens with popups and other scenarios). */ - public TabImpl(ProfileImpl profile, WindowAndroid windowAndroid, long nativeTab) { + public TabImpl( + BrowserImpl browser, ProfileImpl profile, WindowAndroid windowAndroid, long nativeTab) { mId = ++sNextId; + mBrowser = browser; TabImplJni.get().setJavaImpl(nativeTab, TabImpl.this); init(profile, windowAndroid, nativeTab); } @@ -308,7 +316,7 @@ public final class TabImpl extends ITab.Stub { // before installing this observer. if (WebLayerFactoryImpl.getClientMajorVersion() >= 85) { mMediaSessionHelper = new MediaSessionHelper( - mWebContents, MediaSessionManager.createMediaSessionHelperDelegate(mId)); + mWebContents, MediaSessionManager.createMediaSessionHelperDelegate(this)); } } @@ -318,7 +326,8 @@ public final class TabImpl extends ITab.Stub { mPostContainerViewInitDone = true; SelectionPopupController controller = SelectionPopupController.fromWebContents(mWebContents); - controller.setActionModeCallback(new ActionModeCallback(mWebContents)); + mActionModeCallback = new ActionModeCallback(mWebContents); + controller.setActionModeCallback(mActionModeCallback); controller.setSelectionClient(SelectionClient.createSmartSelectionClient(mWebContents)); } @@ -334,6 +343,9 @@ public final class TabImpl extends ITab.Stub { * Sets the BrowserImpl this TabImpl is contained in. */ public void attachToBrowser(BrowserImpl browser) { + // NOTE: during tab creation this is called with |mBrowser| set to |browser|. This happens + // because the tab is created with |mBrowser| already set (to avoid having a bunch of null + // checks). mBrowser = browser; updateFromBrowser(); } @@ -417,7 +429,6 @@ public final class TabImpl extends ITab.Stub { public void onAttachedToViewController( long topControlsContainerViewHandle, long bottomControlsContainerViewHandle) { // attachToFragment() must be called before activate(). - assert mBrowser != null; TabImplJni.get().setBrowserControlsContainerViews( mNativeTab, topControlsContainerViewHandle, bottomControlsContainerViewHandle); mInfoBarContainer.onTabAttachedToViewController(); @@ -454,7 +465,7 @@ public final class TabImpl extends ITab.Stub { public boolean isVisible() { return isActiveTab() && ((mBrowser.isStarted() && mBrowser.isViewAttachedToWindow()) - || mBrowser.isFragmentStoppedForConfigurationChange()); + || mBrowser.isInConfigurationChangeAndWasAttached()); } @CalledByNative @@ -469,7 +480,7 @@ public final class TabImpl extends ITab.Stub { } public boolean isActiveTab() { - return mBrowser != null && mBrowser.getActiveTab() == this; + return mBrowser.getActiveTab() == this; } private void updateWebContentsVisibility() { @@ -529,6 +540,7 @@ public final class TabImpl extends ITab.Stub { StrictModeWorkaround.apply(); mClient = client; mTabCallbackProxy = new TabCallbackProxy(mNativeTab, client); + mActionModeCallback.setTabClient(mClient); } @Override @@ -596,11 +608,13 @@ public final class TabImpl extends ITab.Stub { @Override public void setTranslateTargetLanguage(String targetLanguage) { + StrictModeWorkaround.apply(); TabImplJni.get().setTranslateTargetLanguage(mNativeTab, targetLanguage); } @Override public void setScrollOffsetsEnabled(boolean enabled) { + StrictModeWorkaround.apply(); if (enabled) { if (mGestureStateListenerWithScroll == null) { mGestureStateListenerWithScroll = new GestureStateListenerWithScroll() { @@ -624,6 +638,49 @@ public final class TabImpl extends ITab.Stub { } } + @Override + public void setFloatingActionModeOverride(int actionModeItemTypes) { + StrictModeWorkaround.apply(); + mActionModeCallback.setOverride(actionModeItemTypes); + } + + @Override + public void setDesktopUserAgentEnabled(boolean enable) { + StrictModeWorkaround.apply(); + TabImplJni.get().setDesktopUserAgentEnabled(mNativeTab, enable); + } + + @Override + public boolean isDesktopUserAgentEnabled() { + StrictModeWorkaround.apply(); + return TabImplJni.get().isDesktopUserAgentEnabled(mNativeTab); + } + + @Override + public void download(IContextMenuParams contextMenuParams) { + StrictModeWorkaround.apply(); + NativeContextMenuParamsHolder nativeContextMenuParamsHolder = + (NativeContextMenuParamsHolder) contextMenuParams; + + WindowAndroid window = getBrowser().getWindowAndroid(); + if (window.hasPermission(permission.WRITE_EXTERNAL_STORAGE)) { + continueDownload(nativeContextMenuParamsHolder); + return; + } + + String[] requestPermissions = new String[] {permission.WRITE_EXTERNAL_STORAGE}; + window.requestPermissions(requestPermissions, (permissions, grantResults) -> { + if (grantResults.length == 1 && grantResults[0] == PackageManager.PERMISSION_GRANTED) { + continueDownload(nativeContextMenuParamsHolder); + } + }); + } + + private void continueDownload(NativeContextMenuParamsHolder nativeContextMenuParamsHolder) { + TabImplJni.get().download( + mNativeTab, nativeContextMenuParamsHolder.mNativeContextMenuParams); + } + public void removeFaviconCallbackProxy(FaviconCallbackProxy proxy) { mFaviconCallbackProxies.remove(proxy); } @@ -809,16 +866,12 @@ public final class TabImpl extends ITab.Stub { } @CalledByNative - private void onFindResultAvailable( - int numberOfMatches, int activeMatchOrdinal, boolean finalUpdate) { - try { - if (mFindInPageCallbackClient != null) { - // The WebLayer API deals in indices instead of ordinals. - mFindInPageCallbackClient.onFindResult( - numberOfMatches, activeMatchOrdinal - 1, finalUpdate); - } - } catch (RemoteException e) { - throw new AndroidRuntimeException(e); + private void onFindResultAvailable(int numberOfMatches, int activeMatchOrdinal, + boolean finalUpdate) throws RemoteException { + if (mFindInPageCallbackClient != null) { + // The WebLayer API deals in indices instead of ordinals. + mFindInPageCallbackClient.onFindResult( + numberOfMatches, activeMatchOrdinal - 1, finalUpdate); } if (mFindResultBar != null) { @@ -947,6 +1000,11 @@ public final class TabImpl extends ITab.Stub { mMediaStreamManager.destroy(); mMediaStreamManager = null; + if (mMediaSessionHelper != null) { + mMediaSessionHelper.destroy(); + mMediaSessionHelper = null; + } + // Destroying FaviconCallbackProxy removes from mFaviconCallbackProxies. Copy to avoid // problems. Set<FaviconCallbackProxy> faviconCallbackProxies = mFaviconCallbackProxies; @@ -1015,14 +1073,56 @@ public final class TabImpl extends ITab.Stub { return TextUtils.isEmpty(s) ? null : s; } + private static class NativeContextMenuParamsHolder extends IContextMenuParams.Stub { + // Note: avoid adding more members since an object with a finalizer will delay GC of any + // object it references. + private final long mNativeContextMenuParams; + + NativeContextMenuParamsHolder(long nativeContextMenuParams) { + mNativeContextMenuParams = nativeContextMenuParams; + } + + /** + * A finalizer is required to ensure that the native object associated with + * this object gets destructed, otherwise there would be a memory leak. + * + * This is safe because it makes a simple call into C++ code that is both + * thread-safe and very fast. + * + * @see java.lang.Object#finalize() + */ + @Override + protected final void finalize() throws Throwable { + super.finalize(); + TabImplJni.get().destroyContextMenuParams(mNativeContextMenuParams); + } + } + @CalledByNative - private void showContextMenu(ContextMenuParams params) throws RemoteException { + private void showContextMenu(ContextMenuParams params, long nativeContextMenuParams) + throws RemoteException { if (WebLayerFactoryImpl.getClientMajorVersion() < 82) return; - mClient.showContextMenu(ObjectWrapper.wrap(params.getPageUrl()), + if (WebLayerFactoryImpl.getClientMajorVersion() < 88) { + mClient.showContextMenu(ObjectWrapper.wrap(params.getPageUrl()), + ObjectWrapper.wrap(nonEmptyOrNull(params.getLinkUrl())), + ObjectWrapper.wrap(nonEmptyOrNull(params.getLinkText())), + ObjectWrapper.wrap(nonEmptyOrNull(params.getTitleText())), + ObjectWrapper.wrap(nonEmptyOrNull(params.getSrcUrl()))); + return; + } + + boolean canDownload = + (params.isImage() && UrlUtilities.isDownloadableScheme(params.getSrcUrl())) + || (params.isVideo() && UrlUtilities.isDownloadableScheme(params.getSrcUrl()) + && params.canSaveMedia()) + || (params.isAnchor() && UrlUtilities.isDownloadableScheme(params.getLinkUrl())); + mClient.showContextMenu2(ObjectWrapper.wrap(params.getPageUrl()), ObjectWrapper.wrap(nonEmptyOrNull(params.getLinkUrl())), ObjectWrapper.wrap(nonEmptyOrNull(params.getLinkText())), ObjectWrapper.wrap(nonEmptyOrNull(params.getTitleText())), - ObjectWrapper.wrap(nonEmptyOrNull(params.getSrcUrl()))); + ObjectWrapper.wrap(nonEmptyOrNull(params.getSrcUrl())), params.isImage(), + params.isVideo(), canDownload, + new NativeContextMenuParamsHolder(nativeContextMenuParams)); } @VisibleForTesting @@ -1037,8 +1137,6 @@ public final class TabImpl extends ITab.Stub { } private void onBrowserControlsConstraintUpdated(int constraint) { - // WARNING: this may be called before attached. This means |mBrowser| may be null. - // If something has overridden the FIP's SHOWN constraint, cancel FIP. This causes FIP to // dismiss when entering fullscreen. if (constraint != BrowserControlsState.SHOWN) { @@ -1105,7 +1203,11 @@ public final class TabImpl extends ITab.Stub { @Nullable private BrowserViewController getViewController() { if (!isActiveTab()) return null; - return mBrowser.getPossiblyNullViewController(); + // During rotation it's possible for this to be called before BrowserViewController has been + // updated. Verify BrowserViewController reflects this is the active tab before returning + // it. + BrowserViewController viewController = mBrowser.getPossiblyNullViewController(); + return viewController != null && viewController.getTab() == this ? viewController : null; } @VisibleForTesting @@ -1123,6 +1225,13 @@ public final class TabImpl extends ITab.Stub { return translateInfoBar.getTargetLanguageForTesting(); } + /** Called by {@link FaviconCallbackProxy} when the favicon for the current page has changed. */ + public void onFaviconChanged(Bitmap bitmap) { + if (mMediaSessionHelper != null) { + mMediaSessionHelper.updateFavicon(bitmap); + } + } + @NativeMethods interface Natives { TabImpl fromWebContents(WebContents webContents); @@ -1151,5 +1260,9 @@ public final class TabImpl extends ITab.Stub { boolean canTranslate(long nativeTabImpl); void showTranslateUi(long nativeTabImpl); void setTranslateTargetLanguage(long nativeTabImpl, String targetLanguage); + void setDesktopUserAgentEnabled(long nativeTabImpl, boolean enable); + boolean isDesktopUserAgentEnabled(long nativeTabImpl); + void download(long nativeTabImpl, long nativeContextMenuParams); + void destroyContextMenuParams(long contextMenuParams); } } diff --git a/chromium/weblayer/browser/java/org/chromium/weblayer_private/TranslateCompactInfoBar.java b/chromium/weblayer/browser/java/org/chromium/weblayer_private/TranslateCompactInfoBar.java index a2d2212ee72..937ba551330 100644 --- a/chromium/weblayer/browser/java/org/chromium/weblayer_private/TranslateCompactInfoBar.java +++ b/chromium/weblayer/browser/java/org/chromium/weblayer_private/TranslateCompactInfoBar.java @@ -21,6 +21,7 @@ import org.chromium.base.annotations.JNINamespace; import org.chromium.base.annotations.NativeMethods; import org.chromium.base.metrics.RecordHistogram; import org.chromium.chrome.browser.infobar.ActionType; +import org.chromium.chrome.browser.infobar.InfobarEvent; import org.chromium.components.infobars.InfoBar; import org.chromium.components.infobars.InfoBarCompactLayout; import org.chromium.components.translate.TranslateMenu; @@ -76,38 +77,6 @@ public class TranslateCompactInfoBar extends InfoBar private static final String INFOBAR_HISTOGRAM_TRANSLATION_COUNT = "Translate.CompactInfobar.TranslationsPerPage"; - /** - * This is used to back a UMA histogram, so it should be treated as - * append-only. The values should not be changed or reused, and - * INFOBAR_HISTOGRAM_BOUNDARY should be the last. - */ - private static final int INFOBAR_IMPRESSION = 0; - private static final int INFOBAR_TARGET_TAB_TRANSLATE = 1; - private static final int INFOBAR_DECLINE = 2; - private static final int INFOBAR_OPTIONS = 3; - private static final int INFOBAR_MORE_LANGUAGES = 4; - private static final int INFOBAR_MORE_LANGUAGES_TRANSLATE = 5; - private static final int INFOBAR_PAGE_NOT_IN = 6; - private static final int INFOBAR_ALWAYS_TRANSLATE = 7; - private static final int INFOBAR_NEVER_TRANSLATE = 8; - private static final int INFOBAR_NEVER_TRANSLATE_SITE = 9; - private static final int INFOBAR_SCROLL_HIDE = 10; - private static final int INFOBAR_SCROLL_SHOW = 11; - private static final int INFOBAR_REVERT = 12; - private static final int INFOBAR_SNACKBAR_ALWAYS_TRANSLATE_IMPRESSION = 13; - private static final int INFOBAR_SNACKBAR_NEVER_TRANSLATE_IMPRESSION = 14; - private static final int INFOBAR_SNACKBAR_NEVER_TRANSLATE_SITE_IMPRESSION = 15; - private static final int INFOBAR_SNACKBAR_CANCEL_ALWAYS = 16; - private static final int INFOBAR_SNACKBAR_CANCEL_NEVER_SITE = 17; - private static final int INFOBAR_SNACKBAR_CANCEL_NEVER = 18; - private static final int INFOBAR_ALWAYS_TRANSLATE_UNDO = 19; - private static final int INFOBAR_CLOSE_DEPRECATED = 20; - private static final int INFOBAR_SNACKBAR_AUTO_ALWAYS_IMPRESSION = 21; - private static final int INFOBAR_SNACKBAR_AUTO_NEVER_IMPRESSION = 22; - private static final int INFOBAR_SNACKBAR_CANCEL_AUTO_ALWAYS = 23; - private static final int INFOBAR_SNACKBAR_CANCEL_AUTO_NEVER = 24; - private static final int INFOBAR_HISTOGRAM_BOUNDARY = 25; - // Need 2 instances of TranslateMenuHelper to prevent a race condition bug which happens when // showing language menu after dismissing overflow menu. private TranslateMenuHelper mOverflowMenuHelper; @@ -124,7 +93,7 @@ public class TranslateCompactInfoBar extends InfoBar private static InfoBar create(TabImpl tab, int initialStep, String sourceLanguageCode, String targetLanguageCode, boolean alwaysTranslate, boolean triggeredFromMenu, String[] languages, String[] languageCodes, int[] hashCodes, int tabTextColor) { - recordInfobarAction(INFOBAR_IMPRESSION); + recordInfobarAction(InfobarEvent.INFOBAR_IMPRESSION); return new TranslateCompactInfoBar(initialStep, sourceLanguageCode, targetLanguageCode, alwaysTranslate, triggeredFromMenu, languages, languageCodes, hashCodes, tabTextColor); @@ -217,7 +186,7 @@ public class TranslateCompactInfoBar extends InfoBar @Override public void onClick(View v) { mTabLayout.endScrollingAnimationIfPlaying(); - recordInfobarAction(INFOBAR_OPTIONS); + recordInfobarAction(InfobarEvent.INFOBAR_OPTIONS); initMenuHelper(TranslateMenu.MENU_OVERFLOW); mOverflowMenuHelper.show(TranslateMenu.MENU_OVERFLOW, getParentWidth()); mMenuExpanded = true; @@ -298,7 +267,7 @@ public class TranslateCompactInfoBar extends InfoBar if (isDismissed()) return; if (!mUserInteracted) { - recordInfobarAction(INFOBAR_DECLINE); + recordInfobarAction(InfobarEvent.INFOBAR_DECLINE); } // NOTE: In Chrome there is a check for whether auto "never translate" should be triggered @@ -322,11 +291,11 @@ public class TranslateCompactInfoBar extends InfoBar switch (tab.getPosition()) { case SOURCE_TAB_INDEX: incrementAndRecordTranslationsPerPageCount(); - recordInfobarAction(INFOBAR_REVERT); + recordInfobarAction(InfobarEvent.INFOBAR_REVERT); onButtonClicked(ActionType.TRANSLATE_SHOW_ORIGINAL); return; case TARGET_TAB_INDEX: - recordInfobarAction(INFOBAR_TARGET_TAB_TRANSLATE); + recordInfobarAction(InfobarEvent.INFOBAR_TARGET_TAB_TRANSLATE); recordInfobarLanguageData( INFOBAR_HISTOGRAM_TRANSLATE_LANGUAGE, mOptions.targetLanguageCode()); startTranslating(TARGET_TAB_INDEX); @@ -346,34 +315,34 @@ public class TranslateCompactInfoBar extends InfoBar public void onOverflowMenuItemClicked(int itemId) { switch (itemId) { case TranslateMenu.ID_OVERFLOW_MORE_LANGUAGE: - recordInfobarAction(INFOBAR_MORE_LANGUAGES); + recordInfobarAction(InfobarEvent.INFOBAR_MORE_LANGUAGES); initMenuHelper(TranslateMenu.MENU_TARGET_LANGUAGE); mLanguageMenuHelper.show(TranslateMenu.MENU_TARGET_LANGUAGE, getParentWidth()); return; case TranslateMenu.ID_OVERFLOW_ALWAYS_TRANSLATE: // Only show snackbar when "Always Translate" is enabled. if (!mOptions.getTranslateState(TranslateOptions.Type.ALWAYS_LANGUAGE)) { - recordInfobarAction(INFOBAR_ALWAYS_TRANSLATE); + recordInfobarAction(InfobarEvent.INFOBAR_ALWAYS_TRANSLATE); recordInfobarLanguageData(INFOBAR_HISTOGRAM_ALWAYS_TRANSLATE_LANGUAGE, mOptions.sourceLanguageCode()); createAndShowSnackbar(ACTION_OVERFLOW_ALWAYS_TRANSLATE); } else { - recordInfobarAction(INFOBAR_ALWAYS_TRANSLATE_UNDO); + recordInfobarAction(InfobarEvent.INFOBAR_ALWAYS_TRANSLATE_UNDO); handleTranslateOptionPostSnackbar(ACTION_OVERFLOW_ALWAYS_TRANSLATE); } return; case TranslateMenu.ID_OVERFLOW_NEVER_LANGUAGE: - recordInfobarAction(INFOBAR_NEVER_TRANSLATE); + recordInfobarAction(InfobarEvent.INFOBAR_NEVER_TRANSLATE); recordInfobarLanguageData( INFOBAR_HISTOGRAM_NEVER_TRANSLATE_LANGUAGE, mOptions.sourceLanguageCode()); createAndShowSnackbar(ACTION_OVERFLOW_NEVER_LANGUAGE); return; case TranslateMenu.ID_OVERFLOW_NEVER_SITE: - recordInfobarAction(INFOBAR_NEVER_TRANSLATE_SITE); + recordInfobarAction(InfobarEvent.INFOBAR_NEVER_TRANSLATE_SITE); createAndShowSnackbar(ACTION_OVERFLOW_NEVER_SITE); return; case TranslateMenu.ID_OVERFLOW_NOT_THIS_LANGUAGE: - recordInfobarAction(INFOBAR_PAGE_NOT_IN); + recordInfobarAction(InfobarEvent.INFOBAR_PAGE_NOT_IN); initMenuHelper(TranslateMenu.MENU_SOURCE_LANGUAGE); mLanguageMenuHelper.show(TranslateMenu.MENU_SOURCE_LANGUAGE, getParentWidth()); return; @@ -386,7 +355,7 @@ public class TranslateCompactInfoBar extends InfoBar public void onTargetMenuItemClicked(String code) { // Reset target code in both UI and native. if (mNativeTranslateInfoBarPtr != 0 && mOptions.setTargetLanguage(code)) { - recordInfobarAction(INFOBAR_MORE_LANGUAGES_TRANSLATE); + recordInfobarAction(InfobarEvent.INFOBAR_MORE_LANGUAGES_TRANSLATE); recordInfobarLanguageData( INFOBAR_HISTOGRAM_MORE_LANGUAGES_LANGUAGE, mOptions.targetLanguageCode()); TranslateCompactInfoBarJni.get().applyStringTranslateOption(mNativeTranslateInfoBarPtr, @@ -536,7 +505,7 @@ public class TranslateCompactInfoBar extends InfoBar private static void recordInfobarAction(int action) { RecordHistogram.recordEnumeratedHistogram( - INFOBAR_HISTOGRAM, action, INFOBAR_HISTOGRAM_BOUNDARY); + INFOBAR_HISTOGRAM, action, InfobarEvent.INFOBAR_HISTOGRAM_BOUNDARY); } private void recordInfobarLanguageData(String histogram, String langCode) { diff --git a/chromium/weblayer/browser/java/org/chromium/weblayer_private/UrlBarControllerImpl.java b/chromium/weblayer/browser/java/org/chromium/weblayer_private/UrlBarControllerImpl.java index 41e100e1b47..3c500e2a3f2 100644 --- a/chromium/weblayer/browser/java/org/chromium/weblayer_private/UrlBarControllerImpl.java +++ b/chromium/weblayer/browser/java/org/chromium/weblayer_private/UrlBarControllerImpl.java @@ -8,6 +8,7 @@ import android.content.Context; import android.content.res.ColorStateList; import android.graphics.Color; import android.os.Bundle; +import android.text.TextUtils; import android.util.TypedValue; import android.view.Gravity; import android.view.View; @@ -29,10 +30,12 @@ import org.chromium.base.annotations.JNINamespace; import org.chromium.base.annotations.NativeMethods; import org.chromium.components.content_settings.ContentSettingsType; import org.chromium.components.embedder_support.util.Origin; +import org.chromium.components.embedder_support.util.UrlUtilities; import org.chromium.components.omnibox.SecurityButtonAnimationDelegate; import org.chromium.components.omnibox.SecurityStatusIcon; import org.chromium.components.page_info.PageInfoController; import org.chromium.components.page_info.PermissionParamsListBuilderDelegate; +import org.chromium.components.security_state.ConnectionSecurityLevel; import org.chromium.content_public.browser.WebContents; import org.chromium.weblayer_private.interfaces.IObjectWrapper; import org.chromium.weblayer_private.interfaces.IUrlBarController; @@ -114,6 +117,7 @@ public class UrlBarControllerImpl extends IUrlBarController.Stub { private final UrlBarControllerImpl mController; private float mTextSize; private boolean mShowPageInfoWhenUrlTextClicked; + private boolean mShowPublisherUrl; // These refer to the resources in the embedder's APK, not WebLayer's. private @ColorRes int mUrlTextColor; @@ -136,6 +140,8 @@ public class UrlBarControllerImpl extends IUrlBarController.Stub { mTextSize = options.getFloat(UrlBarOptionsKeys.URL_TEXT_SIZE, DEFAULT_TEXT_SIZE); mShowPageInfoWhenUrlTextClicked = options.getBoolean( UrlBarOptionsKeys.SHOW_PAGE_INFO_WHEN_URL_TEXT_CLICKED, /*default= */ false); + mShowPublisherUrl = + options.getBoolean(UrlBarOptionsKeys.SHOW_PUBLISHER_URL, /*default= */ false); mUrlTextColor = options.getInt(UrlBarOptionsKeys.URL_TEXT_COLOR, /*default= */ 0); mUrlIconColor = options.getInt(UrlBarOptionsKeys.URL_ICON_COLOR, /*default= */ 0); @@ -179,8 +185,24 @@ public class UrlBarControllerImpl extends IUrlBarController.Stub { private void updateView() { if (mBrowserImpl == null) return; - String displayUrl = - UrlBarControllerImplJni.get().getUrlForDisplay(mNativeUrlBarController); + int securityLevel = UrlBarControllerImplJni.get().getConnectionSecurityLevel( + mNativeUrlBarController); + + String publisherUrl = + UrlBarControllerImplJni.get().getPublisherUrl(mNativeUrlBarController); + + String displayUrl; + int securityIcon; + if (mShowPublisherUrl && securityLevel != ConnectionSecurityLevel.DANGEROUS + && !TextUtils.isEmpty(publisherUrl)) { + displayUrl = getContext().getResources().getString(R.string.amp_publisher_url, + UrlUtilities.extractPublisherFromPublisherUrl(publisherUrl)); + securityIcon = R.drawable.amp_icon; + } else { + displayUrl = getUrlForDisplay(); + securityIcon = getSecurityIcon(); + } + mUrlTextView.setText(displayUrl); mUrlTextView.setTextSize( TypedValue.COMPLEX_UNIT_SP, Math.max(MINIMUM_TEXT_SIZE, mTextSize)); @@ -189,11 +211,9 @@ public class UrlBarControllerImpl extends IUrlBarController.Stub { mUrlTextView.setTextColor(ContextCompat.getColor(embedderContext, mUrlTextColor)); } - mSecurityButtonAnimationDelegate.updateSecurityButton(getSecurityIcon()); + mSecurityButtonAnimationDelegate.updateSecurityButton(securityIcon); mSecurityButton.setContentDescription(getContext().getResources().getString( - SecurityStatusIcon.getSecurityIconContentDescriptionResourceId( - UrlBarControllerImplJni.get().getConnectionSecurityLevel( - mNativeUrlBarController)))); + SecurityStatusIcon.getSecurityIconContentDescriptionResourceId(securityLevel))); if (mUrlIconColor != 0 && embedderContext != null) { ImageViewCompat.setImageTintList(mSecurityButton, @@ -225,9 +245,19 @@ public class UrlBarControllerImpl extends IUrlBarController.Stub { private void showPageInfoUi(View v) { WebContents webContents = mBrowserImpl.getActiveTab().getWebContents(); + + String publisherUrl = null; + if (mShowPublisherUrl) { + String publisherUrlMaybeNull = + UrlBarControllerImplJni.get().getPublisherUrl(mNativeUrlBarController); + if (publisherUrlMaybeNull != null && !TextUtils.isEmpty(publisherUrlMaybeNull)) { + publisherUrl = + UrlUtilities.extractPublisherFromPublisherUrl(publisherUrlMaybeNull); + } + } + PageInfoController.show(mBrowserImpl.getWindowAndroid().getActivity().get(), - webContents, - /* contentPublisher= */ null, PageInfoController.OpenedFromSource.TOOLBAR, + webContents, publisherUrl, PageInfoController.OpenedFromSource.TOOLBAR, PageInfoControllerDelegateImpl.create(webContents), new PermissionParamsListBuilderDelegate(mBrowserImpl.getProfile()) { @Override @@ -260,6 +290,7 @@ public class UrlBarControllerImpl extends IUrlBarController.Stub { long createUrlBarController(long browserPtr); void deleteUrlBarController(long urlBarControllerImplPtr); String getUrlForDisplay(long nativeUrlBarControllerImpl); + String getPublisherUrl(long nativeUrlBarControllerImpl); int getConnectionSecurityLevel(long nativeUrlBarControllerImpl); boolean shouldShowDangerTriangleForWarningLevel(long nativeUrlBarControllerImpl); } diff --git a/chromium/weblayer/browser/java/org/chromium/weblayer_private/WebLayerImpl.java b/chromium/weblayer/browser/java/org/chromium/weblayer_private/WebLayerImpl.java index 29f8b0a1626..b15c1a39df8 100644 --- a/chromium/weblayer/browser/java/org/chromium/weblayer_private/WebLayerImpl.java +++ b/chromium/weblayer/browser/java/org/chromium/weblayer_private/WebLayerImpl.java @@ -42,9 +42,9 @@ import org.chromium.base.TraceEvent; import org.chromium.base.annotations.CalledByNative; import org.chromium.base.annotations.JNINamespace; import org.chromium.base.annotations.NativeMethods; -import org.chromium.base.compat.ApiHelperForO; import org.chromium.base.library_loader.LibraryLoader; import org.chromium.base.library_loader.LibraryProcessType; +import org.chromium.base.metrics.RecordHistogram; import org.chromium.components.browser_ui.contacts_picker.ContactsPickerDialog; import org.chromium.components.browser_ui.photo_picker.DecoderServiceHost; import org.chromium.components.browser_ui.photo_picker.ImageDecoder; @@ -78,6 +78,7 @@ import org.chromium.weblayer_private.interfaces.IWebLayerClient; import org.chromium.weblayer_private.interfaces.ObjectWrapper; import org.chromium.weblayer_private.interfaces.StrictModeWorkaround; import org.chromium.weblayer_private.media.MediaRouteDialogFragmentImpl; +import org.chromium.weblayer_private.media.MediaRouterClientImpl; import org.chromium.weblayer_private.media.MediaSessionManager; import org.chromium.weblayer_private.media.MediaStreamManager; import org.chromium.weblayer_private.metrics.MetricsServiceClient; @@ -117,7 +118,7 @@ public final class WebLayerImpl extends IWebLayer.Stub { // The required package ID for WebLayer when loaded as a shared library, hardcoded in the // resources. If this value changes make sure to change _SHARED_LIBRARY_HARDCODED_ID in // //build/android/gyp/util/protoresources.py. - private static final int REQUIRED_PACKAGE_IDENTIFIER = 12; + private static final int REQUIRED_PACKAGE_IDENTIFIER = 36; private final ProfileManager mProfileManager = new ProfileManager(); @@ -217,7 +218,6 @@ public final class WebLayerImpl extends IWebLayer.Stub { notifyWebViewRunningInProcess(remoteContext.getClassLoader()); } - remoteContext = processRemoteContext(remoteContext); Context appContext = minimalInitForContext( ObjectWrapper.unwrap(appContextWrapper, Context.class), remoteContext); PackageInfo packageInfo = WebViewFactory.getLoadedPackageInfo(); @@ -379,7 +379,7 @@ public final class WebLayerImpl extends IWebLayer.Stub { StrictModeWorkaround.apply(); // This is a no-op if init has already happened. minimalInitForContext(ObjectWrapper.unwrap(appContext, Context.class), - processRemoteContext(ObjectWrapper.unwrap(remoteContext, Context.class))); + ObjectWrapper.unwrap(remoteContext, Context.class)); return CrashReporterControllerImpl.getInstance(); } @@ -411,13 +411,26 @@ public final class WebLayerImpl extends IWebLayer.Stub { } @Override + public void onRemoteMediaServiceStarted(IObjectWrapper sessionService, Intent intent) { + StrictModeWorkaround.apply(); + MediaRouterClientImpl.serviceStarted( + ObjectWrapper.unwrap(sessionService, Service.class), intent); + } + + @Override + public void onRemoteMediaServiceDestroyed(int id) { + StrictModeWorkaround.apply(); + MediaRouterClientImpl.serviceDestroyed(id); + } + + @Override public IBinder initializeImageDecoder(IObjectWrapper appContext, IObjectWrapper remoteContext) { StrictModeWorkaround.apply(); assert ContextUtils.getApplicationContext() == null; CommandLine.init(null); minimalInitForContext(ObjectWrapper.unwrap(appContext, Context.class), - processRemoteContext(ObjectWrapper.unwrap(remoteContext, Context.class))); + ObjectWrapper.unwrap(remoteContext, Context.class)); LibraryLoader.getInstance().setLibraryProcessType( LibraryProcessType.PROCESS_WEBLAYER_CHILD); LibraryLoader.getInstance().ensureInitialized(); @@ -439,6 +452,19 @@ public final class WebLayerImpl extends IWebLayer.Stub { public void setClient(IWebLayerClient client) { StrictModeWorkaround.apply(); sClient = client; + + if (WebLayerFactoryImpl.getClientMajorVersion() >= 88) { + try { + RecordHistogram.recordTimesHistogram("WebLayer.Startup.ClassLoaderCreationTime", + sClient.getClassLoaderCreationTime()); + RecordHistogram.recordTimesHistogram( + "WebLayer.Startup.ContextCreationTime", sClient.getContextCreationTime()); + RecordHistogram.recordTimesHistogram("WebLayer.Startup.WebLayerLoaderCreationTime", + sClient.getWebLayerLoaderCreationTime()); + } catch (RemoteException e) { + throw new APICallException(e); + } + } } @Override @@ -506,6 +532,42 @@ public final class WebLayerImpl extends IWebLayer.Stub { } } + public static Intent createRemoteMediaServiceIntent() { + if (sClient == null) { + throw new IllegalStateException("WebLayer should have been initialized already."); + } + + try { + return sClient.createRemoteMediaServiceIntent(); + } catch (RemoteException e) { + throw new APICallException(e); + } + } + + public static int getPresentationApiNotificationId() { + if (sClient == null) { + throw new IllegalStateException("WebLayer should have been initialized already."); + } + + try { + return sClient.getPresentationApiNotificationId(); + } catch (RemoteException e) { + throw new APICallException(e); + } + } + + public static int getRemotePlaybackApiNotificationId() { + if (sClient == null) { + throw new IllegalStateException("WebLayer should have been initialized already."); + } + + try { + return sClient.getRemotePlaybackApiNotificationId(); + } catch (RemoteException e) { + throw new APICallException(e); + } + } + public static String getClientApplicationName() { Context context = ContextUtils.getApplicationContext(); return new StringBuilder() @@ -606,6 +668,10 @@ public final class WebLayerImpl extends IWebLayer.Stub { /** Forces adding entries to the package identifiers array until we hit the required ID. */ private static void forceAddAssetPaths(Context remoteContext, int packageId) { + if (packageId > REQUIRED_PACKAGE_IDENTIFIER) { + throw new AndroidRuntimeException( + "WebLayer package ID too large, aborting: " + packageId); + } try { Method addAssetPath = AssetManager.class.getMethod("addAssetPath", String.class); String path = remoteContext.getApplicationInfo().sourceDir; @@ -823,18 +889,6 @@ public final class WebLayerImpl extends IWebLayer.Stub { } } - private static Context processRemoteContext(Context remoteContext) { - // If WebLayer is in a DFM, make sure the correct resources are used. - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { - try { - return ApiHelperForO.createContextForSplit(remoteContext, "weblayer"); - } catch (PackageManager.NameNotFoundException e) { - // WebLayer is not in a split, the original context will have the resources. - } - } - return remoteContext; - } - private static Context createContextForMode(Context remoteContext, int uiMode) { Configuration configuration = new Configuration(); configuration.uiMode = uiMode; diff --git a/chromium/weblayer/browser/java/org/chromium/weblayer_private/WebMessageReplyProxyImpl.java b/chromium/weblayer/browser/java/org/chromium/weblayer_private/WebMessageReplyProxyImpl.java index d639a42cf3a..980275d0eb3 100644 --- a/chromium/weblayer/browser/java/org/chromium/weblayer_private/WebMessageReplyProxyImpl.java +++ b/chromium/weblayer/browser/java/org/chromium/weblayer_private/WebMessageReplyProxyImpl.java @@ -44,22 +44,14 @@ public final class WebMessageReplyProxyImpl extends IWebMessageReplyProxy.Stub { } @CalledByNative - private void onNativeDestroyed() { + private void onNativeDestroyed() throws RemoteException { mNativeWebMessageReplyProxyImpl = 0; - try { - mClient.onReplyProxyDestroyed(mId); - } catch (RemoteException e) { - throw new APICallException(e); - } + mClient.onReplyProxyDestroyed(mId); } @CalledByNative - private void onPostMessage(String message) { - try { - mClient.onPostMessage(mId, message); - } catch (RemoteException e) { - throw new APICallException(e); - } + private void onPostMessage(String message) throws RemoteException { + mClient.onPostMessage(mId, message); } @Override diff --git a/chromium/weblayer/browser/java/org/chromium/weblayer_private/interfaces/ActionModeItemType.java b/chromium/weblayer/browser/java/org/chromium/weblayer_private/interfaces/ActionModeItemType.java new file mode 100644 index 00000000000..bbce175453d --- /dev/null +++ b/chromium/weblayer/browser/java/org/chromium/weblayer_private/interfaces/ActionModeItemType.java @@ -0,0 +1,17 @@ +// 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_private.interfaces; + +import androidx.annotation.IntDef; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +@IntDef({ActionModeItemType.SHARE, ActionModeItemType.WEB_SEARCH}) +@Retention(RetentionPolicy.SOURCE) +public @interface ActionModeItemType { + int SHARE = 1 << 0; + int WEB_SEARCH = 1 << 1; +} diff --git a/chromium/weblayer/browser/java/org/chromium/weblayer_private/interfaces/IBrowser.aidl b/chromium/weblayer/browser/java/org/chromium/weblayer_private/interfaces/IBrowser.aidl index 485e4b45769..950df71a49e 100644 --- a/chromium/weblayer/browser/java/org/chromium/weblayer_private/interfaces/IBrowser.aidl +++ b/chromium/weblayer/browser/java/org/chromium/weblayer_private/interfaces/IBrowser.aidl @@ -43,4 +43,7 @@ interface IBrowser { // Added in 87. boolean isRestoringPreviousState() = 14; + + // Added in 88. + void setBrowserControlsOffsetsEnabled(in boolean enable) = 13; } diff --git a/chromium/weblayer/browser/java/org/chromium/weblayer_private/interfaces/IBrowserClient.aidl b/chromium/weblayer/browser/java/org/chromium/weblayer_private/interfaces/IBrowserClient.aidl index 6c7eff74173..e7bbb456a83 100644 --- a/chromium/weblayer/browser/java/org/chromium/weblayer_private/interfaces/IBrowserClient.aidl +++ b/chromium/weblayer/browser/java/org/chromium/weblayer_private/interfaces/IBrowserClient.aidl @@ -15,4 +15,8 @@ interface IBrowserClient { // Added in 87. IRemoteFragment createMediaRouteDialogFragment() = 3; void onRestoreCompleted() = 5; + + // Added in 88. + void onBrowserControlsOffsetsChanged(in boolean isTop, + in int controlsOffset) = 4; } diff --git a/chromium/weblayer/browser/java/org/chromium/weblayer_private/interfaces/IContextMenuParams.aidl b/chromium/weblayer/browser/java/org/chromium/weblayer_private/interfaces/IContextMenuParams.aidl new file mode 100644 index 00000000000..58baad20468 --- /dev/null +++ b/chromium/weblayer/browser/java/org/chromium/weblayer_private/interfaces/IContextMenuParams.aidl @@ -0,0 +1,11 @@ +// 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_private.interfaces; + +/** + * Holds on to the native ContextMenuParams object. + */ +interface IContextMenuParams { +} diff --git a/chromium/weblayer/browser/java/org/chromium/weblayer_private/interfaces/INavigation.aidl b/chromium/weblayer/browser/java/org/chromium/weblayer_private/interfaces/INavigation.aidl index 5c94e3418ea..bc05deb6794 100644 --- a/chromium/weblayer/browser/java/org/chromium/weblayer_private/interfaces/INavigation.aidl +++ b/chromium/weblayer/browser/java/org/chromium/weblayer_private/interfaces/INavigation.aidl @@ -33,4 +33,7 @@ interface INavigation { // @since 86 boolean isPageInitiated() = 11; boolean isReload() = 12; + + // @since 88 + void disableNetworkErrorAutoReload() = 17; } diff --git a/chromium/weblayer/browser/java/org/chromium/weblayer_private/interfaces/INavigationControllerClient.aidl b/chromium/weblayer/browser/java/org/chromium/weblayer_private/interfaces/INavigationControllerClient.aidl index 73432f8bd19..0c3fa33d8db 100644 --- a/chromium/weblayer/browser/java/org/chromium/weblayer_private/interfaces/INavigationControllerClient.aidl +++ b/chromium/weblayer/browser/java/org/chromium/weblayer_private/interfaces/INavigationControllerClient.aidl @@ -33,4 +33,8 @@ interface INavigationControllerClient { // Added in M85. void onOldPageNoLongerRendered(in String uri) = 9; + + // Added in M88. + void onFirstContentfulPaint2(long navigationStartMs, long firstContentfulPaintDurationMs) = 10; + void onLargestContentfulPaint(long navigationStartMs, long largestContentfulPaintDurationMs) = 11; } diff --git a/chromium/weblayer/browser/java/org/chromium/weblayer_private/interfaces/IProfileClient.aidl b/chromium/weblayer/browser/java/org/chromium/weblayer_private/interfaces/IProfileClient.aidl index 50de21109a1..16cb26715b4 100644 --- a/chromium/weblayer/browser/java/org/chromium/weblayer_private/interfaces/IProfileClient.aidl +++ b/chromium/weblayer/browser/java/org/chromium/weblayer_private/interfaces/IProfileClient.aidl @@ -4,7 +4,7 @@ package org.chromium.weblayer_private.interfaces; -// Added in Version 87. +// Added in Version 88. interface IProfileClient { void onProfileDestroyed() = 0; } diff --git a/chromium/weblayer/browser/java/org/chromium/weblayer_private/interfaces/ITab.aidl b/chromium/weblayer/browser/java/org/chromium/weblayer_private/interfaces/ITab.aidl index a2985dbc8d4..03f1d69064e 100644 --- a/chromium/weblayer/browser/java/org/chromium/weblayer_private/interfaces/ITab.aidl +++ b/chromium/weblayer/browser/java/org/chromium/weblayer_private/interfaces/ITab.aidl @@ -6,6 +6,7 @@ package org.chromium.weblayer_private.interfaces; import java.util.List; +import org.chromium.weblayer_private.interfaces.IContextMenuParams; import org.chromium.weblayer_private.interfaces.IDownloadCallbackClient; import org.chromium.weblayer_private.interfaces.IErrorPageCallbackClient; import org.chromium.weblayer_private.interfaces.IFaviconFetcher; @@ -75,5 +76,11 @@ interface ITab { // Added in 87 void setScrollOffsetsEnabled(in boolean enabled) = 26; + + // Added in 88 + void setFloatingActionModeOverride(in int actionModeItemTypes) = 27; boolean willAutomaticallyReloadAfterCrash() = 28; + void setDesktopUserAgentEnabled(in boolean enable) = 29; + boolean isDesktopUserAgentEnabled() = 30; + void download(in IContextMenuParams contextMenuParams) = 31; } diff --git a/chromium/weblayer/browser/java/org/chromium/weblayer_private/interfaces/ITabClient.aidl b/chromium/weblayer/browser/java/org/chromium/weblayer_private/interfaces/ITabClient.aidl index 94e02e8c47b..1b8028c83c4 100644 --- a/chromium/weblayer/browser/java/org/chromium/weblayer_private/interfaces/ITabClient.aidl +++ b/chromium/weblayer/browser/java/org/chromium/weblayer_private/interfaces/ITabClient.aidl @@ -4,6 +4,7 @@ package org.chromium.weblayer_private.interfaces; +import org.chromium.weblayer_private.interfaces.IContextMenuParams; import org.chromium.weblayer_private.interfaces.IObjectWrapper; /** @@ -21,6 +22,7 @@ interface ITabClient { // void onCloseTab() = 3; // Added in M82. + // Deprecated in M88. void showContextMenu(in IObjectWrapper pageUrl, in IObjectWrapper linkUrl, in IObjectWrapper linkText, in IObjectWrapper titleOrAltText, in IObjectWrapper srcUrl) = 4; @@ -46,4 +48,12 @@ interface ITabClient { // Added in M87 void onVerticalScrollOffsetChanged(in int offset) = 11; + + // Added in M88 + void onActionItemClicked( + in int actionModeItemType, in IObjectWrapper selectedString) = 12; + void showContextMenu2(in IObjectWrapper pageUrl, in IObjectWrapper linkUrl, + in IObjectWrapper linkText, in IObjectWrapper titleOrAltText, + in IObjectWrapper srcUrl, in boolean isImage, in boolean isVideo, in boolean canDownload, + in IContextMenuParams contextMenuParams) = 13; } diff --git a/chromium/weblayer/browser/java/org/chromium/weblayer_private/interfaces/IWebLayer.aidl b/chromium/weblayer/browser/java/org/chromium/weblayer_private/interfaces/IWebLayer.aidl index ae9df666cf0..d1bbc1dc6ba 100644 --- a/chromium/weblayer/browser/java/org/chromium/weblayer_private/interfaces/IWebLayer.aidl +++ b/chromium/weblayer/browser/java/org/chromium/weblayer_private/interfaces/IWebLayer.aidl @@ -105,4 +105,11 @@ interface IWebLayer { IMediaRouteDialogFragment createMediaRouteDialogFragmentImpl( in IRemoteFragmentClient remoteFragmentClient) = 21; IProfile getIncognitoProfile(in String profileName) = 24; + + // Added in Version 88. + void onRemoteMediaServiceStarted(in IObjectWrapper sessionService, in Intent intent) = 22; + void onRemoteMediaServiceDestroyed(int id) = 23; + + // WARNING: when choosing next value make sure you look back for the max, as + // merges may mean the last function does not have the max value. } diff --git a/chromium/weblayer/browser/java/org/chromium/weblayer_private/interfaces/IWebLayerClient.aidl b/chromium/weblayer/browser/java/org/chromium/weblayer_private/interfaces/IWebLayerClient.aidl index a26f56deb45..933e8507b57 100644 --- a/chromium/weblayer/browser/java/org/chromium/weblayer_private/interfaces/IWebLayerClient.aidl +++ b/chromium/weblayer/browser/java/org/chromium/weblayer_private/interfaces/IWebLayerClient.aidl @@ -13,4 +13,12 @@ interface IWebLayerClient { // Since Version 86. Intent createImageDecoderServiceIntent() = 3; + + // Since Version 88. + long getClassLoaderCreationTime() = 4; + long getContextCreationTime() = 5; + long getWebLayerLoaderCreationTime() = 6; + Intent createRemoteMediaServiceIntent() = 7; + int getPresentationApiNotificationId() = 8; + int getRemotePlaybackApiNotificationId() = 9; } diff --git a/chromium/weblayer/browser/java/org/chromium/weblayer_private/interfaces/LoadError.java b/chromium/weblayer/browser/java/org/chromium/weblayer_private/interfaces/LoadError.java index eba5fbf0c51..9a37dfa0061 100644 --- a/chromium/weblayer/browser/java/org/chromium/weblayer_private/interfaces/LoadError.java +++ b/chromium/weblayer/browser/java/org/chromium/weblayer_private/interfaces/LoadError.java @@ -10,7 +10,8 @@ import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @IntDef({LoadError.NO_ERROR, LoadError.HTTP_CLIENT_ERROR, LoadError.HTTP_SERVER_ERROR, - LoadError.SSL_ERROR, LoadError.CONNECTIVITY_ERROR, LoadError.OTHER_ERROR}) + LoadError.SSL_ERROR, LoadError.CONNECTIVITY_ERROR, LoadError.OTHER_ERROR, + LoadError.SAFE_BROWSING_ERROR}) @Retention(RetentionPolicy.SOURCE) public @interface LoadError { int NO_ERROR = 0; @@ -19,4 +20,6 @@ public @interface LoadError { int SSL_ERROR = 3; int CONNECTIVITY_ERROR = 4; int OTHER_ERROR = 5; + // Sent since 88. + int SAFE_BROWSING_ERROR = 6; } diff --git a/chromium/weblayer/browser/java/org/chromium/weblayer_private/interfaces/RemoteMediaServiceConstants.java b/chromium/weblayer/browser/java/org/chromium/weblayer_private/interfaces/RemoteMediaServiceConstants.java new file mode 100644 index 00000000000..375e3d8a28f --- /dev/null +++ b/chromium/weblayer/browser/java/org/chromium/weblayer_private/interfaces/RemoteMediaServiceConstants.java @@ -0,0 +1,16 @@ +// 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_private.interfaces; + +/** Keys for remote media service intent extras. */ +public interface RemoteMediaServiceConstants { + // Used as a key in the client's AndroidManifest.xml to enable remote media playback, i.e. + // Presentation API, Remote Playback API, and Media Fling (automatic casting of html5 videos). + String FEATURE_ENABLED_KEY = "org.chromium.weblayer.ENABLE_REMOTE_MEDIA"; + + // Used internally by WebLayer as a key to the various values of remote media service + // notification IDs. + String NOTIFICATION_ID_KEY = "REMOTE_MEDIA_SERVICE_NOTIFICATION_ID_KEY"; +} diff --git a/chromium/weblayer/browser/java/org/chromium/weblayer_private/interfaces/UrlBarOptionsKeys.java b/chromium/weblayer/browser/java/org/chromium/weblayer_private/interfaces/UrlBarOptionsKeys.java index 40957da4af7..3045944cba1 100644 --- a/chromium/weblayer/browser/java/org/chromium/weblayer_private/interfaces/UrlBarOptionsKeys.java +++ b/chromium/weblayer/browser/java/org/chromium/weblayer_private/interfaces/UrlBarOptionsKeys.java @@ -10,6 +10,11 @@ public interface UrlBarOptionsKeys { * If true, clicking on the url shows page info. Default is false. */ String SHOW_PAGE_INFO_WHEN_URL_TEXT_CLICKED = "ShowPageInfoWhenUrlTextClicked"; + /** + * If true, shows publisher url. Default is false. + * @since 88 + */ + String SHOW_PUBLISHER_URL = "ShowPublisherUrl"; String URL_ICON_COLOR = "UrlIconColor"; String URL_TEXT_COLOR = "UrlTextColor"; String URL_TEXT_SIZE = "UrlTextSize"; diff --git a/chromium/weblayer/browser/java/org/chromium/weblayer_private/interfaces/WebLayerVersionConstants.java b/chromium/weblayer/browser/java/org/chromium/weblayer_private/interfaces/WebLayerVersionConstants.java index 3ef535d997e..a76b13c3ba8 100644 --- a/chromium/weblayer/browser/java/org/chromium/weblayer_private/interfaces/WebLayerVersionConstants.java +++ b/chromium/weblayer/browser/java/org/chromium/weblayer_private/interfaces/WebLayerVersionConstants.java @@ -15,5 +15,5 @@ public interface WebLayerVersionConstants { * * @see WebLayer#isAvailable() */ - int MAX_SKEW = 3; + int MAX_SKEW = 4; } diff --git a/chromium/weblayer/browser/java/org/chromium/weblayer_private/media/MediaRouteDialogFragmentImpl.java b/chromium/weblayer/browser/java/org/chromium/weblayer_private/media/MediaRouteDialogFragmentImpl.java index aede72b184c..c3c4a2c34f9 100644 --- a/chromium/weblayer/browser/java/org/chromium/weblayer_private/media/MediaRouteDialogFragmentImpl.java +++ b/chromium/weblayer/browser/java/org/chromium/weblayer_private/media/MediaRouteDialogFragmentImpl.java @@ -33,6 +33,7 @@ import org.chromium.weblayer_private.interfaces.IRemoteFragment; import org.chromium.weblayer_private.interfaces.IRemoteFragmentClient; import org.chromium.weblayer_private.interfaces.StrictModeWorkaround; +import java.lang.ref.WeakReference; import java.lang.reflect.Constructor; /** @@ -53,6 +54,10 @@ public class MediaRouteDialogFragmentImpl extends RemoteFragmentImpl { private boolean mStarted; private FragmentController mFragmentController; + // The instance for the currently active dialog, if any. This is a WeakReference to get around + // StaticFieldLeak warnings. + private static WeakReference<MediaRouteDialogFragmentImpl> sInstanceForTest; + /** * A fake FragmentActivity needed to make the Fragment system happy. * @@ -195,6 +200,7 @@ public class MediaRouteDialogFragmentImpl extends RemoteFragmentImpl { public MediaRouteDialogFragmentImpl(IRemoteFragmentClient remoteFragmentClient) { super(remoteFragmentClient); + sInstanceForTest = new WeakReference<MediaRouteDialogFragmentImpl>(this); } @Override @@ -239,6 +245,7 @@ public class MediaRouteDialogFragmentImpl extends RemoteFragmentImpl { StrictModeWorkaround.apply(); super.onDestroy(); mFragmentController.dispatchDestroy(); + sInstanceForTest = null; } @Override @@ -300,4 +307,8 @@ public class MediaRouteDialogFragmentImpl extends RemoteFragmentImpl { private Context getWebLayerContext() { return mContext; } + + public static MediaRouteDialogFragmentImpl getInstanceForTest() { + return sInstanceForTest.get(); + } } diff --git a/chromium/weblayer/browser/java/org/chromium/weblayer_private/media/MediaRouterClientImpl.java b/chromium/weblayer/browser/java/org/chromium/weblayer_private/media/MediaRouterClientImpl.java index 86a596bda08..588aa8d4de6 100644 --- a/chromium/weblayer/browser/java/org/chromium/weblayer_private/media/MediaRouterClientImpl.java +++ b/chromium/weblayer/browser/java/org/chromium/weblayer_private/media/MediaRouterClientImpl.java @@ -4,23 +4,51 @@ package org.chromium.weblayer_private.media; +import android.app.Service; +import android.content.Context; import android.content.Intent; +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageManager; +import android.content.pm.PackageManager.NameNotFoundException; +import android.support.v4.media.session.MediaSessionCompat; import androidx.fragment.app.FragmentManager; +import org.chromium.base.ContextUtils; import org.chromium.base.annotations.CalledByNative; import org.chromium.base.annotations.JNINamespace; +import org.chromium.components.browser_ui.media.MediaNotificationController; import org.chromium.components.browser_ui.media.MediaNotificationInfo; +import org.chromium.components.browser_ui.media.MediaNotificationManager; +import org.chromium.components.browser_ui.notifications.NotificationWrapper; +import org.chromium.components.browser_ui.notifications.NotificationWrapperBuilder; import org.chromium.components.media_router.MediaRouterClient; import org.chromium.content_public.browser.WebContents; import org.chromium.weblayer_private.IntentUtils; import org.chromium.weblayer_private.TabImpl; +import org.chromium.weblayer_private.WebLayerImpl; +import org.chromium.weblayer_private.interfaces.RemoteMediaServiceConstants; /** Provides WebLayer-specific behavior for Media Router. */ @JNINamespace("weblayer") public class MediaRouterClientImpl extends MediaRouterClient { + static int sPresentationNotificationId; + static int sRemotingNotificationId; + private MediaRouterClientImpl() {} + public static void serviceStarted(Service service, Intent intent) { + int notificationId = intent.getIntExtra(RemoteMediaServiceConstants.NOTIFICATION_ID_KEY, 0); + if (notificationId == 0) { + throw new RuntimeException("Invalid RemoteMediaService notification id"); + } + MediaSessionNotificationHelper.serviceStarted(service, intent, notificationId); + } + + public static void serviceDestroyed(int notificationId) { + MediaSessionNotificationHelper.serviceDestroyed(notificationId); + } + @Override public int getTabId(WebContents webContents) { TabImpl tab = TabImpl.fromWebContents(webContents); @@ -34,7 +62,19 @@ public class MediaRouterClientImpl extends MediaRouterClient { @Override public void showNotification(MediaNotificationInfo notificationInfo) { - // TODO: implement. + MediaNotificationManager.show(notificationInfo, () -> { + return new MediaRouterNotificationControllerDelegate(notificationInfo.id); + }); + } + + @Override + public int getPresentationNotificationId() { + return getPresentationNotificationIdFromClient(); + } + + @Override + public int getRemotingNotificationId() { + return getRemotingNotificationIdFromClient(); } @Override @@ -51,4 +91,74 @@ public class MediaRouterClientImpl extends MediaRouterClient { MediaRouterClient.setInstance(new MediaRouterClientImpl()); } + + @CalledByNative + public static boolean isMediaRouterEnabled() { + Context context = ContextUtils.getApplicationContext(); + try { + ApplicationInfo ai = context.getPackageManager().getApplicationInfo( + context.getPackageName(), PackageManager.GET_META_DATA); + return ai.metaData.getBoolean(RemoteMediaServiceConstants.FEATURE_ENABLED_KEY); + } catch (NameNotFoundException e) { + return false; + } + } + + private static class MediaRouterNotificationControllerDelegate + implements MediaNotificationController.Delegate { + // The ID distinguishes between Presentation and Remoting services/notifications. + private final int mNotificationId; + + MediaRouterNotificationControllerDelegate(int notificationId) { + mNotificationId = notificationId; + } + + @Override + public Intent createServiceIntent() { + return WebLayerImpl.createRemoteMediaServiceIntent().putExtra( + RemoteMediaServiceConstants.NOTIFICATION_ID_KEY, mNotificationId); + } + + @Override + public String getAppName() { + return WebLayerImpl.getClientApplicationName(); + } + + @Override + public String getNotificationGroupName() { + if (mNotificationId == getPresentationNotificationIdFromClient()) { + return "org.chromium.weblayer.PresentationApi"; + } + + assert mNotificationId == getRemotingNotificationIdFromClient(); + return "org.chromium.weblayer.RemotePlaybackApi"; + } + + @Override + public NotificationWrapperBuilder createNotificationWrapperBuilder() { + return MediaSessionNotificationHelper.createNotificationWrapperBuilder(mNotificationId); + } + + @Override + public void onMediaSessionUpdated(MediaSessionCompat session) { + // TODO(estade): implement. + } + + @Override + public void logNotificationShown(NotificationWrapper notification) {} + } + + private static int getPresentationNotificationIdFromClient() { + if (sPresentationNotificationId == 0) { + sPresentationNotificationId = WebLayerImpl.getPresentationApiNotificationId(); + } + return sPresentationNotificationId; + } + + private static int getRemotingNotificationIdFromClient() { + if (sRemotingNotificationId == 0) { + sRemotingNotificationId = WebLayerImpl.getRemotePlaybackApiNotificationId(); + } + return sRemotingNotificationId; + } } diff --git a/chromium/weblayer/browser/java/org/chromium/weblayer_private/media/MediaSessionManager.java b/chromium/weblayer/browser/java/org/chromium/weblayer_private/media/MediaSessionManager.java index 3311c77593a..d555a77cf20 100644 --- a/chromium/weblayer/browser/java/org/chromium/weblayer_private/media/MediaSessionManager.java +++ b/chromium/weblayer/browser/java/org/chromium/weblayer_private/media/MediaSessionManager.java @@ -12,14 +12,12 @@ import org.chromium.components.browser_ui.media.MediaNotificationController; import org.chromium.components.browser_ui.media.MediaNotificationInfo; import org.chromium.components.browser_ui.media.MediaNotificationManager; import org.chromium.components.browser_ui.media.MediaSessionHelper; -import org.chromium.components.browser_ui.notifications.ForegroundServiceUtils; -import org.chromium.components.browser_ui.notifications.NotificationMetadata; import org.chromium.components.browser_ui.notifications.NotificationWrapper; import org.chromium.components.browser_ui.notifications.NotificationWrapperBuilder; +import org.chromium.components.embedder_support.browser_context.BrowserContextHandle; import org.chromium.weblayer_private.IntentUtils; +import org.chromium.weblayer_private.TabImpl; import org.chromium.weblayer_private.WebLayerImpl; -import org.chromium.weblayer_private.WebLayerNotificationChannels; -import org.chromium.weblayer_private.WebLayerNotificationWrapperBuilder; /** * A glue class for MediaSession. @@ -31,43 +29,30 @@ public class MediaSessionManager { private static int sNotificationId; public static void serviceStarted(Service service, Intent intent) { - MediaNotificationController controller = getController(); - if (controller != null && controller.processIntent(service, intent)) return; - - // The service has been started with startForegroundService() but the - // notification hasn't been shown. See similar logic in {@link - // ChromeMediaNotificationControllerDelegate}. - MediaNotificationController.finishStartingForegroundServiceOnO( - service, createNotificationWrapperBuilder().buildNotificationWrapper()); - // Call stopForeground to guarantee Android unset the foreground bit. - ForegroundServiceUtils.getInstance().stopForeground( - service, Service.STOP_FOREGROUND_REMOVE); - service.stopSelf(); + MediaSessionNotificationHelper.serviceStarted(service, intent, getNotificationId()); } public static void serviceDestroyed() { - MediaNotificationController controller = getController(); - if (controller != null) controller.onServiceDestroyed(); - MediaNotificationManager.clear(getNotificationId()); + MediaSessionNotificationHelper.serviceDestroyed(getNotificationId()); } - public static MediaSessionHelper.Delegate createMediaSessionHelperDelegate(int tabId) { + public static MediaSessionHelper.Delegate createMediaSessionHelperDelegate(TabImpl tab) { return new MediaSessionHelper.Delegate() { @Override public Intent createBringTabToFrontIntent() { - return IntentUtils.createBringTabToFrontIntent(tabId); + return IntentUtils.createBringTabToFrontIntent(tab.getId()); } @Override - public boolean fetchLargeFaviconImage() { - // TODO(crbug.com/1076463): WebLayer doesn't support favicons. - return false; + public BrowserContextHandle getBrowserContextHandle() { + return tab.getProfile(); } @Override public MediaNotificationInfo.Builder createMediaNotificationInfoBuilder() { - return new MediaNotificationInfo.Builder().setInstanceId(tabId).setId( - getNotificationId()); + return new MediaNotificationInfo.Builder() + .setInstanceId(tab.getId()) + .setId(getNotificationId()); } @Override @@ -79,12 +64,13 @@ public class MediaSessionManager { @Override public void hideMediaNotification() { - MediaNotificationManager.hide(tabId, getNotificationId()); + MediaNotificationManager.hide(tab.getId(), getNotificationId()); } @Override public void activateAndroidMediaSession() { - MediaNotificationManager.activateAndroidMediaSession(tabId, getNotificationId()); + MediaNotificationManager.activateAndroidMediaSession( + tab.getId(), getNotificationId()); } }; } @@ -108,7 +94,8 @@ public class MediaSessionManager { @Override public NotificationWrapperBuilder createNotificationWrapperBuilder() { - return MediaSessionManager.createNotificationWrapperBuilder(); + return MediaSessionNotificationHelper.createNotificationWrapperBuilder( + getNotificationId()); } @Override @@ -120,22 +107,8 @@ public class MediaSessionManager { public void logNotificationShown(NotificationWrapper notification) {} } - private static NotificationWrapperBuilder createNotificationWrapperBuilder() { - // Only the null tag will work as expected, because {@link Service#startForeground()} only - // takes an ID and no tag. If we pass a tag here, then the notification that's used to - // display a paused state (no foreground service) will not be identified as the same one - // that's used with the foreground service. - return WebLayerNotificationWrapperBuilder.create( - WebLayerNotificationChannels.ChannelId.MEDIA_PLAYBACK, - new NotificationMetadata(0, null /*notificationTag*/, getNotificationId())); - } - private static int getNotificationId() { if (sNotificationId == 0) sNotificationId = WebLayerImpl.getMediaSessionNotificationId(); return sNotificationId; } - - private static MediaNotificationController getController() { - return MediaNotificationManager.getController(getNotificationId()); - } } diff --git a/chromium/weblayer/browser/java/org/chromium/weblayer_private/media/MediaSessionNotificationHelper.java b/chromium/weblayer/browser/java/org/chromium/weblayer_private/media/MediaSessionNotificationHelper.java new file mode 100644 index 00000000000..fbd9b5da8b3 --- /dev/null +++ b/chromium/weblayer/browser/java/org/chromium/weblayer_private/media/MediaSessionNotificationHelper.java @@ -0,0 +1,55 @@ +// 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_private.media; + +import android.app.Service; +import android.content.Intent; + +import org.chromium.components.browser_ui.media.MediaNotificationController; +import org.chromium.components.browser_ui.media.MediaNotificationManager; +import org.chromium.components.browser_ui.notifications.ForegroundServiceUtils; +import org.chromium.components.browser_ui.notifications.NotificationMetadata; +import org.chromium.components.browser_ui.notifications.NotificationWrapperBuilder; +import org.chromium.weblayer_private.WebLayerNotificationChannels; +import org.chromium.weblayer_private.WebLayerNotificationWrapperBuilder; + +/** + * A helper class for management of MediaSession (local device), Presentation API and Remote + * Playback API (casting) notifications and foreground services. + */ +class MediaSessionNotificationHelper { + static void serviceStarted(Service service, Intent intent, int notificationId) { + MediaNotificationController controller = + MediaNotificationManager.getController(notificationId); + if (controller != null && controller.processIntent(service, intent)) return; + + // The service has been started with startForegroundService() but the + // notification hasn't been shown. See similar logic in {@link + // ChromeMediaNotificationControllerDelegate}. + MediaNotificationController.finishStartingForegroundServiceOnO(service, + createNotificationWrapperBuilder(notificationId).buildNotificationWrapper()); + // Call stopForeground to guarantee Android unset the foreground bit. + ForegroundServiceUtils.getInstance().stopForeground( + service, Service.STOP_FOREGROUND_REMOVE); + service.stopSelf(); + } + + static void serviceDestroyed(int notificationId) { + MediaNotificationController controller = + MediaNotificationManager.getController(notificationId); + if (controller != null) controller.onServiceDestroyed(); + MediaNotificationManager.clear(notificationId); + } + + static NotificationWrapperBuilder createNotificationWrapperBuilder(int notificationId) { + // Only the null tag will work as expected, because {@link Service#startForeground()} only + // takes an ID and no tag. If we pass a tag here, then the notification that's used to + // display a paused state (no foreground service) will not be identified as the same one + // that's used with the foreground service. + return WebLayerNotificationWrapperBuilder.create( + WebLayerNotificationChannels.ChannelId.MEDIA_PLAYBACK, + new NotificationMetadata(0, null /*notificationTag*/, notificationId)); + } +} diff --git a/chromium/weblayer/browser/java/org/chromium/weblayer_private/payments/OWNERS b/chromium/weblayer/browser/java/org/chromium/weblayer_private/payments/OWNERS new file mode 100644 index 00000000000..faba26b9c87 --- /dev/null +++ b/chromium/weblayer/browser/java/org/chromium/weblayer_private/payments/OWNERS @@ -0,0 +1,5 @@ +# TEAM: payments-dev@chromium.org +# COMPONENT: Blink>Payments +# OS: Android + +file://components/payments/OWNERS diff --git a/chromium/weblayer/browser/java/org/chromium/weblayer_private/payments/WebLayerPaymentRequestFactory.java b/chromium/weblayer/browser/java/org/chromium/weblayer_private/payments/WebLayerPaymentRequestFactory.java new file mode 100644 index 00000000000..c3231fc4fde --- /dev/null +++ b/chromium/weblayer/browser/java/org/chromium/weblayer_private/payments/WebLayerPaymentRequestFactory.java @@ -0,0 +1,112 @@ +// 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_private.payments; + +import androidx.annotation.Nullable; + +import org.chromium.components.embedder_support.browser_context.BrowserContextHandle; +import org.chromium.components.payments.InvalidPaymentRequest; +import org.chromium.components.payments.PaymentFeatureList; +import org.chromium.components.payments.PaymentRequestService; +import org.chromium.components.payments.PaymentRequestServiceUtil; +import org.chromium.components.payments.PrefsStrings; +import org.chromium.components.user_prefs.UserPrefs; +import org.chromium.content_public.browser.FeaturePolicyFeature; +import org.chromium.content_public.browser.RenderFrameHost; +import org.chromium.content_public.browser.WebContents; +import org.chromium.content_public.browser.WebContentsStatics; +import org.chromium.mojo.system.MojoException; +import org.chromium.mojo.system.MojoResult; +import org.chromium.payments.mojom.PaymentRequest; +import org.chromium.services.service_manager.InterfaceFactory; +import org.chromium.weblayer_private.ProfileImpl; +import org.chromium.weblayer_private.TabImpl; + +/** Creates an instance of PaymentRequest for use in WebLayer. */ +public class WebLayerPaymentRequestFactory implements InterfaceFactory<PaymentRequest> { + private final RenderFrameHost mRenderFrameHost; + + /** + * Production implementation of the WebLayerPaymentRequestService's Delegate. Gives true answers + * about the system. + */ + private static class WebLayerPaymentRequestDelegateImpl + implements PaymentRequestService.Delegate { + private final RenderFrameHost mRenderFrameHost; + + /* package */ WebLayerPaymentRequestDelegateImpl(RenderFrameHost renderFrameHost) { + mRenderFrameHost = renderFrameHost; + } + + @Override + public boolean isOffTheRecord() { + ProfileImpl profile = getProfile(); + if (profile == null) return true; + return profile.isIncognito(); + } + + @Override + public String getInvalidSslCertificateErrorMessage() { + assert false : "Not implemented yet"; + return ""; + } + + @Override + public boolean prefsCanMakePayment() { + BrowserContextHandle profile = getProfile(); + return profile != null + && UserPrefs.get(profile).getBoolean(PrefsStrings.CAN_MAKE_PAYMENT_ENABLED); + } + + @Nullable + @Override + public String getTwaPackageName() { + return null; + } + + @Nullable + private ProfileImpl getProfile() { + WebContents webContents = + PaymentRequestServiceUtil.getLiveWebContents(mRenderFrameHost); + if (webContents == null) return null; + TabImpl tab = TabImpl.fromWebContents(webContents); + if (tab == null) return null; + return tab.getProfile(); + } + } + + /** + * Creates an instance of WebLayerPaymentRequestFactory. + * @param renderFrameHost The frame that issues the payment request on the merchant page. + */ + public WebLayerPaymentRequestFactory(RenderFrameHost renderFrameHost) { + mRenderFrameHost = renderFrameHost; + } + + @Override + public PaymentRequest createImpl() { + if (mRenderFrameHost == null) return new InvalidPaymentRequest(); + if (!mRenderFrameHost.isFeatureEnabled(FeaturePolicyFeature.PAYMENT)) { + mRenderFrameHost.getRemoteInterfaces().onConnectionError( + new MojoException(MojoResult.PERMISSION_DENIED)); + return null; + } + + if (!PaymentFeatureList.isEnabled(PaymentFeatureList.WEB_PAYMENTS)) { + return new InvalidPaymentRequest(); + } + + PaymentRequestService.Delegate delegate = + new WebLayerPaymentRequestDelegateImpl(mRenderFrameHost); + + WebContents webContents = WebContentsStatics.fromRenderFrameHost(mRenderFrameHost); + if (webContents == null || webContents.isDestroyed()) return new InvalidPaymentRequest(); + + return PaymentRequestService.createPaymentRequest(mRenderFrameHost, + /*isOffTheRecord=*/delegate.isOffTheRecord(), delegate, + (paymentRequestService) + -> new WebLayerPaymentRequestService(paymentRequestService, delegate)); + } +} diff --git a/chromium/weblayer/browser/java/org/chromium/weblayer_private/payments/WebLayerPaymentRequestService.java b/chromium/weblayer/browser/java/org/chromium/weblayer_private/payments/WebLayerPaymentRequestService.java new file mode 100644 index 00000000000..15a27e1be91 --- /dev/null +++ b/chromium/weblayer/browser/java/org/chromium/weblayer_private/payments/WebLayerPaymentRequestService.java @@ -0,0 +1,57 @@ +// 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_private.payments; + +import org.chromium.components.payments.BrowserPaymentRequest; +import org.chromium.components.payments.PaymentAppService; +import org.chromium.components.payments.PaymentRequestService; +import org.chromium.components.payments.PaymentRequestService.Delegate; +import org.chromium.payments.mojom.PaymentDetails; +import org.chromium.payments.mojom.PaymentValidationErrors; + +/** The WebLayer-specific part of the payment request service. */ +public class WebLayerPaymentRequestService implements BrowserPaymentRequest { + /** + * Create an instance of {@link WebLayerPaymentRequestService}. + * @param paymentRequestService The payment request service. + * @param delegate The delegate of the payment request service. + */ + public WebLayerPaymentRequestService( + PaymentRequestService paymentRequestService, Delegate delegate) { + assert false : "Not implemented yet"; + } + + @Override + public void onPaymentDetailsUpdated( + PaymentDetails details, boolean hasNotifiedInvokedPaymentApp) { + assert false : "Not implemented yet"; + } + + @Override + public void onPaymentDetailsNotUpdated(String selectedShippingOptionError) { + assert false : "Not implemented yet"; + } + + @Override + public void complete(int result) { + assert false : "Not implemented yet"; + } + + @Override + public void retry(PaymentValidationErrors errors) { + assert false : "Not implemented yet"; + } + + @Override + public void close() { + assert false : "Not implemented yet"; + } + + @Override + public void addPaymentAppFactories(PaymentAppService service) { + assert false : "Not implemented yet"; + } + +} diff --git a/chromium/weblayer/browser/java/org/chromium/weblayer_private/test_interfaces/ITestWebLayer.aidl b/chromium/weblayer/browser/java/org/chromium/weblayer_private/test_interfaces/ITestWebLayer.aidl index 53eff3330ed..e11d7a1ded7 100644 --- a/chromium/weblayer/browser/java/org/chromium/weblayer_private/test_interfaces/ITestWebLayer.aidl +++ b/chromium/weblayer/browser/java/org/chromium/weblayer_private/test_interfaces/ITestWebLayer.aidl @@ -4,6 +4,8 @@ package org.chromium.weblayer_private.test_interfaces; +import android.os.Bundle; +import org.chromium.weblayer_private.interfaces.IBrowser; import org.chromium.weblayer_private.interfaces.IObjectWrapper; import org.chromium.weblayer_private.interfaces.ITab; @@ -38,7 +40,7 @@ interface ITestWebLayer { void addInfoBar(in ITab tab, in IObjectWrapper runnable) = 10; // Gets the infobar container view associated with |tab|. - IObjectWrapper getInfoBarContainerView(in ITab tab) = 11; + IObjectWrapper /* View */ getInfoBarContainerView(in ITab tab) = 11; void setIgnoreMissingKeyForTranslateManager(in boolean ignore) = 12; void forceNetworkConnectivityState(in boolean networkAvailable) = 13; @@ -53,4 +55,19 @@ interface ITestWebLayer { // Returns true if a fullscreen toast was shown for |tab|. boolean didShowFullscreenToast(in ITab tab) = 17; + + // Does setup for MediaRouter tests, mocking out Chromecast devices. + void initializeMockMediaRouteProvider( + boolean closeRouteWithErrorOnSend, boolean disableIsSupportsSource, + in String createRouteErrorMessage, in String joinRouteErrorMessage) = 18; + + // Gets a button from the currently visible media route selection dialog. The button represents a + // route and contains the text |name|. Returns null if no such dialog or button exists. + IObjectWrapper /* View */ getMediaRouteButton(String name) = 19; + + // Causes the renderer process in the tab's main frame to crash. + void crashTab(in ITab tab) = 20; + + boolean isWindowOnSmallDevice(in IBrowser browser) = 21; + IObjectWrapper getSecurityButton(IObjectWrapper /* View */ urlBarView) = 22; } |