diff options
Diffstat (limited to 'chromium/weblayer/public')
40 files changed, 1200 insertions, 41 deletions
diff --git a/chromium/weblayer/public/browser.h b/chromium/weblayer/public/browser.h index 44e4c95fc79..8932998183b 100644 --- a/chromium/weblayer/public/browser.h +++ b/chromium/weblayer/public/browser.h @@ -46,12 +46,16 @@ class Browser { virtual ~Browser() {} - virtual Tab* AddTab(std::unique_ptr<Tab> tab) = 0; - virtual std::unique_ptr<Tab> RemoveTab(Tab* tab) = 0; + virtual void AddTab(Tab* tab) = 0; + virtual void DestroyTab(Tab* tab) = 0; virtual void SetActiveTab(Tab* tab) = 0; virtual Tab* GetActiveTab() = 0; virtual std::vector<Tab*> GetTabs() = 0; + // Creates a tab attached to this browser. The returned tab is owned by the + // browser. + virtual Tab* CreateTab() = 0; + // Called early on in shutdown, before any tabs have been removed. virtual void PrepareForShutdown() = 0; diff --git a/chromium/weblayer/public/java/AndroidManifest.xml b/chromium/weblayer/public/java/AndroidManifest.xml index 13a2de7a715..90f4ab59033 100644 --- a/chromium/weblayer/public/java/AndroidManifest.xml +++ b/chromium/weblayer/public/java/AndroidManifest.xml @@ -13,6 +13,7 @@ <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/> <uses-permission android:name="android.permission.CAMERA"/> + <uses-permission android:name="android.permission.FOREGROUND_SERVICE" /> <uses-permission android:name="android.permission.INTERNET"/> <uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS"/> <uses-permission android:name="android.permission.RECORD_AUDIO"/> @@ -68,9 +69,19 @@ <action android:name="org.chromium.weblayer.downloads.PAUSE"/> <action android:name="org.chromium.weblayer.downloads.RESUME"/> <action android:name="org.chromium.weblayer.downloads.CANCEL"/> + <!-- this needs to be in sync with IntentUtils.java--> + <action android:name="org.chromium.weblayer.intent_utils.ACTIVATE_TAB"/> <!-- this needs to be in sync with MediaStreamManager.java--> + <!-- TODO(estade): deprecated, remove in M88. --> <action android:name="org.chromium.weblayer.webrtc.ACTIVATE_TAB"/> </intent-filter> </receiver> + + <service android:name="org.chromium.weblayer.MediaSessionService" + android:exported="false"> + <intent-filter> + <action android:name="android.intent.action.MEDIA_BUTTON" /> + </intent-filter> + </service> </application> </manifest> diff --git a/chromium/weblayer/public/java/BUILD.gn b/chromium/weblayer/public/java/BUILD.gn index 26cf9072075..71c7c3ca23c 100644 --- a/chromium/weblayer/public/java/BUILD.gn +++ b/chromium/weblayer/public/java/BUILD.gn @@ -18,11 +18,18 @@ jinja_template("weblayer_client_manifest") { } android_resources("client_resources") { + custom_package = "org.chromium.weblayer" sources = [ + "res/values-night/colors.xml", + "res/values-night/values.xml", + "res/values-v27/styles.xml", + "res/values-v28/styles.xml", + "res/values/colors.xml", + "res/values/ids.xml", "res/values/styles.xml", + "res/values/values.xml", "res/xml/weblayer_file_paths.xml", ] - deps = [ "//components/browser_ui/styles/android:java_resources" ] android_manifest = weblayer_client_manifest android_manifest_dep = ":weblayer_client_manifest" } @@ -53,6 +60,7 @@ android_library("java") { "org/chromium/weblayer/LoadError.java", "org/chromium/weblayer/MediaCaptureCallback.java", "org/chromium/weblayer/MediaCaptureController.java", + "org/chromium/weblayer/MediaSessionService.java", "org/chromium/weblayer/NavigateParams.java", "org/chromium/weblayer/Navigation.java", "org/chromium/weblayer/NavigationCallback.java", @@ -63,6 +71,7 @@ android_library("java") { "org/chromium/weblayer/ObserverList.java", "org/chromium/weblayer/Profile.java", "org/chromium/weblayer/RemoteFragment.java", + "org/chromium/weblayer/ScrollNotificationType.java", "org/chromium/weblayer/SettingType.java", "org/chromium/weblayer/SiteSettingsActivity.java", "org/chromium/weblayer/SiteSettingsFragment.java", @@ -75,6 +84,9 @@ android_library("java") { "org/chromium/weblayer/UrlBarOptions.java", "org/chromium/weblayer/WebLayer.java", "org/chromium/weblayer/WebLayerFileProvider.java", + "org/chromium/weblayer/WebMessage.java", + "org/chromium/weblayer/WebMessageCallback.java", + "org/chromium/weblayer/WebMessageReplyProxy.java", "org/chromium/weblayer/WebViewCompatibilityHelper.java", _version_constants_java_file, ] diff --git a/chromium/weblayer/public/java/org/chromium/weblayer/Browser.java b/chromium/weblayer/public/java/org/chromium/weblayer/Browser.java index 1afd12ba996..41ec9e5af4e 100644 --- a/chromium/weblayer/public/java/org/chromium/weblayer/Browser.java +++ b/chromium/weblayer/public/java/org/chromium/weblayer/Browser.java @@ -236,6 +236,27 @@ public class Browser { } /** + * Creates a new tab attached to this browser. This will call {@link TabListCallback#onTabAdded} + * with the new tab. + * + * @since 85 + */ + public @NonNull Tab createTab() { + ThreadCheck.ensureOnUiThread(); + if (WebLayer.getSupportedMajorVersionInternal() < 85) { + throw new UnsupportedOperationException(); + } + try { + ITab iTab = mImpl.createTab(); + Tab tab = Tab.getTabById(iTab.getId()); + assert tab != null; + return tab; + } catch (RemoteException e) { + throw new APICallException(e); + } + } + + /** * Control support for embedding use cases such as animations. This should be enabled when the * container view of the fragment is animated in any way, needs to be rotated or blended, or * need to control z-order with other views or other BrowserFragmentImpls. Note embedder should diff --git a/chromium/weblayer/public/java/org/chromium/weblayer/MediaSessionService.java b/chromium/weblayer/public/java/org/chromium/weblayer/MediaSessionService.java new file mode 100644 index 00000000000..c13b09c1207 --- /dev/null +++ b/chromium/weblayer/public/java/org/chromium/weblayer/MediaSessionService.java @@ -0,0 +1,96 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package org.chromium.weblayer; + +import android.app.Service; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.media.AudioManager; +import android.os.IBinder; +import android.os.RemoteException; + +import org.chromium.weblayer_private.interfaces.ObjectWrapper; + +/** + * A foreground {@link Service} for the Web MediaSession API. + * This class is a thin wrapper that forwards lifecycle events to the WebLayer implementation, which + * in turn manages a system notification and {@link MediaSession}. This service will be in the + * foreground when the MediaSession is active. + * @since 85 + */ +public class MediaSessionService extends Service { + // A helper to automatically pause the media session when a user removes headphones. + private BroadcastReceiver mAudioBecomingNoisyReceiver; + + public MediaSessionService() { + if (WebLayer.getSupportedMajorVersionInternal() < 85) { + throw new UnsupportedOperationException(); + } + } + + @Override + public IBinder onBind(Intent intent) { + return null; + } + + @Override + public void onCreate() { + super.onCreate(); + + mAudioBecomingNoisyReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + if (!AudioManager.ACTION_AUDIO_BECOMING_NOISY.equals(intent.getAction())) { + return; + } + + Intent i = new Intent(getApplication(), MediaSessionService.class); + i.setAction(intent.getAction()); + getApplication().startService(i); + } + }; + + IntentFilter filter = new IntentFilter(AudioManager.ACTION_AUDIO_BECOMING_NOISY); + registerReceiver(mAudioBecomingNoisyReceiver, filter); + } + + @Override + public void onDestroy() { + super.onDestroy(); + + try { + getWebLayer().getImpl().onMediaSessionServiceDestroyed(); + } catch (RemoteException e) { + throw new RuntimeException(e); + } + + unregisterReceiver(mAudioBecomingNoisyReceiver); + } + + @Override + public int onStartCommand(Intent intent, int flags, int startId) { + try { + getWebLayer().getImpl().onMediaSessionServiceStarted(ObjectWrapper.wrap(this), intent); + } catch (RemoteException e) { + throw new RuntimeException(e); + } + + return START_NOT_STICKY; + } + + private WebLayer getWebLayer() { + WebLayer webLayer; + try { + webLayer = WebLayer.getLoadedWebLayer(getApplication()); + } catch (UnsupportedVersionException e) { + throw new RuntimeException(e); + } + if (webLayer == null) { + throw new IllegalStateException("WebLayer not initialized"); + } + return webLayer; + } +} diff --git a/chromium/weblayer/public/java/org/chromium/weblayer/Navigation.java b/chromium/weblayer/public/java/org/chromium/weblayer/Navigation.java index 85c880d09c5..53180286877 100644 --- a/chromium/weblayer/public/java/org/chromium/weblayer/Navigation.java +++ b/chromium/weblayer/public/java/org/chromium/weblayer/Navigation.java @@ -172,6 +172,10 @@ public class Navigation extends IClientNavigation.Stub { * NavigationCallback.onNavigationStarted}. When called during start, the header applies to both * the initial network request as well as redirects. * + * This method may be used to set the referer. If the referer is set in navigation start, it is + * reset during the redirect. In other words, if you need to set a referer that applies to + * redirects, then this must be called from {@link onNavigationRedirected}. + * * @param name The name of the header. The name must be rfc 2616 compliant. * @param value The value of the header. The value must not contain '\0', '\n' or '\r'. * diff --git a/chromium/weblayer/public/java/org/chromium/weblayer/NavigationCallback.java b/chromium/weblayer/public/java/org/chromium/weblayer/NavigationCallback.java index 6b64268a386..3b85def6ef8 100644 --- a/chromium/weblayer/public/java/org/chromium/weblayer/NavigationCallback.java +++ b/chromium/weblayer/public/java/org/chromium/weblayer/NavigationCallback.java @@ -4,6 +4,8 @@ package org.chromium.weblayer; +import android.net.Uri; + import androidx.annotation.NonNull; /** @@ -112,4 +114,12 @@ public abstract class NavigationCallback { * non-empty layout has finished. This is *not* called for same-document navigations. */ public void onFirstContentfulPaint() {} + + /** + * Called after each navigation to indicate that the old page is no longer + * being rendered. Note this is not ordered with respect to onFirstContentfulPaint. + * @param newNavigationUri Uri of the new navigation. + * @since 85 + */ + public void onOldPageNoLongerRendered(@NonNull Uri newNavigationUri) {} } diff --git a/chromium/weblayer/public/java/org/chromium/weblayer/NavigationController.java b/chromium/weblayer/public/java/org/chromium/weblayer/NavigationController.java index 6267141c69f..d36650ff973 100644 --- a/chromium/weblayer/public/java/org/chromium/weblayer/NavigationController.java +++ b/chromium/weblayer/public/java/org/chromium/weblayer/NavigationController.java @@ -69,6 +69,9 @@ public class NavigationController { /** * Navigates to the previous navigation. * + * Note: this may go back more than a single navigation entry, see {@link + * isNavigationEntrySkippable} for more details. + * * @throws IndexOutOfBoundsException If {@link #canGoBack} returns false. */ public void goBack() throws IndexOutOfBoundsException { @@ -103,6 +106,9 @@ public class NavigationController { /** * Returns true if there is a navigation before the current one. * + * Note: this may return false even if the current index is not 0, see {@link + * isNavigationEntrySkippable} for more details. + * * @return Whether there is a navigation before the current one. */ public boolean canGoBack() { @@ -212,6 +218,7 @@ public class NavigationController { @NonNull public Uri getNavigationEntryDisplayUri(int index) throws IndexOutOfBoundsException { ThreadCheck.ensureOnUiThread(); + checkNavigationIndex(index); try { return Uri.parse(mNavigationController.getNavigationEntryDisplayUri(index)); } catch (RemoteException e) { @@ -219,12 +226,6 @@ public class NavigationController { } } - private void checkNavigationIndex(int index) throws IndexOutOfBoundsException { - if (index < 0 || index >= getNavigationListSize()) { - throw new IndexOutOfBoundsException(); - } - } - /** * Returns the title of the navigation entry at the supplied index. * @@ -246,6 +247,28 @@ public class NavigationController { } } + /** + * Returns whether this entry will be skipped on a call to {@link goBack} or {@link goForward}. + * This will be true for certain navigations, such as certain client side redirects and + * history.pushState navigations done without user interaction. + * + * @throws IndexOutOfBoundsException If index is not between 0 and {@link + * getNavigationListCurrentIndex}. + * @since 85 + */ + public boolean isNavigationEntrySkippable(int index) throws IndexOutOfBoundsException { + ThreadCheck.ensureOnUiThread(); + if (WebLayer.getSupportedMajorVersionInternal() < 85) { + throw new UnsupportedOperationException(); + } + checkNavigationIndex(index); + try { + return mNavigationController.isNavigationEntrySkippable(index); + } catch (RemoteException e) { + throw new APICallException(e); + } + } + public void registerNavigationCallback(@NonNull NavigationCallback callback) { ThreadCheck.ensureOnUiThread(); mCallbacks.addObserver(callback); @@ -256,6 +279,12 @@ public class NavigationController { mCallbacks.removeObserver(callback); } + private void checkNavigationIndex(int index) throws IndexOutOfBoundsException { + if (index < 0 || index >= getNavigationListSize()) { + throw new IndexOutOfBoundsException(); + } + } + private final class NavigationControllerClientImpl extends INavigationControllerClient.Stub { @Override public IClientNavigation createClientNavigation(INavigation navigationImpl) { @@ -326,5 +355,13 @@ public class NavigationController { callback.onFirstContentfulPaint(); } } + + @Override + public void onOldPageNoLongerRendered(String uri) { + StrictModeWorkaround.apply(); + for (NavigationCallback callback : mCallbacks) { + callback.onOldPageNoLongerRendered(Uri.parse(uri)); + } + } } } diff --git a/chromium/weblayer/public/java/org/chromium/weblayer/Profile.java b/chromium/weblayer/public/java/org/chromium/weblayer/Profile.java index 5d0eab7d54d..ed2071ea780 100644 --- a/chromium/weblayer/public/java/org/chromium/weblayer/Profile.java +++ b/chromium/weblayer/public/java/org/chromium/weblayer/Profile.java @@ -26,6 +26,7 @@ import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.Map; +import java.util.Set; /** * Profile holds state (typically on disk) needed for browsing. Create a @@ -117,13 +118,13 @@ public class Profile { } /** - * Delete all profile data stored on disk. There are a number of edge cases with deleting - * profile data: - * * This method will throw an exception if there are any existing usage of this Profile. For - * example, all BrowserFragment belonging to this profile must be destroyed. - * * This object is considered destroyed after this method returns. Calling any other method - * after will throw exceptions. - * * Creating a new profile of the same name before doneCallback runs will throw an exception. + * Delete all profile data stored on disk. There are a number of edge cases with deleting + * profile data: + * * This method will throw an exception if there are any existing usage of this Profile. For + * example, all BrowserFragment belonging to this profile must be destroyed. + * * This object is considered destroyed after this method returns. Calling any other method + * after will throw exceptions. + * * Creating a new profile of the same name before doneCallback runs will throw an exception. * @since 82 */ public void destroyAndDeleteDataFromDisk(@Nullable Runnable completionCallback) { @@ -254,6 +255,88 @@ public class Profile { } } + /** + * Asynchronously fetches the set of known Browser persistence-ids. See + * {@link WebLayer#createBrowserFragment} for details on the persistence-id. + * + * @param callback The callback that is supplied the set of ids. + * + * @throws IllegalStateException If called on an in memory profile. + * + * @since 85 + */ + public void getBrowserPersistenceIds(@NonNull Callback<Set<String>> callback) { + ThreadCheck.ensureOnUiThread(); + if (WebLayer.getSupportedMajorVersionInternal() < 85) { + throw new UnsupportedOperationException(); + } + if (mName.isEmpty()) { + throw new IllegalStateException( + "getBrowserPersistenceIds() is not applicable to in-memory profiles"); + } + try { + mImpl.getBrowserPersistenceIds( + ObjectWrapper.wrap((ValueCallback<Set<String>>) callback::onResult)); + } catch (RemoteException e) { + throw new APICallException(e); + } + } + + /** + * Asynchronously removes the storage associated with the set of Browser persistence-ids. This + * ignores ids actively in use. {@link doneCallback} is supplied the result of the operation. A + * value of true means all files were removed. A value of false indicates at least one of the + * files could not be removed. + * + * @param callback The callback that is supplied the result of the operation. + * + * @throws IllegalStateException If called on an in memory profile. + * @throws IllegalArgumentException if {@link ids} contains an empty/null string. + * + * @since 85 + */ + public void removeBrowserPersistenceStorage( + @NonNull Set<String> ids, @NonNull Callback<Boolean> callback) { + ThreadCheck.ensureOnUiThread(); + if (WebLayer.getSupportedMajorVersionInternal() < 85) { + throw new UnsupportedOperationException(); + } + if (mName.isEmpty()) { + throw new IllegalStateException( + "removetBrowserPersistenceStorage() is not applicable to in-memory profiles"); + } + try { + mImpl.removeBrowserPersistenceStorage(ids.toArray(new String[ids.size()]), + ObjectWrapper.wrap((ValueCallback<Boolean>) callback::onResult)); + } catch (RemoteException e) { + throw new APICallException(e); + } + } + + /** + * For cross-origin navigations, the implementation may leverage a separate OS process for + * stronger isolation. If an embedder knows that a cross-origin navigation is likely starting + * soon, they can call this method as a hint to the implementation to start a fresh OS process. + * A subsequent navigation may use this preinitialized process, improving performance. It is + * safe to call this multiple times or when it is not certain that the spare renderer will be + * used, although calling this too eagerly may reduce performance as unnecessary processes are + * created. + * + * @since 85 + */ + public void prepareForPossibleCrossOriginNavigation() { + ThreadCheck.ensureOnUiThread(); + if (WebLayer.getSupportedMajorVersionInternal() < 85) { + throw new UnsupportedOperationException(); + } + + try { + mImpl.prepareForPossibleCrossOriginNavigation(); + } catch (RemoteException e) { + throw new APICallException(e); + } + } + static final class DownloadCallbackClientImpl extends IDownloadCallbackClient.Stub { private final DownloadCallback mCallback; diff --git a/chromium/weblayer/public/java/org/chromium/weblayer/ScrollNotificationType.java b/chromium/weblayer/public/java/org/chromium/weblayer/ScrollNotificationType.java new file mode 100644 index 00000000000..b99949fe402 --- /dev/null +++ b/chromium/weblayer/public/java/org/chromium/weblayer/ScrollNotificationType.java @@ -0,0 +1,34 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package org.chromium.weblayer; + +import androidx.annotation.IntDef; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +/** + * @hide + */ +@IntDef({ScrollNotificationType.DIRECTION_CHANGED_UP, + ScrollNotificationType.DIRECTION_CHANGED_DOWN}) +@Retention(RetentionPolicy.SOURCE) +public @interface ScrollNotificationType { + /** + * This is the direction toward vertical scroll offset 0. Note direction change notification + * is sent on direction change. If there are two consecutive scrolls in the same direction, + * the second scroll will not generate a direction change notification. Also the notification + * is sent as a result of scroll change; this means for touch scrolls, this is sent (if there + * is a direction change) on the first touch move, not touch down. + */ + int DIRECTION_CHANGED_UP = + org.chromium.weblayer_private.interfaces.ScrollNotificationType.DIRECTION_CHANGED_UP; + + /** + * This is the direction away from vertical scroll offset 0. See notes on DIRECTION_CHANGED_UP. + */ + int DIRECTION_CHANGED_DOWN = + org.chromium.weblayer_private.interfaces.ScrollNotificationType.DIRECTION_CHANGED_DOWN; +} diff --git a/chromium/weblayer/public/java/org/chromium/weblayer/SettingType.java b/chromium/weblayer/public/java/org/chromium/weblayer/SettingType.java index c5e800e8654..3fbc62492ff 100644 --- a/chromium/weblayer/public/java/org/chromium/weblayer/SettingType.java +++ b/chromium/weblayer/public/java/org/chromium/weblayer/SettingType.java @@ -12,7 +12,9 @@ import java.lang.annotation.RetentionPolicy; /** * @hide */ -@IntDef({SettingType.BASIC_SAFE_BROWSING_ENABLED}) +@IntDef({SettingType.BASIC_SAFE_BROWSING_ENABLED, SettingType.UKM_ENABLED, + SettingType.EXTENDED_REPORTING_SAFE_BROWSING_ENABLED, + SettingType.REAL_TIME_SAFE_BROWSING_ENABLED}) @Retention(RetentionPolicy.SOURCE) public @interface SettingType { /** @@ -21,4 +23,39 @@ public @interface SettingType { */ int BASIC_SAFE_BROWSING_ENABLED = org.chromium.weblayer_private.interfaces.SettingType.BASIC_SAFE_BROWSING_ENABLED; + /** + * Allows the embedder to enable URL-Keyed Metrics. Disabled by default. + */ + int UKM_ENABLED = org.chromium.weblayer_private.interfaces.SettingType.UKM_ENABLED; + + /** + * Allows the embedder to set whether it wants to enable/disable the Extended Reporting + * functionality for Safe Browsing (SBER). This functionality helps improve security on the web + * for everyone. It sends URLs of some pages you visit, limited system information, and some + * page content to Google, to help discover new threats and protect everyone on the web. + * + * This setting is disabled by default, but can also be enabled by the user by checking a + * checkbox in the Safe Browsing interstitial which is displayed when the user encounters a + * dangerous web page. The setting persists on disk. + * + * Note: this setting applies when Safe Browsing is enabled (i.e. BASIC_SAFE_BROWSING_ENABLED + * is true). + * + * @since 85 + */ + int EXTENDED_REPORTING_SAFE_BROWSING_ENABLED = + org.chromium.weblayer_private.interfaces.SettingType + .EXTENDED_REPORTING_SAFE_BROWSING_ENABLED; + + /** + * Allows the embedder to set whether it wants to enable/disable the Safe Browsing Real-time URL + * checks. This functionality is disabled by default. + * + * Note: this setting applies when Safe Browsing is enabled (i.e. BASIC_SAFE_BROWSING_ENABLED + * is true). + * + * @since 85 + */ + int REAL_TIME_SAFE_BROWSING_ENABLED = + org.chromium.weblayer_private.interfaces.SettingType.REAL_TIME_SAFE_BROWSING_ENABLED; } diff --git a/chromium/weblayer/public/java/org/chromium/weblayer/Tab.java b/chromium/weblayer/public/java/org/chromium/weblayer/Tab.java index a4ed8ae8009..bed3eee69cd 100644 --- a/chromium/weblayer/public/java/org/chromium/weblayer/Tab.java +++ b/chromium/weblayer/public/java/org/chromium/weblayer/Tab.java @@ -22,11 +22,14 @@ import org.chromium.weblayer_private.interfaces.IFullscreenCallbackClient; import org.chromium.weblayer_private.interfaces.IObjectWrapper; import org.chromium.weblayer_private.interfaces.ITab; import org.chromium.weblayer_private.interfaces.ITabClient; +import org.chromium.weblayer_private.interfaces.IWebMessageCallbackClient; +import org.chromium.weblayer_private.interfaces.IWebMessageReplyProxy; import org.chromium.weblayer_private.interfaces.ObjectWrapper; import org.chromium.weblayer_private.interfaces.StrictModeWorkaround; import java.util.HashMap; import java.util.HashSet; +import java.util.List; import java.util.Map; import java.util.Set; @@ -344,6 +347,292 @@ public class Tab { } } + /** + * Set arbitrary data on the tab. This will be saved and restored with the browser, so it is + * important to keep this data as small as possible. + * + * @param data The data to set, must be smaller than 4K when serialized. A snapshot of this data + * is taken, so any changes to the passed in object after this call will not be reflected. + * + * @throws IllegalArgumentException if the serialzed size of the data exceeds 4K. + * + * @since 85 + */ + public void setData(@NonNull Map<String, String> data) { + ThreadCheck.ensureOnUiThread(); + if (WebLayer.getSupportedMajorVersionInternal() < 85) { + throw new UnsupportedOperationException(); + } + try { + if (!mImpl.setData(data)) { + throw new IllegalArgumentException("Data given to Tab.setData() was too large."); + } + } catch (RemoteException e) { + throw new APICallException(e); + } + } + + /** + * Get arbitrary data set on the tab with setData(). + * + * @return the data or an empty map if no data was set. + * @since 85 + */ + @NonNull + public Map<String, String> getData() { + ThreadCheck.ensureOnUiThread(); + if (WebLayer.getSupportedMajorVersionInternal() < 85) { + throw new UnsupportedOperationException(); + } + try { + return (Map<String, String>) mImpl.getData(); + } catch (RemoteException e) { + throw new APICallException(e); + } + } + + /** + * Adds a WebMessageCallback and injects a JavaScript object into each frame that the + * WebMessageCallback will listen on. + * + * <p> + * The injected JavaScript object will be named {@code jsObjectName} in the global scope. This + * will inject the JavaScript object in any frame whose origin matches {@code + * allowedOriginRules} for every navigation after this call, and the JavaScript object will be + * available immediately when the page begins to load. + * + * <p> + * Each {@code allowedOriginRules} entry must follow the format {@code SCHEME "://" [ + * HOSTNAME_PATTERN [ ":" PORT ] ]}, each part is explained in the below table: + * + * <table> + * <col width="25%"> + * <tr><th>Rule</th><th>Description</th><th>Example</th></tr> + * + * <tr> + * <td>http/https with hostname</td> + * <td>{@code SCHEME} is http or https; {@code HOSTNAME_PATTERN} is a regular hostname; {@code + * PORT} is optional, when not present, the rule will match port {@code 80} for http and port + * {@code 443} for https.</td> + * <td><ul> + * <li>{@code https://foobar.com:8080} - Matches https:// URL on port 8080, whose normalized + * host is foobar.com.</li> + * <li>{@code https://www.example.com} - Matches https:// URL on port 443, whose normalized host + * is www.example.com.</li> + * </ul></td> + * </tr> + * + * <tr> + * <td>http/https with pattern matching</td> + * <td>{@code SCHEME} is http or https; {@code HOSTNAME_PATTERN} is a sub-domain matching + * pattern with a leading {@code *.}; {@code PORT} is optional, when not present, the rule will + * match port {@code 80} for http and port {@code 443} for https.</td> + * + * <td><ul> + * <li>{@code https://*.example.com} - Matches https://calendar.example.com and + * https://foo.bar.example.com but not https://example.com.</li> + * <li>{@code https://*.example.com:8080} - Matches https://calendar.example.com:8080</li> + * </ul></td> + * </tr> + * + * <tr> + * <td>http/https with IP literal</td> + * <td>{@code SCHEME} is https or https; {@code HOSTNAME_PATTERN} is IP literal; {@code PORT} is + * optional, when not present, the rule will match port {@code 80} for http and port {@code 443} + * for https.</td> + * + * <td><ul> + * <li>{@code https://127.0.0.1} - Matches https:// URL on port 443, whose IPv4 address is + * 127.0.0.1</li> + * <li>{@code https://[::1]} or {@code https://[0:0::1]}- Matches any URL to the IPv6 loopback + * address with port 443.</li> + * <li>{@code https://[::1]:99} - Matches any https:// URL to the IPv6 loopback on port 99.</li> + * </ul></td> + * </tr> + * + * <tr> + * <td>Custom scheme</td> + * <td>{@code SCHEME} is a custom scheme; {@code HOSTNAME_PATTERN} and {@code PORT} must not be + * present.</td> + * <td><ul> + * <li>{@code my-app-scheme://} - Matches any my-app-scheme:// URL.</li> + * </ul></td> + * </tr> + * + * <tr><td>{@code *}</td> + * <td>Wildcard rule, matches any origin.</td> + * <td><ul><li>{@code *}</li></ul></td> + * </table> + * + * <p> + * Note that this is a powerful API, as the JavaScript object will be injected when the frame's + * origin matches any one of the allowed origins. The HTTPS scheme is strongly recommended for + * security; allowing HTTP origins exposes the injected object to any potential network-based + * attackers. If a wildcard {@code "*"} is provided, it will inject the JavaScript object to all + * frames. A wildcard should only be used if the app wants <b>any</b> third party web page to be + * able to use the injected object. When using a wildcard, the app must treat received messages + * as untrustworthy and validate any data carefully. + * + * <p> + * This method can be called multiple times to inject multiple JavaScript objects. + * + * <p> + * Let's say the injected JavaScript object is named {@code myObject}. We will have following + * methods on that object once it is available to use: + * <pre class="prettyprint"> + * // message needs to be a JavaScript String. + * myObject.postMessage(message) + * + * // To receive the message posted from the app side. event has a "data" property, which is the + * // message string from the app side. + * myObject.onmessage(event) + * + * // To be compatible with DOM EventTarget's addEventListener, it accepts type and listener + * // parameters, where type can be only "message" type and listener can only be a JavaScript + * // function for myObject. An event object will be passed to listener with a "data" property, + * // which is the message string from the app side. + * myObject.addEventListener(type, listener) + * + * // To be compatible with DOM EventTarget's removeEventListener, it accepts type and listener + * // parameters, where type can be only "message" type and listener can only be a JavaScript + * // function for myObject. + * myObject.removeEventListener(type, listener) + * </pre> + * + * <p> + * We start the communication between JavaScript and the app from the JavaScript side. In order + * to send message from the app to JavaScript, it needs to post a message from JavaScript first, + * so the app will have a {@link WebMessageReplyProxy} object to respond. Example: + * <pre class="prettyprint"> + * // Web page (in JavaScript) + * myObject.onmessage = function(event) { + * // prints "Got it!" when we receive the app's response. + * console.log(event.data); + * } + * myObject.postMessage("I'm ready!"); + * + * // App (in Java) + * WebMessageCallback callback = new WebMessageCallback() { + * @Override + * public void onWebMessageReceived(WebMessageReplyProxy proxy, + * WebMessage message) { + * // do something about view, message, sourceOrigin and isMainFrame. + * proxy.postMessage(new WebMessage("Got it!")); + * } + * }; + * tab.registerWebMessageCallback(callback, "myObject", rules); + * </pre> + * + * @param callback The WebMessageCallback to notify of messages. + * @param jsObjectName The name to give the injected JavaScript object. + * @param allowedOrigins The set of allowed origins. + * + * @throws IllegalArgumentException if jsObjectName or allowedOrigins is invalid. + * + * @since 85 + */ + public void registerWebMessageCallback(@NonNull WebMessageCallback callback, + @NonNull String jsObjectName, @NonNull List<String> allowedOrigins) { + ThreadCheck.ensureOnUiThread(); + if (WebLayer.getSupportedMajorVersionInternal() < 85) { + throw new UnsupportedOperationException(); + } + try { + mImpl.registerWebMessageCallback( + jsObjectName, allowedOrigins, new WebMessageCallbackClientImpl(callback)); + } catch (RemoteException e) { + throw new APICallException(e); + } + } + + /** + * Removes the JavaScript object previously registered by way of registerWebMessageCallback. + * This impacts future navigations (not any already loaded navigations). + * + * @param jsObjectName Name of the JavaScript object. + * @since 85 + */ + public void unregisterWebMessageCallback(@NonNull String jsObjectName) { + ThreadCheck.ensureOnUiThread(); + if (WebLayer.getSupportedMajorVersionInternal() < 85) { + throw new UnsupportedOperationException(); + } + try { + mImpl.unregisterWebMessageCallback(jsObjectName); + } catch (RemoteException e) { + throw new APICallException(e); + } + } + + /** + * Returns true if the content displayed in this tab can be translated. + * + * @since 85 + */ + public boolean canTranslate() { + ThreadCheck.ensureOnUiThread(); + if (WebLayer.getSupportedMajorVersionInternal() < 85) { + throw new UnsupportedOperationException(); + } + try { + return mImpl.canTranslate(); + } catch (RemoteException e) { + throw new APICallException(e); + } + } + + /** + * Shows the UI which allows the user to translate the content displayed in this tab. + * + * @since 85 + */ + public void showTranslateUi() { + ThreadCheck.ensureOnUiThread(); + if (WebLayer.getSupportedMajorVersionInternal() < 85) { + throw new UnsupportedOperationException(); + } + try { + mImpl.showTranslateUi(); + } catch (RemoteException e) { + throw new APICallException(e); + } + } + + private static final class WebMessageCallbackClientImpl extends IWebMessageCallbackClient.Stub { + private final WebMessageCallback mCallback; + // Maps from id of IWebMessageReplyProxy to WebMessageReplyProxy. This is done to avoid AIDL + // creating an implementation of IWebMessageReplyProxy every time onPostMessage() is called. + private final Map<Integer, WebMessageReplyProxy> mProxyIdToProxy = + new HashMap<Integer, WebMessageReplyProxy>(); + + private WebMessageCallbackClientImpl(WebMessageCallback callback) { + mCallback = callback; + } + + @Override + public void onNewReplyProxy(IWebMessageReplyProxy proxy, int proxyId, boolean isMainFrame, + String sourceOrigin) { + StrictModeWorkaround.apply(); + assert mProxyIdToProxy.get(proxyId) == null; + mProxyIdToProxy.put( + proxyId, new WebMessageReplyProxy(proxy, isMainFrame, sourceOrigin)); + } + + @Override + public void onPostMessage(int proxyId, String string) { + StrictModeWorkaround.apply(); + assert mProxyIdToProxy.get(proxyId) != null; + mCallback.onWebMessageReceived(mProxyIdToProxy.get(proxyId), new WebMessage(string)); + } + + @Override + public void onReplyProxyDestroyed(int proxyId) { + StrictModeWorkaround.apply(); + assert mProxyIdToProxy.get(proxyId) != null; + mProxyIdToProxy.remove(proxyId); + } + } + private final class TabClientImpl extends ITabClient.Stub { @Override public void visibleUriChanged(String uriString) { @@ -437,6 +726,23 @@ public class Tab { callback.bringTabToFront(); } } + + @Override + public void onBackgroundColorChanged(int color) { + StrictModeWorkaround.apply(); + for (TabCallback callback : mCallbacks) { + callback.onBackgroundColorChanged(color); + } + } + + @Override + public void onScrollNotification( + @ScrollNotificationType int notificationType, float currentScrollRatio) { + StrictModeWorkaround.apply(); + for (TabCallback callback : mCallbacks) { + callback.onScrollNotification(notificationType, currentScrollRatio); + } + } } private static final class ErrorPageCallbackClientImpl extends IErrorPageCallbackClient.Stub { diff --git a/chromium/weblayer/public/java/org/chromium/weblayer/TabCallback.java b/chromium/weblayer/public/java/org/chromium/weblayer/TabCallback.java index 007a7160453..5b1ecf4298c 100644 --- a/chromium/weblayer/public/java/org/chromium/weblayer/TabCallback.java +++ b/chromium/weblayer/public/java/org/chromium/weblayer/TabCallback.java @@ -54,4 +54,28 @@ public abstract class TabCallback { * containing Activity, and the task to be foregrounded. */ public void bringTabToFront() {} + + /** + * Called when then background color of the page changes. The background color typically comes + * from css background-color, but heuristics and blending may be used depending upon the page. + * This is mostly useful for filling in gaps around the web page during resize, but it will + * not necessarily match the full background of the page. + * @param color The new ARGB color of the page background. + * @since 85 + */ + public void onBackgroundColorChanged(int color) {} + + /** + * Notification for scroll of the root of the web page. This is generally sent as a result of + * displaying web page. See ScrollNotificationType for more details. ScrollNotificationType is + * meant to be extensible and new types may be added in the future. Embedder should take care + * to allow unknown values. + * @param notificationType type of notification. See ScrollNotificationType for more details. + * @param currentScrollRatio value in [0, 1] indicating the current scroll ratio. For example + * a web page that is 200 pixels, has a viewport of height 50 pixels + * and a scroll offset of 50 pixels will have a ratio of 0.5. + * @since 85 + */ + public void onScrollNotification( + @ScrollNotificationType int notificationType, float currentScrollRatio) {} } diff --git a/chromium/weblayer/public/java/org/chromium/weblayer/WebLayer.java b/chromium/weblayer/public/java/org/chromium/weblayer/WebLayer.java index 50eab9dcb31..b47b68ec6cf 100644 --- a/chromium/weblayer/public/java/org/chromium/weblayer/WebLayer.java +++ b/chromium/weblayer/public/java/org/chromium/weblayer/WebLayer.java @@ -32,6 +32,7 @@ import org.chromium.weblayer_private.interfaces.IWebLayerClient; import org.chromium.weblayer_private.interfaces.IWebLayerFactory; import org.chromium.weblayer_private.interfaces.ObjectWrapper; import org.chromium.weblayer_private.interfaces.StrictModeWorkaround; +import org.chromium.weblayer_private.interfaces.WebLayerVersionConstants; import java.lang.reflect.Field; import java.lang.reflect.Method; @@ -179,6 +180,14 @@ public class WebLayer { return mImpl; } + static WebLayer getLoadedWebLayer(@NonNull Context appContext) + throws UnsupportedVersionException { + ThreadCheck.ensureOnUiThread(); + appContext = appContext.getApplicationContext(); + checkAvailable(appContext); + return getWebLayerLoader(appContext).getLoadedWebLayer(); + } + /** * Returns the supported version. Using any functions defined in a newer version than * returned by {@link getSupportedMajorVersion} result in throwing an @@ -270,17 +279,20 @@ public class WebLayer { } Class factoryClass = remoteClassLoader.loadClass( "org.chromium.weblayer_private.WebLayerFactoryImpl"); - // NOTE: the 20 comes from the previous scheme of incrementing versioning. It must - // remain at 20 for Chrome version 79. - // TODO(https://crbug.com/1031830): change 20 to -1 when 83 goes to stable. mFactory = IWebLayerFactory.Stub.asInterface( (IBinder) factoryClass .getMethod("create", String.class, int.class, int.class) .invoke(null, WebLayerClientVersionConstants.PRODUCT_VERSION, - WebLayerClientVersionConstants.PRODUCT_MAJOR_VERSION, 20)); + WebLayerClientVersionConstants.PRODUCT_MAJOR_VERSION, -1)); available = mFactory.isClientSupported(); majorVersion = mFactory.getImplementationMajorVersion(); version = mFactory.getImplementationVersion(); + // See comment in WebLayerFactoryImpl.isClientSupported() for details on this. + if (available + && WebLayerClientVersionConstants.PRODUCT_MAJOR_VERSION > majorVersion) { + available = WebLayerClientVersionConstants.PRODUCT_MAJOR_VERSION - majorVersion + <= WebLayerVersionConstants.MAX_SKEW; + } } catch (Exception e) { Log.e(TAG, "Unable to create WebLayerFactory", e); } @@ -360,6 +372,10 @@ public class WebLayer { } } + WebLayer getLoadedWebLayer() { + return mWebLayer; + } + @Nullable private IWebLayer getIWebLayer(@NonNull Context appContext) { if (mIWebLayer != null) return mIWebLayer; @@ -668,5 +684,18 @@ public class WebLayer { // client library because it's referenced in the manifest. return new Intent(WebLayer.getAppContext(), BroadcastReceiver.class); } + + @Override + public Intent createMediaSessionServiceIntent() { + StrictModeWorkaround.apply(); + return new Intent(WebLayer.getAppContext(), MediaSessionService.class); + } + + @Override + public int getMediaSessionNotificationId() { + StrictModeWorkaround.apply(); + // The id is part of the public library to avoid conflicts. + return R.id.weblayer_media_session_notification; + } } } diff --git a/chromium/weblayer/public/java/org/chromium/weblayer/WebMessage.java b/chromium/weblayer/public/java/org/chromium/weblayer/WebMessage.java new file mode 100644 index 00000000000..7fd27bd2bdc --- /dev/null +++ b/chromium/weblayer/public/java/org/chromium/weblayer/WebMessage.java @@ -0,0 +1,34 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package org.chromium.weblayer; + +import androidx.annotation.NonNull; + +/** + * Used when sending and receiving messages to a page. + * + * @since 85 + */ +public class WebMessage { + private final String mContents; + + /** + * Creates a message with the specified contents. + * + * @param message Contents of the message. + */ + public WebMessage(@NonNull String message) { + mContents = message; + } + + /** + * Returns the contents of the message. + * + * @return The contents of the message. + */ + public @NonNull String getContents() { + return mContents; + } +} diff --git a/chromium/weblayer/public/java/org/chromium/weblayer/WebMessageCallback.java b/chromium/weblayer/public/java/org/chromium/weblayer/WebMessageCallback.java new file mode 100644 index 00000000000..bf35f5a4b5d --- /dev/null +++ b/chromium/weblayer/public/java/org/chromium/weblayer/WebMessageCallback.java @@ -0,0 +1,24 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package org.chromium.weblayer; + +import androidx.annotation.NonNull; + +/** + * Receives messages from a JavaScript object that was created by calling {@link + * Tab#registerWebMessageCallback(). + * + * @since 85 + */ +public abstract class WebMessageCallback { + /** + * Called when a message is received from the page. + * + * @param replyProxy An object that may be used to post a message back to the page. + * @param message The message from the page. + */ + public void onWebMessageReceived( + @NonNull WebMessageReplyProxy replyProxy, @NonNull WebMessage message) {} +} diff --git a/chromium/weblayer/public/java/org/chromium/weblayer/WebMessageReplyProxy.java b/chromium/weblayer/public/java/org/chromium/weblayer/WebMessageReplyProxy.java new file mode 100644 index 00000000000..de9df58d879 --- /dev/null +++ b/chromium/weblayer/public/java/org/chromium/weblayer/WebMessageReplyProxy.java @@ -0,0 +1,70 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package org.chromium.weblayer; + +import android.os.RemoteException; + +import androidx.annotation.NonNull; + +import org.chromium.weblayer_private.interfaces.APICallException; +import org.chromium.weblayer_private.interfaces.IWebMessageReplyProxy; + +/** + * Used to post a message to a page. WebMessageReplyProxy is created when a page posts a message to + * the JavaScript object that was created by way of {@link Tab#registerWebMessageCallback}. + * + * @since 85 + */ +public class WebMessageReplyProxy { + private final IWebMessageReplyProxy mIReplyProxy; + private final boolean mIsMainFrame; + private final String mSourceOrigin; + + // Constructor for test mocking. + protected WebMessageReplyProxy() { + this(null, false, ""); + } + + WebMessageReplyProxy( + IWebMessageReplyProxy iReplyProxy, boolean isMainFrame, String sourceOrigin) { + mIReplyProxy = iReplyProxy; + mIsMainFrame = isMainFrame; + mSourceOrigin = sourceOrigin; + } + + /** + * Returns true if the connection is to the main frame. + * + * @return True if the connection is to the main frame. + */ + public boolean isMainFrame() { + ThreadCheck.ensureOnUiThread(); + return mIsMainFrame; + } + + /** + * Returns the origin of the page. + * + * @return the origin of the page. + */ + public @NonNull String getSourceOrigin() { + ThreadCheck.ensureOnUiThread(); + return mSourceOrigin; + } + + /** + * Posts a message to the page. + * + * @param message The message to send to the page. + */ + public void postMessage(@NonNull WebMessage message) { + ThreadCheck.ensureOnUiThread(); + try { + mIReplyProxy.postMessage(message.getContents()); + } catch (RemoteException e) { + throw new APICallException(e); + } + } +} diff --git a/chromium/weblayer/public/java/res/values-night/colors.xml b/chromium/weblayer/public/java/res/values-night/colors.xml new file mode 100644 index 00000000000..3846e456aee --- /dev/null +++ b/chromium/weblayer/public/java/res/values-night/colors.xml @@ -0,0 +1,10 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- 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. --> + +<resources> + <color name="weblayer_default_bg_color">@color/weblayer_default_bg_color_dark</color> + <color name="weblayer_bottom_system_nav_color">@android:color/black</color> + <color name="weblayer_bottom_system_nav_divider_color">@android:color/black</color> +</resources> diff --git a/chromium/weblayer/public/java/res/values-night/values.xml b/chromium/weblayer/public/java/res/values-night/values.xml new file mode 100644 index 00000000000..5009ad51a2a --- /dev/null +++ b/chromium/weblayer/public/java/res/values-night/values.xml @@ -0,0 +1,10 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- 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. --> + +<resources xmlns:tools="http://schemas.android.com/tools"> + <!-- Status and navigation bar icon styling. --> + <bool name="weblayer_window_light_status_bar">false</bool> + <bool name="weblayer_window_light_navigation_bar">false</bool> +</resources> diff --git a/chromium/weblayer/public/java/res/values-v27/styles.xml b/chromium/weblayer/public/java/res/values-v27/styles.xml new file mode 100644 index 00000000000..0ffe78b5f62 --- /dev/null +++ b/chromium/weblayer/public/java/res/values-v27/styles.xml @@ -0,0 +1,12 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- 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. --> + +<resources> + <style name="Theme.WebLayer.SiteSettings" parent="Base.Theme.WebLayer.SiteSettings"> + <item name="android:navigationBarColor">@color/weblayer_bottom_system_nav_color</item> + <item name="android:navigationBarDividerColor">@color/weblayer_bottom_system_nav_divider_color</item> + <item name="android:windowLightNavigationBar">@bool/weblayer_window_light_navigation_bar</item> + </style> +</resources> diff --git a/chromium/weblayer/public/java/res/values-v28/styles.xml b/chromium/weblayer/public/java/res/values-v28/styles.xml new file mode 100644 index 00000000000..0a21de0a4eb --- /dev/null +++ b/chromium/weblayer/public/java/res/values-v28/styles.xml @@ -0,0 +1,18 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- 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. --> + +<resources> + <style name="Theme.WebLayer.SiteSettings" parent="Base.Theme.WebLayer.SiteSettings"> + <item name="android:navigationBarColor">@color/weblayer_bottom_system_nav_color</item> + <item name="android:navigationBarDividerColor">@color/weblayer_bottom_system_nav_divider_color</item> + <item name="android:windowLightNavigationBar">@bool/weblayer_window_light_navigation_bar</item> + + <!-- The windowLightStatusBar attribute was added in API 23, but we + avoid using it via XML prior to 28 due to: https://crbug.com/884144 + and https://crbug.com/1014844 --> + <item name="android:statusBarColor">@color/weblayer_default_bg_color</item> + <item name="android:windowLightStatusBar">@bool/weblayer_window_light_status_bar</item> + </style> +</resources> diff --git a/chromium/weblayer/public/java/res/values/colors.xml b/chromium/weblayer/public/java/res/values/colors.xml new file mode 100644 index 00000000000..bb86aec4897 --- /dev/null +++ b/chromium/weblayer/public/java/res/values/colors.xml @@ -0,0 +1,16 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- 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. --> + +<resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="UnusedResources"> + <color name="weblayer_default_bg_color">@color/weblayer_default_bg_color_light</color> + <color name="weblayer_default_bg_color_light">@android:color/white</color> + <color name="weblayer_default_bg_color_dark">@color/weblayer_modern_grey_900</color> + + <color name="weblayer_bottom_system_nav_color">@android:color/white</color> + <color name="weblayer_bottom_system_nav_divider_color">@color/weblayer_black_alpha_12</color> + + <color name="weblayer_black_alpha_12">#1F000000</color> + <color name="weblayer_modern_grey_900">#202124</color> +</resources> diff --git a/chromium/weblayer/public/java/res/values/ids.xml b/chromium/weblayer/public/java/res/values/ids.xml new file mode 100644 index 00000000000..300c7e62a50 --- /dev/null +++ b/chromium/weblayer/public/java/res/values/ids.xml @@ -0,0 +1,8 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- 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. --> + +<resources> + <item type="id" name="weblayer_media_session_notification" /> +</resources> diff --git a/chromium/weblayer/public/java/res/values/styles.xml b/chromium/weblayer/public/java/res/values/styles.xml index 478e0f2bbcd..143124cc560 100644 --- a/chromium/weblayer/public/java/res/values/styles.xml +++ b/chromium/weblayer/public/java/res/values/styles.xml @@ -4,21 +4,18 @@ found in the LICENSE file. --> <resources xmlns:tools="http://schemas.android.com/tools"> - <style name="Theme.WebLayer" parent="Theme.BrowserUI"> + <style name="Base.Theme.WebLayer.SiteSettings" parent="Theme.AppCompat.DayNight"> <!-- Window Properties --> - <item name="android:windowBackground">@color/default_bg_color</item> + <item name="android:windowBackground">@color/weblayer_default_bg_color</item> <!-- Action bar color --> - <item name="colorPrimary">@color/default_bg_color</item> + <item name="colorPrimary">@color/weblayer_default_bg_color</item> <!-- Status bar color --> + <item name="colorPrimaryDark">@android:color/black</item> <item name="android:statusBarColor" tools:targetApi="21">@android:color/black</item> <item name="android:windowLightStatusBar" tools:targetApi="23">false</item> - <item name="colorPrimaryDark">@android:color/black</item> </style> - <style name="Theme.WebLayer.SiteSettings"> - <item name="windowActionBar">true</item> - <item name="windowNoTitle">false</item> - </style> + <style name="Theme.WebLayer.SiteSettings" parent="Base.Theme.WebLayer.SiteSettings" /> </resources> diff --git a/chromium/weblayer/public/java/res/values/values.xml b/chromium/weblayer/public/java/res/values/values.xml new file mode 100644 index 00000000000..a04c1d9a0ea --- /dev/null +++ b/chromium/weblayer/public/java/res/values/values.xml @@ -0,0 +1,10 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- 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. --> + +<resources xmlns:tools="http://schemas.android.com/tools"> + <!-- Status and navigation bar icon styling. --> + <bool name="weblayer_window_light_status_bar">true</bool> + <bool name="weblayer_window_light_navigation_bar">true</bool> +</resources> diff --git a/chromium/weblayer/public/javatests/org/chromium/weblayer/ObserverListTest.java b/chromium/weblayer/public/javatests/org/chromium/weblayer/ObserverListTest.java index 35343813c9d..605037dd909 100644 --- a/chromium/weblayer/public/javatests/org/chromium/weblayer/ObserverListTest.java +++ b/chromium/weblayer/public/javatests/org/chromium/weblayer/ObserverListTest.java @@ -4,7 +4,7 @@ package org.chromium.weblayer; -import android.support.test.filters.SmallTest; +import androidx.test.filters.SmallTest; import org.junit.Assert; import org.junit.Test; diff --git a/chromium/weblayer/public/javatests/org/chromium/weblayer/WebViewCompatibilityHelperTest.java b/chromium/weblayer/public/javatests/org/chromium/weblayer/WebViewCompatibilityHelperTest.java index ceca8dc15a9..998f0907ec4 100644 --- a/chromium/weblayer/public/javatests/org/chromium/weblayer/WebViewCompatibilityHelperTest.java +++ b/chromium/weblayer/public/javatests/org/chromium/weblayer/WebViewCompatibilityHelperTest.java @@ -7,9 +7,10 @@ package org.chromium.weblayer; import android.content.Context; import android.os.Build; import android.support.test.InstrumentationRegistry; -import android.support.test.filters.SmallTest; import android.util.Pair; +import androidx.test.filters.SmallTest; + import org.junit.Assert; import org.junit.Test; import org.junit.runner.RunWith; diff --git a/chromium/weblayer/public/javatestutil/BUILD.gn b/chromium/weblayer/public/javatestutil/BUILD.gn index 48b69a71639..5fd626da668 100644 --- a/chromium/weblayer/public/javatestutil/BUILD.gn +++ b/chromium/weblayer/public/javatestutil/BUILD.gn @@ -12,6 +12,7 @@ android_library("test_java") { deps = [ "//base:base_java_test_support", "//third_party/junit:junit", + "//weblayer/browser/java:interfaces_java", "//weblayer/browser/java:test_java", "//weblayer/public/java", ] diff --git a/chromium/weblayer/public/javatestutil/org/chromium/weblayer/TestWebLayer.java b/chromium/weblayer/public/javatestutil/org/chromium/weblayer/TestWebLayer.java index 370103f4fab..1623a9e7906 100644 --- a/chromium/weblayer/public/javatestutil/org/chromium/weblayer/TestWebLayer.java +++ b/chromium/weblayer/public/javatestutil/org/chromium/weblayer/TestWebLayer.java @@ -9,10 +9,12 @@ import android.content.pm.PackageManager; import android.os.IBinder; import android.os.RemoteException; import android.util.AndroidRuntimeException; +import android.view.View; import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import org.chromium.weblayer_private.interfaces.ObjectWrapper; import org.chromium.weblayer_private.test_interfaces.ITestWebLayer; /** @@ -75,4 +77,41 @@ public final class TestWebLayer { public void setSystemLocationSettingEnabled(boolean enabled) throws RemoteException { mITestWebLayer.setSystemLocationSettingEnabled(enabled); } + + // Runs |runnable| when cc::RenderFrameMetadata's |top_controls_height| and + // |bottom_controls_height| matches the supplied values. |runnable| may be run synchronously. + public void waitForBrowserControlsMetadataState(Tab tab, int top, int bottom, Runnable runnable) + throws RemoteException { + mITestWebLayer.waitForBrowserControlsMetadataState( + tab.getITab(), top, bottom, ObjectWrapper.wrap(runnable)); + } + + public void setAccessibilityEnabled(boolean enabled) throws RemoteException { + mITestWebLayer.setAccessibilityEnabled(enabled); + } + + public boolean canBrowserControlsScroll(Tab tab) throws RemoteException { + return mITestWebLayer.canBrowserControlsScroll(tab.getITab()); + } + + public void addInfoBar(Tab tab, Runnable runnable) throws RemoteException { + mITestWebLayer.addInfoBar(tab.getITab(), ObjectWrapper.wrap(runnable)); + } + + public View getInfoBarContainerView(Tab tab) throws RemoteException { + return (View) ObjectWrapper.unwrap( + mITestWebLayer.getInfoBarContainerView(tab.getITab()), View.class); + } + + public void setIgnoreMissingKeyForTranslateManager(boolean ignore) throws RemoteException { + mITestWebLayer.setIgnoreMissingKeyForTranslateManager(ignore); + } + + public void forceNetworkConnectivityState(boolean networkAvailable) throws RemoteException { + mITestWebLayer.forceNetworkConnectivityState(networkAvailable); + } + + public boolean canInfoBarContainerScroll(Tab tab) throws RemoteException { + return mITestWebLayer.canInfoBarContainerScroll(tab.getITab()); + } } diff --git a/chromium/weblayer/public/js_communication/web_message.cc b/chromium/weblayer/public/js_communication/web_message.cc new file mode 100644 index 00000000000..ebdcd6632d0 --- /dev/null +++ b/chromium/weblayer/public/js_communication/web_message.cc @@ -0,0 +1,13 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "weblayer/public/js_communication/web_message.h" + +namespace weblayer { + +WebMessage::WebMessage() = default; + +WebMessage::~WebMessage() = default; + +} // namespace weblayer diff --git a/chromium/weblayer/public/js_communication/web_message.h b/chromium/weblayer/public/js_communication/web_message.h new file mode 100644 index 00000000000..169cbe1da0c --- /dev/null +++ b/chromium/weblayer/public/js_communication/web_message.h @@ -0,0 +1,21 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef WEBLAYER_PUBLIC_JS_COMMUNICATION_WEB_MESSAGE_H_ +#define WEBLAYER_PUBLIC_JS_COMMUNICATION_WEB_MESSAGE_H_ + +#include "base/strings/string16.h" + +namespace weblayer { + +struct WebMessage { + WebMessage(); + ~WebMessage(); + + base::string16 message; +}; + +} // namespace weblayer + +#endif // WEBLAYER_PUBLIC_JS_COMMUNICATION_WEB_MESSAGE_H_ diff --git a/chromium/weblayer/public/js_communication/web_message_host.h b/chromium/weblayer/public/js_communication/web_message_host.h new file mode 100644 index 00000000000..b81ab149abd --- /dev/null +++ b/chromium/weblayer/public/js_communication/web_message_host.h @@ -0,0 +1,26 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef WEBLAYER_PUBLIC_JS_COMMUNICATION_WEB_MESSAGE_HOST_H_ +#define WEBLAYER_PUBLIC_JS_COMMUNICATION_WEB_MESSAGE_HOST_H_ + +#include <memory> + +namespace weblayer { + +struct WebMessage; + +// Represents the browser side of a WebMessage channel. See +// WebMessageHostFactory for details. +class WebMessageHost { + public: + virtual ~WebMessageHost() = default; + + // Called when the page sends a message to the browser side. + virtual void OnPostMessage(std::unique_ptr<WebMessage> message) = 0; +}; + +} // namespace weblayer + +#endif // WEBLAYER_PUBLIC_JS_COMMUNICATION_WEB_MESSAGE_HOST_H_ diff --git a/chromium/weblayer/public/js_communication/web_message_host_factory.h b/chromium/weblayer/public/js_communication/web_message_host_factory.h new file mode 100644 index 00000000000..2f754e15653 --- /dev/null +++ b/chromium/weblayer/public/js_communication/web_message_host_factory.h @@ -0,0 +1,35 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef WEBLAYER_PUBLIC_JS_COMMUNICATION_WEB_MESSAGE_HOST_FACTORY_H_ +#define WEBLAYER_PUBLIC_JS_COMMUNICATION_WEB_MESSAGE_HOST_FACTORY_H_ + +#include <memory> +#include <string> + +namespace weblayer { + +class WebMessageHost; +class WebMessageReplyProxy; + +// Creates a WebMessageHost in response to a page interacting with the object +// registered by way of Tab::AddWebMessageHostFactory(). A WebMessageHost is +// created for every page that matches the parameters of +// AddWebMessageHostFactory(). +class WebMessageHostFactory { + public: + virtual ~WebMessageHostFactory() = default; + + // The returned object is destroyed when the corresponding renderer has + // been destroyed. |proxy| may be used to send messages to the page and is + // valid for the life of the WebMessageHost. + virtual std::unique_ptr<WebMessageHost> CreateHost( + const std::string& origin_string, + bool is_main_frame, + WebMessageReplyProxy* proxy) = 0; +}; + +} // namespace weblayer + +#endif // WEBLAYER_PUBLIC_JS_COMMUNICATION_WEB_MESSAGE_HOST_FACTORY_H_ diff --git a/chromium/weblayer/public/js_communication/web_message_reply_proxy.h b/chromium/weblayer/public/js_communication/web_message_reply_proxy.h new file mode 100644 index 00000000000..e7147096494 --- /dev/null +++ b/chromium/weblayer/public/js_communication/web_message_reply_proxy.h @@ -0,0 +1,25 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef WEBLAYER_PUBLIC_JS_COMMUNICATION_WEB_MESSAGE_REPLY_PROXY_H_ +#define WEBLAYER_PUBLIC_JS_COMMUNICATION_WEB_MESSAGE_REPLY_PROXY_H_ + +#include <memory> + +namespace weblayer { + +struct WebMessage; + +// Used to send messages to the page. +class WebMessageReplyProxy { + public: + virtual void PostMessage(std::unique_ptr<WebMessage>) = 0; + + protected: + virtual ~WebMessageReplyProxy() = default; +}; + +} // namespace weblayer + +#endif // WEBLAYER_PUBLIC_JS_COMMUNICATION_WEB_MESSAGE_REPLY_PROXY_H_ diff --git a/chromium/weblayer/public/navigation.h b/chromium/weblayer/public/navigation.h index 725962e4908..af69c1bbfe8 100644 --- a/chromium/weblayer/public/navigation.h +++ b/chromium/weblayer/public/navigation.h @@ -94,6 +94,11 @@ class Navigation { // and redirect. When called during start, the header applies to both the // start and redirect. |name| must be rfc 2616 compliant and |value| must // not contain '\0', '\n' or '\r'. + // + // This function may be used to set the referer. If the referer is set in + // navigation start, it is reset during the redirect. In other words, if you + // need to set a referer that applies to redirects, then this must be called + // from NavigationRedirected(). virtual void SetRequestHeader(const std::string& name, const std::string& value) = 0; diff --git a/chromium/weblayer/public/navigation_controller.h b/chromium/weblayer/public/navigation_controller.h index 4b042c19353..c584c5d1432 100644 --- a/chromium/weblayer/public/navigation_controller.h +++ b/chromium/weblayer/public/navigation_controller.h @@ -60,6 +60,11 @@ class NavigationController { // Gets the page title of the given entry in the back/forward list, or an // empty string if there is no navigation entry at that index. virtual std::string GetNavigationEntryTitle(int index) = 0; + + // Returns whether this entry will be skipped on a call to GoBack() or + // GoForward(). This will be true for navigations that were done without a + // user gesture, including both client side redirects and history.pushState. + virtual bool IsNavigationEntrySkippable(int index) = 0; }; } // namespace weblayer diff --git a/chromium/weblayer/public/navigation_observer.h b/chromium/weblayer/public/navigation_observer.h index 6b0d2dda9eb..86567cb5255 100644 --- a/chromium/weblayer/public/navigation_observer.h +++ b/chromium/weblayer/public/navigation_observer.h @@ -5,6 +5,8 @@ #ifndef WEBLAYER_PUBLIC_NAVIGATION_OBSERVER_H_ #define WEBLAYER_PUBLIC_NAVIGATION_OBSERVER_H_ +class GURL; + namespace weblayer { class Navigation; @@ -95,6 +97,11 @@ class NavigationObserver { // This is fired after each navigation has completed to indicate that the // first paint after a non-empty layout has finished. virtual void OnFirstContentfulPaint() {} + + // Called after each navigation to indicate that the old page is no longer + // being rendered. Note this is not ordered with respect to + // OnFirstContentfulPaint. + virtual void OnOldPageNoLongerRendered(const GURL& url) {} }; } // namespace weblayer diff --git a/chromium/weblayer/public/new_tab_delegate.h b/chromium/weblayer/public/new_tab_delegate.h index 2d131444df1..27291bf1b99 100644 --- a/chromium/weblayer/public/new_tab_delegate.h +++ b/chromium/weblayer/public/new_tab_delegate.h @@ -33,7 +33,9 @@ enum class NewTabType { // in web terms, a new popup/window (and random other things). class NewTabDelegate { public: - virtual void OnNewTab(std::unique_ptr<Tab> new_tab, NewTabType type) = 0; + // Called when a new tab is created by the browser. |new_tab| is owned by the + // browser. + virtual void OnNewTab(Tab* new_tab, NewTabType type) = 0; // The page has requested a tab that was created by way of OnNewTab() to be // closed. This is sent to the NewTabDelegate set on the page created by way diff --git a/chromium/weblayer/public/profile.h b/chromium/weblayer/public/profile.h index f878ed679c9..48f49c52758 100644 --- a/chromium/weblayer/public/profile.h +++ b/chromium/weblayer/public/profile.h @@ -5,9 +5,13 @@ #ifndef WEBLAYER_PUBLIC_PROFILE_H_ #define WEBLAYER_PUBLIC_PROFILE_H_ -#include <algorithm> +#include <memory> #include <string> +#include "base/callback_forward.h" +#include "base/containers/flat_set.h" +#include "base/time/time.h" + namespace base { class FilePath; } @@ -23,8 +27,12 @@ enum class BrowsingDataType { CACHE = 1, }; +// Used for setting/getting profile related settings. enum class SettingType { BASIC_SAFE_BROWSING_ENABLED = 0, + UKM_ENABLED = 1, + EXTENDED_REPORTING_SAFE_BROWSING_ENABLED = 2, + REAL_TIME_SAFE_BROWSING_ENABLED = 3, }; class Profile { @@ -61,11 +69,35 @@ class Profile { // Gets the cookie manager for this profile. virtual CookieManager* GetCookieManager() = 0; + // Asynchronously fetches the set of known Browser persistence-ids. See + // Browser::PersistenceInfo for more details on persistence-ids. + virtual void GetBrowserPersistenceIds( + base::OnceCallback<void(base::flat_set<std::string>)> callback) = 0; + + // Asynchronously removes the storage associated with the set of + // Browser persistence-ids. This ignores ids actively in use. |done_callback| + // is run with the result of the operation (on the main thread). A value of + // true means all files were removed. A value of false indicates at least one + // of the files could not be removed. + virtual void RemoveBrowserPersistenceStorage( + base::OnceCallback<void(bool)> done_callback, + base::flat_set<std::string> ids) = 0; + // Set the boolean value of the given setting type. virtual void SetBooleanSetting(SettingType type, bool value) = 0; // Get the boolean value of the given setting type. virtual bool GetBooleanSetting(SettingType type) = 0; + + // For cross-origin navigations, the implementation may leverage a separate OS + // process for stronger isolation. If an embedder knows that a cross-origin + // navigation is likely starting soon, they can call this method as a hint to + // the implementation to start a fresh OS process. A subsequent navigation may + // use this preinitialized process, improving performance. It is safe to call + // this multiple times or when it is not certain that the spare renderer will + // be used, although calling this too eagerly may reduce performance as + // unnecessary processes are created. + virtual void PrepareForPossibleCrossOriginNavigation() = 0; }; } // namespace weblayer diff --git a/chromium/weblayer/public/tab.h b/chromium/weblayer/public/tab.h index eeec5ff218a..ccf25ff7fd6 100644 --- a/chromium/weblayer/public/tab.h +++ b/chromium/weblayer/public/tab.h @@ -6,7 +6,9 @@ #define WEBLAYER_PUBLIC_TAB_H_ #include <algorithm> +#include <map> #include <string> +#include <vector> #include "base/callback_forward.h" #include "base/strings/string16.h" @@ -27,19 +29,13 @@ class ErrorPageDelegate; class FullscreenDelegate; class NavigationController; class NewTabDelegate; -class Profile; class TabObserver; +class WebMessageHostFactory; // Represents a tab that is navigable. class Tab { public: - static std::unique_ptr<Tab> Create(Profile* profile); - -#if defined(OS_ANDROID) - static Tab* GetLastTabForTesting(); -#endif - - virtual ~Tab() {} + virtual ~Tab() = default; // Sets the ErrorPageDelegate. If none is set, a default action will be taken // for any given interaction with an error page. @@ -75,6 +71,40 @@ class Tab { // Returns the tab's guid. virtual const std::string& GetGuid() = 0; + // Allows the embedder to get and set arbitrary data on the tab. This will be + // saved and restored with the browser, so it is important to keep this data + // as small as possible. + virtual void SetData(const std::map<std::string, std::string>& data) = 0; + virtual const std::map<std::string, std::string>& GetData() = 0; + + // Adds a new WebMessageHostFactory. For any urls that match + // |allowed_origin_rules| a JS object is registered using the name + // |js_object_name| (in the global namespace). Script may use the object to + // send and receive messages and is available at page load time. + // + // The page is responsible for initiating the connection. That is, + // WebMessageHostFactory::CreateHost() is called once the page posts a + // message to the JS object. + // + // |allowed_origin_rules| is a set of rules used to determine which pages + // this applies to. '*' may be used to match anything. If not '*' the format + // is 'scheme://host:port': + // . scheme: The scheme, which can not be empty or contain '*'. + // . host: The host to match against. Can not contain '/' and may start with + // '*.' to match against a specific subdomain. + // . port (optional): matches a specific port. + // + // Returns an empty string on success. On failure, the return string gives + // an error message. + virtual base::string16 AddWebMessageHostFactory( + std::unique_ptr<WebMessageHostFactory> factory, + const base::string16& js_object_name, + const std::vector<std::string>& allowed_origin_rules) = 0; + + // Removes the WebMessageHostFactory registered under |js_object_name|. + virtual void RemoveWebMessageHostFactory( + const base::string16& js_object_name) = 0; + #if !defined(OS_ANDROID) // TODO: this isn't a stable API, so use it now for expediency in the C++ API, // but if we ever want to have backward or forward compatibility in C++ this |