diff --git a/DEPS b/DEPS index adaabf6..5023cef 100644 --- a/DEPS +++ b/DEPS
@@ -40,11 +40,11 @@ # Three lines of non-changing comments so that # the commit queue can handle CLs rolling Skia # and whatever else without interference from each other. - 'skia_revision': '114a3c0b2b26c84b9d0907a99fd8ab7938631246', + 'skia_revision': 'b5e4842543318e1eac827433855c9a37cdb7f26a', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling V8 # and whatever else without interference from each other. - 'v8_revision': '20ee3efa8a86d2e9ceb835861d2251939443c7c7', + 'v8_revision': '02777351e7ebb36db9477e225ca6e7689cf99a9e', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling swarming_client # and whatever else without interference from each other. @@ -96,7 +96,7 @@ # Three lines of non-changing comments so that # the commit queue can handle CLs rolling catapult # and whatever else without interference from each other. - 'catapult_revision': '32a3f0b8f49bb6a3e5c7f158ff7aea5584843294', + 'catapult_revision': 'deb4a2aa8f11cb520e9b8bc7413296d899a3af47', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling libFuzzer # and whatever else without interference from each other. @@ -265,7 +265,7 @@ Var('chromium_git') + '/chromium/deps/mesa.git' + '@' + 'ef811c6bd4de74e13e7035ca882cc77f85793fef', 'src/third_party/ced/src': - Var('chromium_git') + '/external/github.com/google/compact_enc_det.git' + '@' + 'e21eb6aed10b9f6e2727f136c52420033214d458', + Var('chromium_git') + '/external/github.com/google/compact_enc_det.git' + '@' + '910cca22d881b02cbc8950fa02ccbcdcfb782456', 'src/third_party/swiftshader': Var('swiftshader_git') + '/SwiftShader.git' + '@' + Var('swiftshader_revision'),
diff --git a/WATCHLISTS b/WATCHLISTS index 72076941..f036247 100644 --- a/WATCHLISTS +++ b/WATCHLISTS
@@ -1867,7 +1867,8 @@ 'blink_vibration': ['mlamouri+watch-blink@chromium.org'], 'blink_viewport_interaction': ['kenneth.christiansen@gmail.com'], 'blink_w3ctests': ['blink-reviews-w3ctests@chromium.org'], - 'blink_web': ['kinuko+watch@chromium.org'], + 'blink_web': ['kinuko+watch@chromium.org', + 'platform-architecture-syd+reviews-web@chromium.org'], 'blink_webcomponents': ['dglazkov+blink@chromium.org', 'webcomponents-bugzilla@chromium.org'], 'blink_webp': ['jzern@chromium.org',
diff --git a/base/task_scheduler/environment_config.h b/base/task_scheduler/environment_config.h index 3a86453..54f2ff3 100644 --- a/base/task_scheduler/environment_config.h +++ b/base/task_scheduler/environment_config.h
@@ -7,6 +7,7 @@ #include <stddef.h> +#include "base/base_export.h" #include "base/task_scheduler/task_traits.h" #include "base/threading/thread.h"
diff --git a/base/task_scheduler/scheduler_single_thread_task_runner_manager.cc b/base/task_scheduler/scheduler_single_thread_task_runner_manager.cc index 3f45500..a445a165 100644 --- a/base/task_scheduler/scheduler_single_thread_task_runner_manager.cc +++ b/base/task_scheduler/scheduler_single_thread_task_runner_manager.cc
@@ -330,19 +330,9 @@ SchedulerSingleThreadTaskRunnerManager::SchedulerSingleThreadTaskRunnerManager( TaskTracker* task_tracker, DelayedTaskManager* delayed_task_manager) - : task_tracker_(task_tracker), - delayed_task_manager_(delayed_task_manager), - shared_scheduler_workers_ {} -#if defined(OS_WIN) - , - shared_com_scheduler_workers_ {} -#endif // defined(OS_WIN) -{ + : task_tracker_(task_tracker), delayed_task_manager_(delayed_task_manager) { DCHECK(task_tracker_); DCHECK(delayed_task_manager_); - static_assert( - arraysize(shared_scheduler_workers_) == ENVIRONMENT_COUNT, - "The size of |shared_scheduler_workers_| must match ENVIRONMENT_COUNT"); #if defined(OS_WIN) static_assert(arraysize(shared_com_scheduler_workers_) == arraysize(shared_scheduler_workers_), @@ -427,7 +417,8 @@ DCHECK(thread_mode != SingleThreadTaskRunnerThreadMode::SHARED || !traits.with_base_sync_primitives()) << "Using WithBaseSyncPrimitives() on a shared SingleThreadTaskRunner " - "may cause deadlocks. Either reevaluate your usage pattern or use " + "may cause deadlocks. Either reevaluate your usage (e.g. use " + "SequencedTaskRunner) or use " "SingleThreadTaskRunnerThreadMode::DEDICATED."; // To simplify the code, |dedicated_worker| is a local only variable that // allows the code to treat both the DEDICATED and SHARED cases similarly for @@ -460,7 +451,8 @@ if (new_worker && started) worker->Start(); - return new SchedulerSingleThreadTaskRunner(this, traits, worker, thread_mode); + return MakeRefCounted<SchedulerSingleThreadTaskRunner>(this, traits, worker, + thread_mode); } void SchedulerSingleThreadTaskRunnerManager::JoinForTesting() { @@ -569,8 +561,10 @@ AutoSchedulerLock auto_lock(lock_); for (size_t i = 0; i < arraysize(shared_scheduler_workers_); ++i) { local_shared_scheduler_workers[i] = shared_scheduler_workers_[i]; + shared_scheduler_workers_[i] = nullptr; #if defined(OS_WIN) local_shared_com_scheduler_workers[i] = shared_com_scheduler_workers_[i]; + shared_com_scheduler_workers_[i] = nullptr; #endif } }
diff --git a/base/task_scheduler/scheduler_single_thread_task_runner_manager.h b/base/task_scheduler/scheduler_single_thread_task_runner_manager.h index c6cd8ed..1b4e51a 100644 --- a/base/task_scheduler/scheduler_single_thread_task_runner_manager.h +++ b/base/task_scheduler/scheduler_single_thread_task_runner_manager.h
@@ -40,9 +40,10 @@ // Manages a pool of threads which are each associated with one or more // SingleThreadTaskRunners. // -// SingleThreadTaskRunners using SingleThreadTaskRunnerThreadMode::SHARED, are +// SingleThreadTaskRunners using SingleThreadTaskRunnerThreadMode::SHARED are // backed by shared SchedulerWorkers for each COM+task environment combination. -// These workers are only reclaimed during JoinForTesting(). +// These workers are lazily instantiated and then only reclaimed during +// JoinForTesting() // // No threads are created (and hence no tasks can run) before Start() is called. // @@ -61,9 +62,7 @@ // Creates a SingleThreadTaskRunner which runs tasks with |traits| on a thread // named "TaskSchedulerSingleThread[Shared]" + |name| + // kEnvironmentParams[GetEnvironmentIndexForTraits(traits)].name_suffix + - // index. "Shared" will be in the thread name when |thread_mode| is SHARED. If - // |thread_mode| is DEDICATED, a thread will be dedicated to the returned task - // runner, otherwise it could be shared with other SingleThreadTaskRunners. + // index. scoped_refptr<SingleThreadTaskRunner> CreateSingleThreadTaskRunnerWithTraits( const std::string& name, const TaskTraits& traits, @@ -73,9 +72,7 @@ // Creates a SingleThreadTaskRunner which runs tasks with |traits| on a COM // STA thread named "TaskSchedulerSingleThreadCOMSTA[Shared]" + |name| + // kEnvironmentParams[GetEnvironmentIndexForTraits(traits)].name_suffix + - // index. "Shared" will be in the thread name when |thread_mode| is SHARED. If - // |thread_mode| is DEDICATED, a thread will be dedicated to the returned task - // runner, otherwise it could be shared with other SingleThreadTaskRunners. + // index. scoped_refptr<SingleThreadTaskRunner> CreateCOMSTATaskRunnerWithTraits( const std::string& name, const TaskTraits& traits, @@ -122,10 +119,10 @@ std::vector<scoped_refptr<SchedulerWorker>> workers_; int next_worker_id_ = 0; - SchedulerWorker* shared_scheduler_workers_[4]; + SchedulerWorker* shared_scheduler_workers_[ENVIRONMENT_COUNT] = {}; #if defined(OS_WIN) - SchedulerWorker* shared_com_scheduler_workers_[4]; + SchedulerWorker* shared_com_scheduler_workers_[ENVIRONMENT_COUNT] = {}; #endif // defined(OS_WIN) // Set to true when Start() is called.
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java index 82b7ca0..faea5d9 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java
@@ -51,6 +51,7 @@ import org.chromium.chrome.browser.IntentHandler.TabOpenType; import org.chromium.chrome.browser.appmenu.AppMenuPropertiesDelegate; import org.chromium.chrome.browser.bookmarks.BookmarkUtils; +import org.chromium.chrome.browser.browseractions.BrowserActionsContextMenuItemDelegate; import org.chromium.chrome.browser.compositor.CompositorViewHolder; import org.chromium.chrome.browser.compositor.bottombar.OverlayPanel.StateChangeReason; import org.chromium.chrome.browser.compositor.layouts.Layout; @@ -616,10 +617,9 @@ @Override public void onStartWithNative() { super.onStartWithNative(); - // If we don't have a current tab, show the overview mode. - if (getActivityTab() == null && !mLayoutManager.overviewVisible()) { - mLayoutManager.showOverview(false); - } + + setInitialOverviewState(); + BrowserActionsContextMenuItemDelegate.cancelBrowserActionsNotification(); resetSavedInstanceState(); } @@ -673,6 +673,18 @@ } } + private void setInitialOverviewState() { + boolean isOverviewVisible = mLayoutManager.overviewVisible(); + if (getActivityTab() == null && !isOverviewVisible) { + toggleOverview(); + } + + if (BrowserActionsContextMenuItemDelegate.toggleOverviewByBrowserActions( + getIntent(), isOverviewVisible)) { + toggleOverview(); + } + } + private void initializeUI() { try { TraceEvent.begin("ChromeTabbedActivity.initializeUI"); @@ -1823,8 +1835,12 @@ private void toggleOverview() { Tab currentTab = getActivityTab(); - ContentViewCore contentViewCore = - currentTab != null ? currentTab.getContentViewCore() : null; + // If we don't have a current tab, show the overview mode. + if (currentTab == null) { + mLayoutManager.showOverview(false); + return; + } + ContentViewCore contentViewCore = currentTab.getContentViewCore(); if (!mLayoutManager.overviewVisible()) { getCompositorViewHolder().hideKeyboard(new Runnable() {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/browseractions/BrowserActionsContextMenuItemDelegate.java b/chrome/android/java/src/org/chromium/chrome/browser/browseractions/BrowserActionsContextMenuItemDelegate.java index 11854ef..7c2bd87ed 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/browseractions/BrowserActionsContextMenuItemDelegate.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/browseractions/BrowserActionsContextMenuItemDelegate.java
@@ -4,35 +4,105 @@ package org.chromium.chrome.browser.browseractions; +import android.app.NotificationManager; import android.app.PendingIntent; import android.app.PendingIntent.CanceledException; import android.content.ClipData; import android.content.ClipboardManager; import android.content.Context; import android.content.Intent; +import android.content.SharedPreferences; import android.net.Uri; import android.provider.Browser; import org.chromium.base.ContextUtils; import org.chromium.base.Log; +import org.chromium.chrome.R; import org.chromium.chrome.browser.IntentHandler; import org.chromium.chrome.browser.document.ChromeLauncherActivity; +import org.chromium.chrome.browser.notifications.ChromeNotificationBuilder; +import org.chromium.chrome.browser.notifications.NotificationBuilderFactory; +import org.chromium.chrome.browser.notifications.NotificationConstants; +import org.chromium.chrome.browser.notifications.NotificationUmaTracker; +import org.chromium.chrome.browser.notifications.channels.ChannelDefinitions; import org.chromium.chrome.browser.tabmodel.TabModel.TabLaunchType; import org.chromium.chrome.browser.util.IntentUtils; +import org.chromium.ui.widget.Toast; /** * A delegate responsible for taking actions based on browser action context menu selections. */ public class BrowserActionsContextMenuItemDelegate { private static final String TAG = "BrowserActionsItem"; + /** + * Action to request open ChromeTabbedActivity in tab switcher mode. + */ + public static final String ACTION_BROWSER_ACTIONS_OPEN_IN_BACKGROUND = + "org.chromium.chrome.browser.browseractions.browser_action_open_in_background"; + + public static final String PREF_HAS_BROWSER_ACTIONS_NOTIFICATION = + "org.chromium.chrome.browser.browseractions.HAS_BROWSER_ACTIONS_NOTIFICATION"; + + /** + * Extra that indicates whether to show a Tab for single url or the tab switcher for + * multiple urls. + */ + public static final String EXTRA_IS_SINGLE_URL = + "org.chromium.chrome.browser.browseractions.is_single_url"; private final Context mContext; + private final NotificationManager mNotificationManager; + private final SharedPreferences mSharedPreferences; + + private void sendBrowserActionsNotification() { + ChromeNotificationBuilder builder = createNotificationBuilder(); + mNotificationManager.notify( + NotificationConstants.NOTIFICATION_ID_BROWSER_ACTIONS, builder.build()); + mSharedPreferences.edit().putBoolean(PREF_HAS_BROWSER_ACTIONS_NOTIFICATION, true).apply(); + NotificationUmaTracker.getInstance().onNotificationShown( + NotificationUmaTracker.BROWSER_ACTIONS, ChannelDefinitions.CHANNEL_ID_BROWSER); + } + + private ChromeNotificationBuilder createNotificationBuilder() { + ChromeNotificationBuilder builder = + NotificationBuilderFactory + .createChromeNotificationBuilder( + true /* preferCompat */, ChannelDefinitions.CHANNEL_ID_BROWSER) + .setSmallIcon(R.drawable.infobar_chrome) + .setLocalOnly(true) + .setAutoCancel(true) + .setContentText( + mContext.getString(R.string.browser_actions_notification_text)); + int titleResId = hasBrowserActionsNotification() + ? R.string.browser_actions_multi_links_open_notification_title + : R.string.browser_actions_single_link_open_notification_title; + builder.setContentTitle(mContext.getString(titleResId)); + Intent intent = buildNotificationIntent(); + PendingIntent notifyPendingIntent = + PendingIntent.getActivity(mContext, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT); + builder.setContentIntent(notifyPendingIntent); + return builder; + } + + private Intent buildNotificationIntent() { + Intent intent = new Intent(mContext, ChromeLauncherActivity.class); + intent.setAction(ACTION_BROWSER_ACTIONS_OPEN_IN_BACKGROUND); + intent.putExtra(EXTRA_IS_SINGLE_URL, !hasBrowserActionsNotification()); + return intent; + } + + private boolean hasBrowserActionsNotification() { + return mSharedPreferences.getBoolean(PREF_HAS_BROWSER_ACTIONS_NOTIFICATION, false); + } /** * Builds a {@link BrowserActionsContextMenuItemDelegate} instance. */ public BrowserActionsContextMenuItemDelegate() { mContext = ContextUtils.getApplicationContext(); + mNotificationManager = + (NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE); + mSharedPreferences = ContextUtils.getAppSharedPreferences(); } /** @@ -66,7 +136,12 @@ * Called when the {@code linkUrl} should be opened in Chrome in the background. * @param linkUrl The url to open. */ - public void onOpenInBackground(String linkUrl) {} + public void onOpenInBackground(String linkUrl) { + sendBrowserActionsNotification(); + Toast.makeText(mContext, R.string.browser_actions_open_in_background_toast_message, + Toast.LENGTH_SHORT) + .show(); + } /** * Called when a custom item of Browser action menu is selected. @@ -91,4 +166,43 @@ * @param linkUrl The url to share. */ public void share(String linkUrl) {} + + /** + * Cancel Browser Actions notification. + */ + public static void cancelBrowserActionsNotification() { + NotificationManager notificationManager = + (NotificationManager) ContextUtils.getApplicationContext().getSystemService( + Context.NOTIFICATION_SERVICE); + notificationManager.cancel(NotificationConstants.NOTIFICATION_ID_BROWSER_ACTIONS); + ContextUtils.getAppSharedPreferences() + .edit() + .putBoolean( + BrowserActionsContextMenuItemDelegate.PREF_HAS_BROWSER_ACTIONS_NOTIFICATION, + false) + .apply(); + } + + /** + * Checks whether Chrome should display tab switcher via Browser Actions Intent. + * @param intent The intent to open the Chrome. + * @param isOverviewVisible Whether tab switcher is shown. + */ + public static boolean toggleOverviewByBrowserActions(Intent intent, boolean isOverviewVisible) { + boolean fromBrowserActions = isStartedByBrowserActions(intent); + boolean isSingleUrl = IntentUtils.safeGetBooleanExtra( + intent, BrowserActionsContextMenuItemDelegate.EXTRA_IS_SINGLE_URL, false); + if (fromBrowserActions) { + return isSingleUrl == isOverviewVisible; + } + return false; + } + + private static boolean isStartedByBrowserActions(Intent intent) { + if (BrowserActionsContextMenuItemDelegate.ACTION_BROWSER_ACTIONS_OPEN_IN_BACKGROUND.equals( + intent.getAction())) { + return true; + } + return false; + } }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/notifications/NotificationConstants.java b/chrome/android/java/src/org/chromium/chrome/browser/notifications/NotificationConstants.java index 5d823ce..a2dca7f1 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/notifications/NotificationConstants.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/notifications/NotificationConstants.java
@@ -56,6 +56,11 @@ public static final int NOTIFICATION_ID_PHYSICAL_WEB = 3; /** + * Unique identifier for Browser Actions notification. + */ + public static final int NOTIFICATION_ID_BROWSER_ACTIONS = 4; + + /** * Unique identifier for the summary notification for downloads. Using the ID this summary was * going to have before it was migrated here. * TODO(dtrainor): Clean up this ID and make sure it's in line with existing id counters without
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/notifications/NotificationUmaTracker.java b/chrome/android/java/src/org/chromium/chrome/browser/notifications/NotificationUmaTracker.java index f74a3eb..e07f398 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/notifications/NotificationUmaTracker.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/notifications/NotificationUmaTracker.java
@@ -30,7 +30,8 @@ private static final String TAG = "NotifsUMATracker"; @Retention(RetentionPolicy.SOURCE) @IntDef({DOWNLOAD_FILES, DOWNLOAD_PAGES, CLOSE_INCOGNITO, CONTENT_SUGGESTION, MEDIA_CAPTURE, - PHYSICAL_WEB, MEDIA, SITES, SYNC, WEBAPK, SYSTEM_NOTIFICATION_TYPE_BOUNDARY}) + PHYSICAL_WEB, MEDIA, SITES, SYNC, WEBAPK, BROWSER_ACTIONS, + SYSTEM_NOTIFICATION_TYPE_BOUNDARY}) public @interface SystemNotificationType {} /* @@ -53,8 +54,9 @@ public static final int SITES = 7; public static final int SYNC = 8; public static final int WEBAPK = 9; + public static final int BROWSER_ACTIONS = 10; - private static final int SYSTEM_NOTIFICATION_TYPE_BOUNDARY = 10; + private static final int SYSTEM_NOTIFICATION_TYPE_BOUNDARY = 11; private static final String LAST_SHOWN_NOTIFICATION_TYPE_KEY = "NotificationUmaTracker.LastShownNotificationType";
diff --git a/chrome/android/java/strings/android_chrome_strings.grd b/chrome/android/java/strings/android_chrome_strings.grd index 2fd8993c..d3c6a62 100644 --- a/chrome/android/java/strings/android_chrome_strings.grd +++ b/chrome/android/java/strings/android_chrome_strings.grd
@@ -1737,6 +1737,18 @@ <message name="IDS_BROWSER_ACTIONS_SHARE" desc="Browser Actions menu item to share the url address of the selected link. [CHAR-LIMIT=30]"> Share... </message> + <message name="IDS_BROWSER_ACTIONS_OPEN_IN_BACKGROUND_TOAST_MESSAGE" desc="Browser Actions toast message when to open a link in new Chrome tab in the backgoround. [CHAR-LIMIT=30]"> + Link opened in Chrome + </message> + <message name="IDS_BROWSER_ACTIONS_SINGLE_LINK_OPEN_NOTIFICATION_TITLE" desc="Content title displayed in the notification when one link is opened in the background from Browser Actions. [CHAR-LIMIT=30]"> + Link opened in Chrome + </message> + <message name="IDS_BROWSER_ACTIONS_MULTI_LINKS_OPEN_NOTIFICATION_TITLE" desc="Content title displayed in the notification when multiple links are opened in the background from Browser Actions. [CHAR-LIMIT=30]"> + Multiple links opened in Chrome + </message> + <message name="IDS_BROWSER_ACTIONS_NOTIFICATION_TEXT" desc="Context text displayed in the notification when links are opened in the background from Browser Actions. [CHAR-LIMIT=30]"> + Tap to view + </message> <!-- App banner strings --> <message name="IDS_APP_BANNER_INSTALLING" desc="Button text indicating that an application is being installed. [CHAR-LIMIT=25]">
diff --git a/chrome/app/bookmarks_strings.grdp b/chrome/app/bookmarks_strings.grdp index a4c5b40..d6d070a 100644 --- a/chrome/app/bookmarks_strings.grdp +++ b/chrome/app/bookmarks_strings.grdp
@@ -200,16 +200,16 @@ <message name="IDS_BOOKMARK_AX_BUBBLE_PAGE_BOOKMARK" desc="Title of the bubble when re-clicking on a bookmark, read by spoken feedback"> Edit bookmark </message> - <message name="IDS_BOOKMARK_BUBBLE_TITLE_TEXT" desc="Text preceding the title of the page that was bookmarked"> + <message name="IDS_BOOKMARK_BUBBLE_NAME_LABEL" desc="Text preceding the title of the page that was bookmarked"> Name: </message> - <message name="IDS_BOOKMARK_AX_BUBBLE_TITLE_TEXT" desc="Text preceding the title of the page that was bookmarked, read by spoken feedback"> + <message name="IDS_BOOKMARK_AX_BUBBLE_NAME_LABEL" desc="Text preceding the title of the page that was bookmarked, read by spoken feedback"> Bookmark name </message> - <message name="IDS_BOOKMARK_BUBBLE_FOLDER_TEXT" desc="Text preceding the folder selector"> + <message name="IDS_BOOKMARK_BUBBLE_FOLDER_LABEL" desc="Text preceding the folder selector"> Folder: </message> - <message name="IDS_BOOKMARK_AX_BUBBLE_FOLDER_TEXT" desc="Text preceding the folder selector"> + <message name="IDS_BOOKMARK_AX_BUBBLE_FOLDER_LABEL" desc="Text preceding the folder selector"> Bookmark folder </message> <message name="IDS_BOOKMARK_BUBBLE_OPTIONS" desc="Title of the button the user can click to edit details of the bookmark">
diff --git a/chrome/app/nibs/BookmarkBubble.xib b/chrome/app/nibs/BookmarkBubble.xib index ddb1618..67b9672 100644 --- a/chrome/app/nibs/BookmarkBubble.xib +++ b/chrome/app/nibs/BookmarkBubble.xib
@@ -87,7 +87,7 @@ <textField verticalHuggingPriority="750" id="10"> <rect key="frame" x="17" y="6" width="65" height="14"/> <autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMinY="YES"/> - <textFieldCell key="cell" controlSize="small" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="^IDS_BOOKMARK_BUBBLE_FOLDER_TEXT" id="11"> + <textFieldCell key="cell" controlSize="small" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="^IDS_BOOKMARK_BUBBLE_FOLDER_LABEL" id="11"> <font key="font" metaFont="smallSystem"/> <color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/> <color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/> @@ -96,7 +96,7 @@ <textField verticalHuggingPriority="750" id="8"> <rect key="frame" x="17" y="31" width="65" height="14"/> <autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMinY="YES"/> - <textFieldCell key="cell" controlSize="small" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="^IDS_BOOKMARK_BUBBLE_TITLE_TEXT" id="9"> + <textFieldCell key="cell" controlSize="small" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="^IDS_BOOKMARK_BUBBLE_NAME_LABEL" id="9"> <font key="font" metaFont="smallSystem"/> <color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/> <color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc index dd8a2e27..0ae1b11 100644 --- a/chrome/browser/about_flags.cc +++ b/chrome/browser/about_flags.cc
@@ -235,6 +235,15 @@ const FeatureEntry::Choice kMarkHttpAsChoices[] = { {flags_ui::kGenericExperimentChoiceDefault, "", ""}, + {flag_descriptions::kMarkHttpAsNonSecureAfterEditing, + security_state::switches::kMarkHttpAs, + security_state::switches::kMarkHttpAsNonSecureAfterEditing}, + {flag_descriptions::kMarkHttpAsNonSecureWhileIncognito, + security_state::switches::kMarkHttpAs, + security_state::switches::kMarkHttpAsNonSecureWhileIncognito}, + {flag_descriptions::kMarkHttpAsNonSecureWhileIncognitoOrEditing, + security_state::switches::kMarkHttpAs, + security_state::switches::kMarkHttpAsNonSecureWhileIncognitoOrEditing}, {flag_descriptions::kMarkHttpAsDangerous, security_state::switches::kMarkHttpAs, security_state::switches::kMarkHttpAsDangerous}}; @@ -1851,6 +1860,13 @@ kOsDesktop, SINGLE_VALUE_TYPE( switches::kEnableMessageCenterAlwaysScrollUpUponNotificationRemoval)}, + {"enable-message-center-new-style-notification", + flag_descriptions::kMessageCenterNewStyleNotificationName, + flag_descriptions::kMessageCenterNewStyleNotificationDescription, + kOsDesktop, + ENABLE_DISABLE_VALUE_TYPE( + switches::kEnableMessageCenterNewStyleNotification, + switches::kDisableMessageCenterNewStyleNotification)}, #endif // !OS_ANDROID {"enable-md-policy-page", flag_descriptions::kEnableMaterialDesignPolicyPageName,
diff --git a/chrome/browser/background_sync/background_sync_permission_context.cc b/chrome/browser/background_sync/background_sync_permission_context.cc index 84350dd..74a48d9 100644 --- a/chrome/browser/background_sync/background_sync_permission_context.cc +++ b/chrome/browser/background_sync/background_sync_permission_context.cc
@@ -10,7 +10,8 @@ BackgroundSyncPermissionContext::BackgroundSyncPermissionContext( Profile* profile) : PermissionContextBase(profile, - CONTENT_SETTINGS_TYPE_BACKGROUND_SYNC) {} + CONTENT_SETTINGS_TYPE_BACKGROUND_SYNC, + blink::WebFeaturePolicyFeature::kNotFound) {} void BackgroundSyncPermissionContext::CancelPermissionRequest( content::WebContents* web_contents,
diff --git a/chrome/browser/chromeos/BUILD.gn b/chrome/browser/chromeos/BUILD.gn index 10c3cc3..f22e7aa 100644 --- a/chrome/browser/chromeos/BUILD.gn +++ b/chrome/browser/chromeos/BUILD.gn
@@ -977,6 +977,8 @@ "login/ui/user_adding_screen_input_methods_controller.h", "login/ui/web_contents_forced_title.cc", "login/ui/web_contents_forced_title.h", + "login/ui/web_contents_set_background_color.cc", + "login/ui/web_contents_set_background_color.h", "login/ui/webui_login_display.cc", "login/ui/webui_login_display.h", "login/ui/webui_login_view.cc",
diff --git a/chrome/browser/chromeos/arc/auth/arc_active_directory_enrollment_token_fetcher.cc b/chrome/browser/chromeos/arc/auth/arc_active_directory_enrollment_token_fetcher.cc index 3137e46f..7fbfb56e 100644 --- a/chrome/browser/chromeos/arc/auth/arc_active_directory_enrollment_token_fetcher.cc +++ b/chrome/browser/chromeos/arc/auth/arc_active_directory_enrollment_token_fetcher.cc
@@ -62,9 +62,9 @@ callback_ = callback; dm_token_storage_ = base::MakeUnique<policy::DMTokenStorage>( g_browser_process->local_state()); - dm_token_storage_->RetrieveDMToken( - base::Bind(&ArcActiveDirectoryEnrollmentTokenFetcher::OnDMTokenAvailable, - weak_ptr_factory_.GetWeakPtr())); + dm_token_storage_->RetrieveDMToken(base::BindOnce( + &ArcActiveDirectoryEnrollmentTokenFetcher::OnDMTokenAvailable, + weak_ptr_factory_.GetWeakPtr())); } void ArcActiveDirectoryEnrollmentTokenFetcher::OnDMTokenAvailable( @@ -220,8 +220,8 @@ if (close_saml_page) { content::BrowserThread::PostTask( content::BrowserThread::UI, FROM_HERE, - base::Bind(&chrome::CloseWebContents, browser_, web_contents, - false /* add_to_history */)); + base::BindOnce(&chrome::CloseWebContents, browser_, web_contents, + false /* add_to_history */)); } browser_ = nullptr; }
diff --git a/chrome/browser/chromeos/arc/auth/arc_active_directory_enrollment_token_fetcher_browsertest.cc b/chrome/browser/chromeos/arc/auth/arc_active_directory_enrollment_token_fetcher_browsertest.cc index ed82bb6..2f7d5e4 100644 --- a/chrome/browser/chromeos/arc/auth/arc_active_directory_enrollment_token_fetcher_browsertest.cc +++ b/chrome/browser/chromeos/arc/auth/arc_active_directory_enrollment_token_fetcher_browsertest.cc
@@ -213,8 +213,8 @@ net::NetworkDelegate* network_delegate) { content::BrowserThread::PostTask( content::BrowserThread::UI, FROM_HERE, - base::Bind(&TabStripModel::CloseSelectedTabs, - base::Unretained(browser->tab_strip_model()))); + base::BindOnce(&TabStripModel::CloseSelectedTabs, + base::Unretained(browser->tab_strip_model()))); return nullptr; } @@ -291,7 +291,7 @@ auto dm_token_storage = base::MakeUnique<policy::DMTokenStorage>( g_browser_process->local_state()); dm_token_storage->StoreDMToken( - kFakeDmToken, base::Bind( + kFakeDmToken, base::BindOnce( [](base::RunLoop* run_loop, bool success) { EXPECT_TRUE(success); run_loop->Quit();
diff --git a/chrome/browser/chromeos/arc/downloads_watcher/arc_downloads_watcher_service.cc b/chrome/browser/chromeos/arc/downloads_watcher/arc_downloads_watcher_service.cc index f57671f..3717476 100644 --- a/chrome/browser/chromeos/arc/downloads_watcher/arc_downloads_watcher_service.cc +++ b/chrome/browser/chromeos/arc/downloads_watcher/arc_downloads_watcher_service.cc
@@ -266,8 +266,8 @@ outstanding_task_ = true; BrowserThread::PostDelayedTask( BrowserThread::FILE, FROM_HERE, - base::Bind(&DownloadsWatcher::DelayBuildTimestampMap, - weak_ptr_factory_.GetWeakPtr()), + base::BindOnce(&DownloadsWatcher::DelayBuildTimestampMap, + weak_ptr_factory_.GetWeakPtr()), kBuildTimestampMapDelay); } else { last_notify_time_ = base::TimeTicks::Now(); @@ -301,7 +301,7 @@ } BrowserThread::PostTask( BrowserThread::UI, FROM_HERE, - base::Bind(callback_, base::Passed(std::move(string_paths)))); + base::BindOnce(callback_, base::Passed(std::move(string_paths)))); if (last_notify_time_ > snapshot_time) DelayBuildTimestampMap(); else @@ -339,9 +339,9 @@ watcher_ = base::MakeUnique<DownloadsWatcher>( base::Bind(&ArcDownloadsWatcherService::OnDownloadsChanged, weak_ptr_factory_.GetWeakPtr())); - BrowserThread::PostTask( - BrowserThread::FILE, FROM_HERE, - base::Bind(&DownloadsWatcher::Start, base::Unretained(watcher_.get()))); + BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE, + base::BindOnce(&DownloadsWatcher::Start, + base::Unretained(watcher_.get()))); } void ArcDownloadsWatcherService::StopWatchingDownloads() {
diff --git a/chrome/browser/chromeos/arc/fileapi/arc_content_file_system_async_file_util.cc b/chrome/browser/chromeos/arc/fileapi/arc_content_file_system_async_file_util.cc index 1a142f3f..b267fb3 100644 --- a/chrome/browser/chromeos/arc/fileapi/arc_content_file_system_async_file_util.cc +++ b/chrome/browser/chromeos/arc/fileapi/arc_content_file_system_async_file_util.cc
@@ -44,7 +44,7 @@ NOTIMPLEMENTED(); base::ThreadTaskRunnerHandle::Get()->PostTask( FROM_HERE, - base::Bind(callback, base::Passed(base::File()), base::Closure())); + base::BindOnce(callback, base::Passed(base::File()), base::Closure())); } void ArcContentFileSystemAsyncFileUtil::EnsureFileExists( @@ -53,7 +53,8 @@ const EnsureFileExistsCallback& callback) { NOTIMPLEMENTED(); base::ThreadTaskRunnerHandle::Get()->PostTask( - FROM_HERE, base::Bind(callback, base::File::FILE_ERROR_FAILED, false)); + FROM_HERE, + base::BindOnce(callback, base::File::FILE_ERROR_FAILED, false)); } void ArcContentFileSystemAsyncFileUtil::CreateDirectory( @@ -64,7 +65,7 @@ const StatusCallback& callback) { NOTIMPLEMENTED(); base::ThreadTaskRunnerHandle::Get()->PostTask( - FROM_HERE, base::Bind(callback, base::File::FILE_ERROR_FAILED)); + FROM_HERE, base::BindOnce(callback, base::File::FILE_ERROR_FAILED)); } void ArcContentFileSystemAsyncFileUtil::GetFileInfo( @@ -83,8 +84,8 @@ const ReadDirectoryCallback& callback) { NOTIMPLEMENTED(); base::ThreadTaskRunnerHandle::Get()->PostTask( - FROM_HERE, - base::Bind(callback, base::File::FILE_ERROR_FAILED, EntryList(), false)); + FROM_HERE, base::BindOnce(callback, base::File::FILE_ERROR_FAILED, + EntryList(), false)); } void ArcContentFileSystemAsyncFileUtil::Touch( @@ -95,7 +96,7 @@ const StatusCallback& callback) { NOTIMPLEMENTED(); base::ThreadTaskRunnerHandle::Get()->PostTask( - FROM_HERE, base::Bind(callback, base::File::FILE_ERROR_FAILED)); + FROM_HERE, base::BindOnce(callback, base::File::FILE_ERROR_FAILED)); } void ArcContentFileSystemAsyncFileUtil::Truncate( @@ -105,7 +106,7 @@ const StatusCallback& callback) { NOTIMPLEMENTED(); base::ThreadTaskRunnerHandle::Get()->PostTask( - FROM_HERE, base::Bind(callback, base::File::FILE_ERROR_FAILED)); + FROM_HERE, base::BindOnce(callback, base::File::FILE_ERROR_FAILED)); } void ArcContentFileSystemAsyncFileUtil::CopyFileLocal( @@ -117,7 +118,7 @@ const StatusCallback& callback) { NOTIMPLEMENTED(); base::ThreadTaskRunnerHandle::Get()->PostTask( - FROM_HERE, base::Bind(callback, base::File::FILE_ERROR_FAILED)); + FROM_HERE, base::BindOnce(callback, base::File::FILE_ERROR_FAILED)); } void ArcContentFileSystemAsyncFileUtil::MoveFileLocal( @@ -128,7 +129,7 @@ const StatusCallback& callback) { NOTIMPLEMENTED(); base::ThreadTaskRunnerHandle::Get()->PostTask( - FROM_HERE, base::Bind(callback, base::File::FILE_ERROR_FAILED)); + FROM_HERE, base::BindOnce(callback, base::File::FILE_ERROR_FAILED)); } void ArcContentFileSystemAsyncFileUtil::CopyInForeignFile( @@ -138,7 +139,7 @@ const StatusCallback& callback) { NOTIMPLEMENTED(); base::ThreadTaskRunnerHandle::Get()->PostTask( - FROM_HERE, base::Bind(callback, base::File::FILE_ERROR_FAILED)); + FROM_HERE, base::BindOnce(callback, base::File::FILE_ERROR_FAILED)); } void ArcContentFileSystemAsyncFileUtil::DeleteFile( @@ -147,7 +148,7 @@ const StatusCallback& callback) { NOTIMPLEMENTED(); base::ThreadTaskRunnerHandle::Get()->PostTask( - FROM_HERE, base::Bind(callback, base::File::FILE_ERROR_FAILED)); + FROM_HERE, base::BindOnce(callback, base::File::FILE_ERROR_FAILED)); } void ArcContentFileSystemAsyncFileUtil::DeleteDirectory( @@ -156,7 +157,7 @@ const StatusCallback& callback) { NOTIMPLEMENTED(); base::ThreadTaskRunnerHandle::Get()->PostTask( - FROM_HERE, base::Bind(callback, base::File::FILE_ERROR_FAILED)); + FROM_HERE, base::BindOnce(callback, base::File::FILE_ERROR_FAILED)); } void ArcContentFileSystemAsyncFileUtil::DeleteRecursively( @@ -165,7 +166,7 @@ const StatusCallback& callback) { NOTIMPLEMENTED(); base::ThreadTaskRunnerHandle::Get()->PostTask( - FROM_HERE, base::Bind(callback, base::File::FILE_ERROR_FAILED)); + FROM_HERE, base::BindOnce(callback, base::File::FILE_ERROR_FAILED)); } void ArcContentFileSystemAsyncFileUtil::CreateSnapshotFile( @@ -174,9 +175,10 @@ const CreateSnapshotFileCallback& callback) { NOTIMPLEMENTED(); base::ThreadTaskRunnerHandle::Get()->PostTask( - FROM_HERE, base::Bind(callback, base::File::FILE_ERROR_FAILED, - base::File::Info(), base::FilePath(), - scoped_refptr<storage::ShareableFileReference>())); + FROM_HERE, + base::BindOnce(callback, base::File::FILE_ERROR_FAILED, + base::File::Info(), base::FilePath(), + scoped_refptr<storage::ShareableFileReference>())); } } // namespace arc
diff --git a/chrome/browser/chromeos/arc/fileapi/arc_content_file_system_file_stream_reader.cc b/chrome/browser/chromeos/arc/fileapi/arc_content_file_system_file_stream_reader.cc index 401d439..7ba4f61f 100644 --- a/chrome/browser/chromeos/arc/fileapi/arc_content_file_system_file_stream_reader.cc +++ b/chrome/browser/chromeos/arc/fileapi/arc_content_file_system_file_stream_reader.cc
@@ -51,7 +51,8 @@ // Use the task runner to destruct |file_| after the completion of all // in-flight operations. task_runner_->PostTask( - FROM_HERE, base::Bind(&base::DeletePointer<base::File>, file_.release())); + FROM_HERE, + base::BindOnce(&base::DeletePointer<base::File>, file_.release())); } int ArcContentFileSystemFileStreamReader::Read(
diff --git a/chrome/browser/chromeos/arc/fileapi/arc_file_system_operation_runner.cc b/chrome/browser/chromeos/arc/fileapi/arc_file_system_operation_runner.cc index d597e1ae..77bcfeb 100644 --- a/chrome/browser/chromeos/arc/fileapi/arc_file_system_operation_runner.cc +++ b/chrome/browser/chromeos/arc/fileapi/arc_file_system_operation_runner.cc
@@ -98,7 +98,7 @@ arc_bridge_service()->file_system(), GetFileSize); if (!file_system_instance) { base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, - base::Bind(callback, -1)); + base::BindOnce(callback, -1)); return; } file_system_instance->GetFileSize(url.spec(), callback); @@ -118,7 +118,8 @@ arc_bridge_service()->file_system(), OpenFileToRead); if (!file_system_instance) { base::ThreadTaskRunnerHandle::Get()->PostTask( - FROM_HERE, base::Bind(callback, base::Passed(mojo::ScopedHandle()))); + FROM_HERE, + base::BindOnce(callback, base::Passed(mojo::ScopedHandle()))); return; } file_system_instance->OpenFileToRead(url.spec(), callback); @@ -139,7 +140,8 @@ arc_bridge_service()->file_system(), GetDocument); if (!file_system_instance) { base::ThreadTaskRunnerHandle::Get()->PostTask( - FROM_HERE, base::Bind(callback, base::Passed(mojom::DocumentPtr()))); + FROM_HERE, + base::BindOnce(callback, base::Passed(mojom::DocumentPtr()))); return; } file_system_instance->GetDocument(authority, document_id, callback); @@ -161,7 +163,7 @@ arc_bridge_service()->file_system(), GetChildDocuments); if (!file_system_instance) { base::ThreadTaskRunnerHandle::Get()->PostTask( - FROM_HERE, base::Bind(callback, base::nullopt)); + FROM_HERE, base::BindOnce(callback, base::nullopt)); return; } file_system_instance->GetChildDocuments(authority, parent_document_id, @@ -185,7 +187,7 @@ arc_bridge_service()->file_system(), AddWatcher); if (!file_system_instance) { base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, - base::Bind(callback, -1)); + base::BindOnce(callback, -1)); return; } file_system_instance->AddWatcher( @@ -201,8 +203,8 @@ // RemoveWatcher() is never deferred since watchers do not persist across // container reboots. if (should_defer_) { - base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, - base::Bind(callback, false)); + base::ThreadTaskRunnerHandle::Get()->PostTask( + FROM_HERE, base::BindOnce(callback, false)); return; } @@ -211,8 +213,8 @@ // users must not assume registered callbacks are immediately invalidated. auto iter = watcher_callbacks_.find(watcher_id); if (iter == watcher_callbacks_.end()) { - base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, - base::Bind(callback, false)); + base::ThreadTaskRunnerHandle::Get()->PostTask( + FROM_HERE, base::BindOnce(callback, false)); return; } watcher_callbacks_.erase(iter); @@ -220,8 +222,8 @@ auto* file_system_instance = ARC_GET_INSTANCE_FOR_METHOD( arc_bridge_service()->file_system(), AddWatcher); if (!file_system_instance) { - base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, - base::Bind(callback, false)); + base::ThreadTaskRunnerHandle::Get()->PostTask( + FROM_HERE, base::BindOnce(callback, false)); return; } file_system_instance->RemoveWatcher(watcher_id, callback);
diff --git a/chrome/browser/chromeos/arc/fileapi/arc_file_system_operation_runner_util.cc b/chrome/browser/chromeos/arc/fileapi/arc_file_system_operation_runner_util.cc index 4cbb0b5..424894bfd 100644 --- a/chrome/browser/chromeos/arc/fileapi/arc_file_system_operation_runner_util.cc +++ b/chrome/browser/chromeos/arc/fileapi/arc_file_system_operation_runner_util.cc
@@ -24,7 +24,7 @@ DCHECK_CURRENTLY_ON(BrowserThread::UI); BrowserThread::PostTask( BrowserThread::IO, FROM_HERE, - base::Bind(callback, base::Passed(std::move(result)))); + base::BindOnce(callback, base::Passed(std::move(result)))); } void GetFileSizeOnUIThread(const GURL& url, @@ -156,7 +156,8 @@ DCHECK_CURRENTLY_ON(BrowserThread::UI); BrowserThread::PostTask( BrowserThread::IO, FROM_HERE, - base::Bind(&ObserverIOThreadWrapper::OnWatchersClearedOnIOThread, this)); + base::BindOnce(&ObserverIOThreadWrapper::OnWatchersClearedOnIOThread, + this)); } void ObserverIOThreadWrapper::OnWatchersClearedOnIOThread() { @@ -171,8 +172,8 @@ DCHECK_CURRENTLY_ON(BrowserThread::IO); BrowserThread::PostTask( BrowserThread::UI, FROM_HERE, - base::Bind(&GetFileSizeOnUIThread, url, - base::Bind(&PostToIOThread<int64_t>, callback))); + base::BindOnce(&GetFileSizeOnUIThread, url, + base::Bind(&PostToIOThread<int64_t>, callback))); } void OpenFileToReadOnIOThread(const GURL& url, @@ -180,8 +181,9 @@ DCHECK_CURRENTLY_ON(BrowserThread::IO); BrowserThread::PostTask( BrowserThread::UI, FROM_HERE, - base::Bind(&OpenFileToReadOnUIThread, url, - base::Bind(&PostToIOThread<mojo::ScopedHandle>, callback))); + base::BindOnce( + &OpenFileToReadOnUIThread, url, + base::Bind(&PostToIOThread<mojo::ScopedHandle>, callback))); } void GetDocumentOnIOThread(const std::string& authority, @@ -190,8 +192,9 @@ DCHECK_CURRENTLY_ON(BrowserThread::IO); BrowserThread::PostTask( BrowserThread::UI, FROM_HERE, - base::Bind(&GetDocumentOnUIThread, authority, document_id, - base::Bind(&PostToIOThread<mojom::DocumentPtr>, callback))); + base::BindOnce( + &GetDocumentOnUIThread, authority, document_id, + base::Bind(&PostToIOThread<mojom::DocumentPtr>, callback))); } void GetChildDocumentsOnIOThread(const std::string& authority, @@ -200,7 +203,7 @@ DCHECK_CURRENTLY_ON(BrowserThread::IO); BrowserThread::PostTask( BrowserThread::UI, FROM_HERE, - base::Bind( + base::BindOnce( &GetChildDocumentsOnUIThread, authority, parent_document_id, base::Bind( &PostToIOThread<base::Optional<std::vector<mojom::DocumentPtr>>>, @@ -214,7 +217,7 @@ DCHECK_CURRENTLY_ON(BrowserThread::IO); BrowserThread::PostTask( BrowserThread::UI, FROM_HERE, - base::Bind( + base::BindOnce( &AddWatcherOnUIThread, authority, document_id, base::Bind(&PostToIOThread<ArcFileSystemOperationRunner::ChangeType>, watcher_callback), @@ -226,14 +229,14 @@ DCHECK_CURRENTLY_ON(BrowserThread::IO); BrowserThread::PostTask( BrowserThread::UI, FROM_HERE, - base::Bind(&RemoveWatcherOnUIThread, watcher_id, - base::Bind(&PostToIOThread<bool>, callback))); + base::BindOnce(&RemoveWatcherOnUIThread, watcher_id, + base::Bind(&PostToIOThread<bool>, callback))); } void AddObserverOnIOThread(scoped_refptr<ObserverIOThreadWrapper> observer) { DCHECK_CURRENTLY_ON(BrowserThread::IO); BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, - base::Bind(&AddObserverOnUIThread, observer)); + base::BindOnce(&AddObserverOnUIThread, observer)); } void RemoveObserverOnIOThread(scoped_refptr<ObserverIOThreadWrapper> observer) { @@ -242,7 +245,7 @@ // called after this function returns. observer->Disable(); BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, - base::Bind(&RemoveObserverOnUIThread, observer)); + base::BindOnce(&RemoveObserverOnUIThread, observer)); } } // namespace file_system_operation_runner_util
diff --git a/chrome/browser/chromeos/arc/policy/arc_android_management_checker.cc b/chrome/browser/chromeos/arc/policy/arc_android_management_checker.cc index d95bb86f..5dfe3be 100644 --- a/chrome/browser/chromeos/arc/policy/arc_android_management_checker.cc +++ b/chrome/browser/chromeos/arc/policy/arc_android_management_checker.cc
@@ -134,8 +134,9 @@ VLOG(2) << "Schedule next android management check in " << retry_delay_; base::ThreadTaskRunnerHandle::Get()->PostDelayedTask( - FROM_HERE, base::Bind(&ArcAndroidManagementChecker::StartCheckInternal, - weak_ptr_factory_.GetWeakPtr()), + FROM_HERE, + base::BindOnce(&ArcAndroidManagementChecker::StartCheckInternal, + weak_ptr_factory_.GetWeakPtr()), retry_delay_); retry_delay_ = std::min(retry_delay_ * 2, kRetryDelayMax); }
diff --git a/chrome/browser/chromeos/arc/process/arc_process_service.cc b/chrome/browser/chromeos/arc/process/arc_process_service.cc index ec6e417..e406736 100644 --- a/chrome/browser/chromeos/arc/process/arc_process_service.cc +++ b/chrome/browser/chromeos/arc/process/arc_process_service.cc
@@ -273,7 +273,7 @@ void ArcProcessService::OnInstanceReady() { DCHECK_CURRENTLY_ON(content::BrowserThread::UI); - task_runner_->PostTask(FROM_HERE, base::Bind(&Reset, nspid_to_pid_)); + task_runner_->PostTask(FROM_HERE, base::BindOnce(&Reset, nspid_to_pid_)); instance_ready_ = true; }
diff --git a/chrome/browser/chromeos/arc/tracing/arc_tracing_bridge.cc b/chrome/browser/chromeos/arc/tracing/arc_tracing_bridge.cc index 73004ea..be3ede1 100644 --- a/chrome/browser/chromeos/arc/tracing/arc_tracing_bridge.cc +++ b/chrome/browser/chromeos/arc/tracing/arc_tracing_bridge.cc
@@ -78,8 +78,8 @@ if (!tracing_instance) { // Use PostTask as the convention of TracingAgent. The caller expects // callback to be called after this function returns. - base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, - base::Bind(callback, false)); + base::ThreadTaskRunnerHandle::Get()->PostTask( + FROM_HERE, base::BindOnce(callback, false)); return; } @@ -100,8 +100,8 @@ mojom::TracingInstance* tracing_instance = ARC_GET_INSTANCE_FOR_METHOD(arc_bridge_service()->tracing(), StopTracing); if (!tracing_instance) { - base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, - base::Bind(callback, false)); + base::ThreadTaskRunnerHandle::Get()->PostTask( + FROM_HERE, base::BindOnce(callback, false)); return; } tracing_instance->StopTracing(callback);
diff --git a/chrome/browser/chromeos/arc/video/gpu_arc_video_service_host.cc b/chrome/browser/chromeos/arc/video/gpu_arc_video_service_host.cc index 45066a60..43a9ab4 100644 --- a/chrome/browser/chromeos/arc/video/gpu_arc_video_service_host.cc +++ b/chrome/browser/chromeos/arc/video/gpu_arc_video_service_host.cc
@@ -40,8 +40,8 @@ void Create(mojom::VideoAcceleratorServiceRequest request) override { content::BrowserThread::PostTask( content::BrowserThread::IO, FROM_HERE, - base::Bind(&ConnectToVideoAcceleratorServiceOnIOThread, - base::Passed(&request))); + base::BindOnce(&ConnectToVideoAcceleratorServiceOnIOThread, + base::Passed(&request))); } private:
diff --git a/ui/views/controls/webview/web_contents_set_background_color.cc b/chrome/browser/chromeos/login/ui/web_contents_set_background_color.cc similarity index 85% rename from ui/views/controls/webview/web_contents_set_background_color.cc rename to chrome/browser/chromeos/login/ui/web_contents_set_background_color.cc index e5de0e42..6b57011 100644 --- a/ui/views/controls/webview/web_contents_set_background_color.cc +++ b/chrome/browser/chromeos/login/ui/web_contents_set_background_color.cc
@@ -1,17 +1,17 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. +// Copyright 2016 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 "ui/views/controls/webview/web_contents_set_background_color.h" +#include "chrome/browser/chromeos/login/ui/web_contents_set_background_color.h" #include "base/memory/ptr_util.h" #include "content/public/browser/render_view_host.h" #include "content/public/browser/render_widget_host.h" #include "content/public/browser/render_widget_host_view.h" -DEFINE_WEB_CONTENTS_USER_DATA_KEY(views::WebContentsSetBackgroundColor); +DEFINE_WEB_CONTENTS_USER_DATA_KEY(chromeos::WebContentsSetBackgroundColor); -namespace views { +namespace chromeos { // static void WebContentsSetBackgroundColor::CreateForWebContentsWithColor( @@ -54,4 +54,4 @@ new_host->GetWidget()->GetView()->SetBackgroundColor(color_); } -} // namespace views +} // namespace chromeos
diff --git a/ui/views/controls/webview/web_contents_set_background_color.h b/chrome/browser/chromeos/login/ui/web_contents_set_background_color.h similarity index 72% rename from ui/views/controls/webview/web_contents_set_background_color.h rename to chrome/browser/chromeos/login/ui/web_contents_set_background_color.h index e10a3a3..024d310 100644 --- a/ui/views/controls/webview/web_contents_set_background_color.h +++ b/chrome/browser/chromeos/login/ui/web_contents_set_background_color.h
@@ -2,17 +2,16 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef UI_VIEWS_CONTROLS_WEBVIEW_WEB_CONTENTS_SET_BACKGROUND_COLOR_H_ -#define UI_VIEWS_CONTROLS_WEBVIEW_WEB_CONTENTS_SET_BACKGROUND_COLOR_H_ +#ifndef CHROME_BROWSER_CHROMEOS_LOGIN_UI_WEB_CONTENTS_SET_BACKGROUND_COLOR_H_ +#define CHROME_BROWSER_CHROMEOS_LOGIN_UI_WEB_CONTENTS_SET_BACKGROUND_COLOR_H_ #include "content/public/browser/web_contents_observer.h" #include "content/public/browser/web_contents_user_data.h" -#include "ui/views/controls/webview/webview_export.h" // Defined in SkColor.h (32-bit ARGB color). using SkColor = unsigned int; -namespace views { +namespace chromeos { // Ensures that the background color of a given WebContents instance is always // set to a given color value. @@ -20,9 +19,8 @@ : public content::WebContentsObserver, public content::WebContentsUserData<WebContentsSetBackgroundColor> { public: - WEBVIEW_EXPORT static void CreateForWebContentsWithColor( - content::WebContents* web_contents, - SkColor color); + static void CreateForWebContentsWithColor(content::WebContents* web_contents, + SkColor color); ~WebContentsSetBackgroundColor() override; @@ -41,6 +39,6 @@ DISALLOW_COPY_AND_ASSIGN(WebContentsSetBackgroundColor); }; -} // namespace views +} // namespace chromeos -#endif // UI_VIEWS_CONTROLS_WEBVIEW_WEB_CONTENTS_SET_BACKGROUND_COLOR_H_ +#endif // CHROME_BROWSER_CHROMEOS_LOGIN_UI_WEB_CONTENTS_SET_BACKGROUND_COLOR_H_
diff --git a/chrome/browser/chromeos/login/ui/webui_login_view.cc b/chrome/browser/chromeos/login/ui/webui_login_view.cc index 78e038c8..89d498d 100644 --- a/chrome/browser/chromeos/login/ui/webui_login_view.cc +++ b/chrome/browser/chromeos/login/ui/webui_login_view.cc
@@ -25,6 +25,7 @@ #include "chrome/browser/chromeos/login/ui/preloaded_web_view_factory.h" #include "chrome/browser/chromeos/login/ui/proxy_settings_dialog.h" #include "chrome/browser/chromeos/login/ui/web_contents_forced_title.h" +#include "chrome/browser/chromeos/login/ui/web_contents_set_background_color.h" #include "chrome/browser/chromeos/login/ui/webui_login_display.h" #include "chrome/browser/chromeos/profiles/profile_helper.h" #include "chrome/browser/extensions/chrome_extension_web_contents_observer.h" @@ -56,7 +57,6 @@ #include "ui/gfx/geometry/rect.h" #include "ui/gfx/geometry/size.h" #include "ui/keyboard/keyboard_controller.h" -#include "ui/views/controls/webview/web_contents_set_background_color.h" #include "ui/views/controls/webview/webview.h" #include "ui/views/widget/widget.h" @@ -220,7 +220,7 @@ if (!title.empty()) WebContentsForcedTitle::CreateForWebContentsWithTitle(web_contents, title); - views::WebContentsSetBackgroundColor::CreateForWebContentsWithColor( + WebContentsSetBackgroundColor::CreateForWebContentsWithColor( web_contents, SK_ColorTRANSPARENT); // Ensure that the login UI has a tab ID, which will allow the GAIA auth
diff --git a/chrome/browser/flag_descriptions.cc b/chrome/browser/flag_descriptions.cc index c8a324b7..056ca964 100644 --- a/chrome/browser/flag_descriptions.cc +++ b/chrome/browser/flag_descriptions.cc
@@ -171,8 +171,13 @@ const char kMarkHttpAsName[] = "Mark non-secure origins as non-secure"; const char kMarkHttpAsDescription[] = "Change the UI treatment for HTTP pages"; - const char kMarkHttpAsDangerous[] = "Always mark HTTP as actively dangerous"; +const char kMarkHttpAsNonSecureAfterEditing[] = + "Warn on HTTP after editing forms"; +const char kMarkHttpAsNonSecureWhileIncognito[] = + "Warn on HTTP while in Incognito mode"; +const char kMarkHttpAsNonSecureWhileIncognitoOrEditing[] = + "Warn on HTTP while in Incognito mode or after editing forms"; // Material design of the Incognito NTP. @@ -2056,6 +2061,11 @@ "Enables experiment that message center always scroll up when a " "notification is removed."; +const char kMessageCenterNewStyleNotificationName[] = "New style notification"; + +const char kMessageCenterNewStyleNotificationDescription[] = + "Enables the experiment style of material-design notification"; + const char kCastStreamingHwEncodingName[] = "Cast Streaming hardware video encoding";
diff --git a/chrome/browser/flag_descriptions.h b/chrome/browser/flag_descriptions.h index c2df124f..d9e11a9 100644 --- a/chrome/browser/flag_descriptions.h +++ b/chrome/browser/flag_descriptions.h
@@ -427,6 +427,9 @@ extern const char kMarkHttpAsName[]; extern const char kMarkHttpAsDescription[]; extern const char kMarkHttpAsDangerous[]; +extern const char kMarkHttpAsNonSecureAfterEditing[]; +extern const char kMarkHttpAsNonSecureWhileIncognito[]; +extern const char kMarkHttpAsNonSecureWhileIncognitoOrEditing[]; extern const char kMaterialDesignIncognitoNTPName[]; extern const char kMaterialDesignIncognitoNTPDescription[]; @@ -440,6 +443,9 @@ extern const char kMemoryCoordinatorName[]; extern const char kMemoryCoordinatorDescription[]; +extern const char kMessageCenterNewStyleNotificationName[]; +extern const char kMessageCenterNewStyleNotificationDescription[]; + extern const char kMessageCenterAlwaysScrollUpUponRemovalName[]; extern const char kMessageCenterAlwaysScrollUpUponRemovalDescription[];
diff --git a/chrome/browser/geolocation/geolocation_permission_context.cc b/chrome/browser/geolocation/geolocation_permission_context.cc index 0283b6c..20358af 100644 --- a/chrome/browser/geolocation/geolocation_permission_context.cc +++ b/chrome/browser/geolocation/geolocation_permission_context.cc
@@ -16,7 +16,8 @@ GeolocationPermissionContext::GeolocationPermissionContext(Profile* profile) : PermissionContextBase(profile, - CONTENT_SETTINGS_TYPE_GEOLOCATION), + CONTENT_SETTINGS_TYPE_GEOLOCATION, + blink::WebFeaturePolicyFeature::kGeolocation), extensions_context_(profile) {} GeolocationPermissionContext::~GeolocationPermissionContext() {
diff --git a/chrome/browser/media/midi_permission_context.cc b/chrome/browser/media/midi_permission_context.cc index 7e880138..f137ee5 100644 --- a/chrome/browser/media/midi_permission_context.cc +++ b/chrome/browser/media/midi_permission_context.cc
@@ -5,7 +5,9 @@ #include "chrome/browser/media/midi_permission_context.h" MidiPermissionContext::MidiPermissionContext(Profile* profile) - : PermissionContextBase(profile, CONTENT_SETTINGS_TYPE_MIDI) {} + : PermissionContextBase(profile, + CONTENT_SETTINGS_TYPE_MIDI, + blink::WebFeaturePolicyFeature::kMidiFeature) {} MidiPermissionContext::~MidiPermissionContext() { }
diff --git a/chrome/browser/media/midi_sysex_permission_context.cc b/chrome/browser/media/midi_sysex_permission_context.cc index 689e141a..a09accf 100644 --- a/chrome/browser/media/midi_sysex_permission_context.cc +++ b/chrome/browser/media/midi_sysex_permission_context.cc
@@ -10,7 +10,9 @@ #include "url/gurl.h" MidiSysexPermissionContext::MidiSysexPermissionContext(Profile* profile) - : PermissionContextBase(profile, CONTENT_SETTINGS_TYPE_MIDI_SYSEX) {} + : PermissionContextBase(profile, + CONTENT_SETTINGS_TYPE_MIDI_SYSEX, + blink::WebFeaturePolicyFeature::kMidiFeature) {} MidiSysexPermissionContext::~MidiSysexPermissionContext() {}
diff --git a/chrome/browser/media/protected_media_identifier_permission_context.cc b/chrome/browser/media/protected_media_identifier_permission_context.cc index ebaa354..7fd089b 100644 --- a/chrome/browser/media/protected_media_identifier_permission_context.cc +++ b/chrome/browser/media/protected_media_identifier_permission_context.cc
@@ -41,7 +41,8 @@ ProtectedMediaIdentifierPermissionContext:: ProtectedMediaIdentifierPermissionContext(Profile* profile) : PermissionContextBase(profile, - CONTENT_SETTINGS_TYPE_PROTECTED_MEDIA_IDENTIFIER) + CONTENT_SETTINGS_TYPE_PROTECTED_MEDIA_IDENTIFIER, + blink::WebFeaturePolicyFeature::kEme) #if defined(OS_CHROMEOS) , weak_factory_(this)
diff --git a/chrome/browser/media/webrtc/media_stream_device_permission_context.cc b/chrome/browser/media/webrtc/media_stream_device_permission_context.cc index d31aeda..7a8813f03 100644 --- a/chrome/browser/media/webrtc/media_stream_device_permission_context.cc +++ b/chrome/browser/media/webrtc/media_stream_device_permission_context.cc
@@ -14,10 +14,25 @@ #include "content/public/common/url_constants.h" #include "extensions/common/constants.h" +namespace { + +blink::WebFeaturePolicyFeature GetFeaturePolicyFeature( + ContentSettingsType type) { + if (type == CONTENT_SETTINGS_TYPE_MEDIASTREAM_MIC) + return blink::WebFeaturePolicyFeature::kMicrophone; + + DCHECK_EQ(CONTENT_SETTINGS_TYPE_MEDIASTREAM_CAMERA, type); + return blink::WebFeaturePolicyFeature::kCamera; +} + +} // namespace + MediaStreamDevicePermissionContext::MediaStreamDevicePermissionContext( Profile* profile, const ContentSettingsType content_settings_type) - : PermissionContextBase(profile, content_settings_type), + : PermissionContextBase(profile, + content_settings_type, + GetFeaturePolicyFeature(content_settings_type)), content_settings_type_(content_settings_type) { DCHECK(content_settings_type_ == CONTENT_SETTINGS_TYPE_MEDIASTREAM_MIC || content_settings_type_ == CONTENT_SETTINGS_TYPE_MEDIASTREAM_CAMERA);
diff --git a/chrome/browser/notifications/notification_permission_context.cc b/chrome/browser/notifications/notification_permission_context.cc index 261d3c62..a159d69 100644 --- a/chrome/browser/notifications/notification_permission_context.cc +++ b/chrome/browser/notifications/notification_permission_context.cc
@@ -159,7 +159,9 @@ NotificationPermissionContext::NotificationPermissionContext( Profile* profile, ContentSettingsType content_settings_type) - : PermissionContextBase(profile, content_settings_type), + : PermissionContextBase(profile, + content_settings_type, + blink::WebFeaturePolicyFeature::kNotFound), weak_factory_ui_thread_(this) { DCHECK(content_settings_type == CONTENT_SETTINGS_TYPE_NOTIFICATIONS || content_settings_type == CONTENT_SETTINGS_TYPE_PUSH_MESSAGING);
diff --git a/chrome/browser/permissions/permission_context_base.cc b/chrome/browser/permissions/permission_context_base.cc index 35af53f..3f263c4 100644 --- a/chrome/browser/permissions/permission_context_base.cc +++ b/chrome/browser/permissions/permission_context_base.cc
@@ -35,6 +35,7 @@ #include "content/public/browser/browser_thread.h" #include "content/public/browser/render_frame_host.h" #include "content/public/browser/web_contents.h" +#include "content/public/common/content_features.h" #include "content/public/common/origin_util.h" #include "extensions/common/constants.h" #include "url/gurl.h" @@ -83,9 +84,11 @@ PermissionContextBase::PermissionContextBase( Profile* profile, - const ContentSettingsType content_settings_type) + ContentSettingsType content_settings_type, + blink::WebFeaturePolicyFeature feature_policy_feature) : profile_(profile), content_settings_type_(content_settings_type), + feature_policy_feature_(feature_policy_feature), weak_factory_(this) { #if defined(OS_ANDROID) permission_queue_controller_.reset( @@ -244,6 +247,14 @@ } } + // Check whether the feature is enabled for the frame by feature policy. We + // can only do this when a RenderFrameHost has been provided. + if (render_frame_host && + !PermissionAllowedByFeaturePolicy(render_frame_host)) { + return PermissionResult(CONTENT_SETTING_BLOCK, + PermissionStatusSource::UNSPECIFIED); + } + ContentSetting content_setting = GetPermissionStatusInternal( render_frame_host, requesting_origin, embedding_origin); if (content_setting == CONTENT_SETTING_ASK) { @@ -463,3 +474,18 @@ return CONTENT_SETTINGS_TYPE_NOTIFICATIONS; return content_settings_type_; } + +bool PermissionContextBase::PermissionAllowedByFeaturePolicy( + content::RenderFrameHost* rfh) const { + if (!base::FeatureList::IsEnabled( + features::kUseFeaturePolicyForPermissions)) { + // Default to ignoring the feature policy. + return true; + } + + // Some features don't have an associated feature policy yet. Allow those. + if (feature_policy_feature_ == blink::WebFeaturePolicyFeature::kNotFound) + return true; + + return rfh->IsFeatureEnabled(feature_policy_feature_); +}
diff --git a/chrome/browser/permissions/permission_context_base.h b/chrome/browser/permissions/permission_context_base.h index 72ef40ee..e50a900b 100644 --- a/chrome/browser/permissions/permission_context_base.h +++ b/chrome/browser/permissions/permission_context_base.h
@@ -16,6 +16,7 @@ #include "components/content_settings/core/common/content_settings.h" #include "components/content_settings/core/common/content_settings_types.h" #include "components/keyed_service/core/keyed_service.h" +#include "third_party/WebKit/public/platform/WebFeaturePolicyFeature.h" #if defined(OS_ANDROID) class PermissionQueueController; @@ -58,7 +59,8 @@ class PermissionContextBase : public KeyedService { public: PermissionContextBase(Profile* profile, - const ContentSettingsType content_settings_type); + ContentSettingsType content_settings_type, + blink::WebFeaturePolicyFeature feature_policy_feature); ~PermissionContextBase() override; // A field trial used to enable the global permissions kill switch. @@ -180,6 +182,8 @@ private: friend class PermissionContextBaseTests; + bool PermissionAllowedByFeaturePolicy(content::RenderFrameHost* rfh) const; + // Called when a request is no longer used so it can be cleaned up. void CleanUpRequest(const PermissionRequestID& id); @@ -204,6 +208,7 @@ Profile* profile_; const ContentSettingsType content_settings_type_; + const blink::WebFeaturePolicyFeature feature_policy_feature_; #if defined(OS_ANDROID) std::unique_ptr<PermissionQueueController> permission_queue_controller_; #endif
diff --git a/chrome/browser/permissions/permission_context_base_feature_policy_unittest.cc b/chrome/browser/permissions/permission_context_base_feature_policy_unittest.cc new file mode 100644 index 0000000..0029ba5 --- /dev/null +++ b/chrome/browser/permissions/permission_context_base_feature_policy_unittest.cc
@@ -0,0 +1,178 @@ +// Copyright 2017 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 "base/test/scoped_feature_list.h" +#include "chrome/browser/geolocation/geolocation_permission_context.h" +#include "chrome/browser/media/midi_permission_context.h" +#include "chrome/browser/notifications/notification_permission_context.h" +#include "chrome/browser/profiles/profile.h" +#include "chrome/test/base/chrome_render_view_host_test_harness.h" +#include "chrome/test/base/testing_profile.h" +#include "content/public/browser/render_frame_host.h" +#include "content/public/browser/web_contents.h" +#include "content/public/common/content_features.h" +#include "content/public/test/navigation_simulator.h" +#include "content/public/test/test_renderer_host.h" +#include "third_party/WebKit/public/platform/WebFeaturePolicyFeature.h" +#include "url/gurl.h" +#include "url/origin.h" + +// Integration tests for querying permissions that have a feature policy set. +// These tests are not meant to cover every edge case as the FeaturePolicy class +// itself is tested thoroughly in feature_policy_unittest.cc and in +// render_frame_host_feature_policy_unittest.cc. Instead they are meant to +// ensure that integration with content::PermissionContextBase works correctly. +class PermissionContextBaseFeaturePolicyTest + : public ChromeRenderViewHostTestHarness { + public: + void SetUp() override { + ChromeRenderViewHostTestHarness::SetUp(); + feature_list_.InitAndEnableFeature( + features::kUseFeaturePolicyForPermissions); + } + + protected: + static constexpr const char* kOrigin1 = "https://google.com"; + static constexpr const char* kOrigin2 = "https://maps.google.com"; + + content::RenderFrameHost* GetMainRFH(const char* origin) { + content::RenderFrameHost* result = web_contents()->GetMainFrame(); + content::RenderFrameHostTester::For(result) + ->InitializeRenderFrameIfNeeded(); + SimulateNavigation(&result, GURL(origin)); + return result; + } + + content::RenderFrameHost* AddChildRFH(content::RenderFrameHost* parent, + const char* origin) { + content::RenderFrameHost* result = + content::RenderFrameHostTester::For(parent)->AppendChild(""); + content::RenderFrameHostTester::For(result) + ->InitializeRenderFrameIfNeeded(); + SimulateNavigation(&result, GURL(origin)); + return result; + } + + // The header policy should only be set once on page load, so we refresh the + // page to simulate that. + void RefreshPageAndSetHeaderPolicy(content::RenderFrameHost** rfh, + blink::WebFeaturePolicyFeature feature, + const std::vector<std::string>& origins) { + content::RenderFrameHost* current = *rfh; + SimulateNavigation(¤t, current->GetLastCommittedURL()); + std::vector<url::Origin> parsed_origins; + for (const std::string& origin : origins) + parsed_origins.push_back(url::Origin(GURL(origin))); + content::RenderFrameHostTester::For(current)->SimulateFeaturePolicyHeader( + feature, parsed_origins); + *rfh = current; + } + + ContentSetting GetPermissionForFrame(PermissionContextBase* pcb, + content::RenderFrameHost* rfh) { + return pcb + ->GetPermissionStatus( + rfh, rfh->GetLastCommittedURL(), + web_contents()->GetMainFrame()->GetLastCommittedURL()) + .content_setting; + } + + private: + void SimulateNavigation(content::RenderFrameHost** rfh, const GURL& url) { + auto navigation_simulator = + content::NavigationSimulator::CreateRendererInitiated(url, *rfh); + navigation_simulator->Commit(); + *rfh = navigation_simulator->GetFinalRenderFrameHost(); + } + + base::test::ScopedFeatureList feature_list_; +}; + +// Feature policy should be ignored when the kUseFeaturePolicyForPermissions +// feature is disabled. +TEST_F(PermissionContextBaseFeaturePolicyTest, FeatureDisabled) { + // Disable the feature. + base::test::ScopedFeatureList feature_list; + feature_list.Init(); + + content::RenderFrameHost* parent = GetMainRFH(kOrigin1); + + RefreshPageAndSetHeaderPolicy(&parent, + blink::WebFeaturePolicyFeature::kMidiFeature, + std::vector<std::string>()); + MidiPermissionContext midi(profile()); + EXPECT_EQ(CONTENT_SETTING_ALLOW, GetPermissionForFrame(&midi, parent)); + + RefreshPageAndSetHeaderPolicy(&parent, + blink::WebFeaturePolicyFeature::kGeolocation, + std::vector<std::string>()); + GeolocationPermissionContext geolocation(profile()); + EXPECT_EQ(CONTENT_SETTING_ASK, GetPermissionForFrame(&geolocation, parent)); +} + +TEST_F(PermissionContextBaseFeaturePolicyTest, DefaultPolicy) { + content::RenderFrameHost* parent = GetMainRFH(kOrigin1); + content::RenderFrameHost* child = AddChildRFH(parent, kOrigin2); + + // Midi is allowed by default in the top level frame but not in subframes. + MidiPermissionContext midi(profile()); + EXPECT_EQ(CONTENT_SETTING_ALLOW, GetPermissionForFrame(&midi, parent)); + EXPECT_EQ(CONTENT_SETTING_BLOCK, GetPermissionForFrame(&midi, child)); + + // Geolocation is ask by default in top level frames but not in subframes. + GeolocationPermissionContext geolocation(profile()); + EXPECT_EQ(CONTENT_SETTING_ASK, GetPermissionForFrame(&geolocation, parent)); + EXPECT_EQ(CONTENT_SETTING_BLOCK, GetPermissionForFrame(&geolocation, child)); + + // Notifications doesn't have an associated feature policy so it should be ask + // by default in top level and subframes. + NotificationPermissionContext notifications( + profile(), CONTENT_SETTINGS_TYPE_NOTIFICATIONS); + EXPECT_EQ(CONTENT_SETTING_ASK, GetPermissionForFrame(¬ifications, parent)); + EXPECT_EQ(CONTENT_SETTING_ASK, GetPermissionForFrame(¬ifications, child)); +} + +TEST_F(PermissionContextBaseFeaturePolicyTest, DisabledTopLevelFrame) { + content::RenderFrameHost* parent = GetMainRFH(kOrigin1); + + // Disable midi in the top level frame. + RefreshPageAndSetHeaderPolicy(&parent, + blink::WebFeaturePolicyFeature::kMidiFeature, + std::vector<std::string>()); + content::RenderFrameHost* child = AddChildRFH(parent, kOrigin2); + MidiPermissionContext midi(profile()); + EXPECT_EQ(CONTENT_SETTING_BLOCK, GetPermissionForFrame(&midi, parent)); + EXPECT_EQ(CONTENT_SETTING_BLOCK, GetPermissionForFrame(&midi, child)); + + // Disable geolocation in the top level frame. + RefreshPageAndSetHeaderPolicy(&parent, + blink::WebFeaturePolicyFeature::kGeolocation, + std::vector<std::string>()); + child = AddChildRFH(parent, kOrigin2); + GeolocationPermissionContext geolocation(profile()); + EXPECT_EQ(CONTENT_SETTING_BLOCK, GetPermissionForFrame(&geolocation, parent)); + EXPECT_EQ(CONTENT_SETTING_BLOCK, GetPermissionForFrame(&geolocation, child)); +} + +TEST_F(PermissionContextBaseFeaturePolicyTest, EnabledForChildFrame) { + content::RenderFrameHost* parent = GetMainRFH(kOrigin1); + + // Enable midi for the child frame. + RefreshPageAndSetHeaderPolicy(&parent, + blink::WebFeaturePolicyFeature::kMidiFeature, + {kOrigin1, kOrigin2}); + content::RenderFrameHost* child = AddChildRFH(parent, kOrigin2); + MidiPermissionContext midi(profile()); + EXPECT_EQ(CONTENT_SETTING_ALLOW, GetPermissionForFrame(&midi, parent)); + EXPECT_EQ(CONTENT_SETTING_ALLOW, GetPermissionForFrame(&midi, child)); + + // Enable geolocation for the child frame. + RefreshPageAndSetHeaderPolicy(&parent, + blink::WebFeaturePolicyFeature::kGeolocation, + {kOrigin1, kOrigin2}); + child = AddChildRFH(parent, kOrigin2); + GeolocationPermissionContext geolocation(profile()); + EXPECT_EQ(CONTENT_SETTING_ASK, GetPermissionForFrame(&geolocation, parent)); + EXPECT_EQ(CONTENT_SETTING_ASK, GetPermissionForFrame(&geolocation, child)); +}
diff --git a/chrome/browser/permissions/permission_context_base_unittest.cc b/chrome/browser/permissions/permission_context_base_unittest.cc index 655dcf2a..dd62e4a9 100644 --- a/chrome/browser/permissions/permission_context_base_unittest.cc +++ b/chrome/browser/permissions/permission_context_base_unittest.cc
@@ -102,7 +102,9 @@ public: TestPermissionContext(Profile* profile, const ContentSettingsType content_settings_type) - : PermissionContextBase(profile, content_settings_type), + : PermissionContextBase(profile, + content_settings_type, + blink::WebFeaturePolicyFeature::kNotFound), tab_context_updated_(false) {} ~TestPermissionContext() override {}
diff --git a/chrome/browser/permissions/permission_request_impl.cc b/chrome/browser/permissions/permission_request_impl.cc index 0d38dad..9d252f2 100644 --- a/chrome/browser/permissions/permission_request_impl.cc +++ b/chrome/browser/permissions/permission_request_impl.cc
@@ -172,3 +172,7 @@ const { return PermissionUtil::GetGestureType(has_gesture_); } + +ContentSettingsType PermissionRequestImpl::GetContentSettingsType() const { + return content_settings_type_; +}
diff --git a/chrome/browser/permissions/permission_request_impl.h b/chrome/browser/permissions/permission_request_impl.h index ea3eec0..035a5b6f 100644 --- a/chrome/browser/permissions/permission_request_impl.h +++ b/chrome/browser/permissions/permission_request_impl.h
@@ -49,6 +49,7 @@ bool ShouldShowPersistenceToggle() const override; PermissionRequestType GetPermissionRequestType() const override; PermissionRequestGestureType GetGestureType() const override; + ContentSettingsType GetContentSettingsType() const override; GURL request_origin_; ContentSettingsType content_settings_type_;
diff --git a/chrome/browser/plugins/flash_permission_context.cc b/chrome/browser/plugins/flash_permission_context.cc index 1fd0711..5ff9ed8 100644 --- a/chrome/browser/plugins/flash_permission_context.cc +++ b/chrome/browser/plugins/flash_permission_context.cc
@@ -32,7 +32,8 @@ FlashPermissionContext::FlashPermissionContext(Profile* profile) : PermissionContextBase(profile, - CONTENT_SETTINGS_TYPE_PLUGINS) {} + CONTENT_SETTINGS_TYPE_PLUGINS, + blink::WebFeaturePolicyFeature::kNotFound) {} FlashPermissionContext::~FlashPermissionContext() {}
diff --git a/chrome/browser/storage/durable_storage_permission_context.cc b/chrome/browser/storage/durable_storage_permission_context.cc index eb31debc..9b898071 100644 --- a/chrome/browser/storage/durable_storage_permission_context.cc +++ b/chrome/browser/storage/durable_storage_permission_context.cc
@@ -29,7 +29,8 @@ DurableStoragePermissionContext::DurableStoragePermissionContext( Profile* profile) : PermissionContextBase(profile, - CONTENT_SETTINGS_TYPE_DURABLE_STORAGE) {} + CONTENT_SETTINGS_TYPE_DURABLE_STORAGE, + blink::WebFeaturePolicyFeature::kNotFound) {} void DurableStoragePermissionContext::DecidePermission( content::WebContents* web_contents,
diff --git a/chrome/browser/ui/app_list/search_answer_web_contents_delegate.cc b/chrome/browser/ui/app_list/search_answer_web_contents_delegate.cc index a0f9487b..d9ba2e56 100644 --- a/chrome/browser/ui/app_list/search_answer_web_contents_delegate.cc +++ b/chrome/browser/ui/app_list/search_answer_web_contents_delegate.cc
@@ -21,7 +21,6 @@ #include "ui/app_list/app_list_features.h" #include "ui/app_list/app_list_model.h" #include "ui/app_list/search_box_model.h" -#include "ui/views/controls/webview/web_contents_set_background_color.h" #include "ui/views/controls/webview/webview.h" #include "ui/views/widget/widget.h" @@ -106,11 +105,6 @@ web_view_->SetFocusBehavior(views::View::FocusBehavior::NEVER); model->AddObserver(this); - - // Make the webview transparent since it's going to be shown on top of a - // highlightable button. - views::WebContentsSetBackgroundColor::CreateForWebContentsWithColor( - web_contents_.get(), SK_ColorTRANSPARENT); } SearchAnswerWebContentsDelegate::~SearchAnswerWebContentsDelegate() { @@ -174,7 +168,8 @@ IsCardSizeOk(pref_size) || features::IsAnswerCardDarkRunEnabled(); model_->SetSearchAnswerAvailable(is_card_size_ok_ && received_answer_ && !web_contents_->IsLoading()); - web_view_->SetPreferredSize(pref_size); + if (!features::IsAnswerCardDarkRunEnabled()) + web_view_->SetPreferredSize(pref_size); if (!answer_loaded_time_.is_null()) { UMA_HISTOGRAM_TIMES("SearchAnswer.ResizeAfterLoadTime", base::TimeTicks::Now() - answer_loaded_time_);
diff --git a/chrome/browser/ui/views/bookmarks/bookmark_bubble_view.cc b/chrome/browser/ui/views/bookmarks/bookmark_bubble_view.cc index a7dbcb9..6c0ece3 100644 --- a/chrome/browser/ui/views/bookmarks/bookmark_bubble_view.cc +++ b/chrome/browser/ui/views/bookmarks/bookmark_bubble_view.cc
@@ -31,6 +31,7 @@ #include "ui/views/layout/fill_layout.h" #include "ui/views/layout/grid_layout.h" #include "ui/views/widget/widget.h" +#include "ui/views/window/dialog_client_view.h" #if defined(OS_WIN) #include "chrome/browser/sync/profile_sync_service_factory.h" @@ -42,8 +43,6 @@ using base::UserMetricsAction; using bookmarks::BookmarkModel; using bookmarks::BookmarkNode; -using views::ColumnSet; -using views::GridLayout; namespace { @@ -128,8 +127,17 @@ // ui::DialogModel ------------------------------------------------------------- int BookmarkBubbleView::GetDialogButtons() const { - // TODO(tapted): DialogClientView should manage the buttons. - return ui::DIALOG_BUTTON_NONE; + // TODO(tapted): DialogClientView should manage the ios promo buttons too. + return is_showing_ios_promotion_ + ? ui::DIALOG_BUTTON_NONE + : (ui::DIALOG_BUTTON_OK | ui::DIALOG_BUTTON_CANCEL); +} + +base::string16 BookmarkBubbleView::GetDialogButtonLabel( + ui::DialogButton button) const { + return l10n_util::GetStringUTF16((button == ui::DIALOG_BUTTON_OK) + ? IDS_DONE + : IDS_BOOKMARK_BUBBLE_REMOVE_BOOKMARK); } // views::WidgetDelegate ------------------------------------------------------- @@ -178,6 +186,19 @@ // views::DialogDelegate ------------------------------------------------------- +views::View* BookmarkBubbleView::CreateExtraView() { + edit_button_ = views::MdTextButton::CreateSecondaryUiButton( + this, l10n_util::GetStringUTF16(IDS_BOOKMARK_BUBBLE_OPTIONS)); + edit_button_->AddAccelerator(ui::Accelerator(ui::VKEY_E, ui::EF_ALT_DOWN)); + return edit_button_; +} + +bool BookmarkBubbleView::GetExtraViewPadding(int* padding) { + *padding = ChromeLayoutProvider::Get()->GetDistanceMetric( + DISTANCE_UNRELATED_CONTROL_HORIZONTAL_LARGE); + return true; +} + views::View* BookmarkBubbleView::CreateFootnoteView() { #if defined(OS_WIN) if (!is_showing_ios_promotion_ && @@ -198,31 +219,43 @@ return footnote_view_; } +bool BookmarkBubbleView::Cancel() { + base::RecordAction(UserMetricsAction("BookmarkBubble_Unstar")); + // Set this so we remove the bookmark after the window closes. + remove_bookmark_ = true; + apply_edits_ = false; + return true; +} + +bool BookmarkBubbleView::Accept() { +#if defined(OS_WIN) + using desktop_ios_promotion::PromotionEntryPoint; + if (IsIOSPromotionEligible(PromotionEntryPoint::BOOKMARKS_BUBBLE)) { + ShowIOSPromotion(PromotionEntryPoint::BOOKMARKS_BUBBLE); + return false; + } +#endif + return true; +} + +bool BookmarkBubbleView::Close() { + // Allow closing when activation lost. Default would call Accept(). + return true; +} + +void BookmarkBubbleView::UpdateButton(views::LabelButton* button, + ui::DialogButton type) { + LocationBarBubbleDelegateView::UpdateButton(button, type); + if (type == ui::DIALOG_BUTTON_CANCEL) + button->AddAccelerator(ui::Accelerator(ui::VKEY_R, ui::EF_ALT_DOWN)); +} + // views::View ----------------------------------------------------------------- const char* BookmarkBubbleView::GetClassName() const { return "BookmarkBubbleView"; } -bool BookmarkBubbleView::AcceleratorPressed( - const ui::Accelerator& accelerator) { - ui::KeyboardCode key_code = accelerator.key_code(); - if (key_code == ui::VKEY_RETURN) { - HandleButtonPressed(save_button_); - return true; - } - if (key_code == ui::VKEY_E && accelerator.IsAltDown()) { - HandleButtonPressed(edit_button_); - return true; - } - if (key_code == ui::VKEY_R && accelerator.IsAltDown()) { - HandleButtonPressed(remove_button_); - return true; - } - - return LocationBarBubbleDelegateView::AcceleratorPressed(accelerator); -} - void BookmarkBubbleView::GetAccessibleNodeData(ui::AXNodeData* node_data) { LocationBarBubbleDelegateView::GetAccessibleNodeData(node_data); node_data->SetName(l10n_util::GetStringUTF8( @@ -234,7 +267,8 @@ void BookmarkBubbleView::ButtonPressed(views::Button* sender, const ui::Event& event) { - HandleButtonPressed(sender); + base::RecordAction(UserMetricsAction("BookmarkBubble_Edit")); + ShowEditor(); } // views::ComboboxListener ----------------------------------------------------- @@ -258,82 +292,54 @@ // views::BubbleDialogDelegateView --------------------------------------------- void BookmarkBubbleView::Init() { - remove_button_ = views::MdTextButton::CreateSecondaryUiButton( - this, l10n_util::GetStringUTF16(IDS_BOOKMARK_BUBBLE_REMOVE_BOOKMARK)); + using views::GridLayout; - edit_button_ = views::MdTextButton::CreateSecondaryUiButton( - this, l10n_util::GetStringUTF16(IDS_BOOKMARK_BUBBLE_OPTIONS)); - - save_button_ = views::MdTextButton::CreateSecondaryUiButton( - this, l10n_util::GetStringUTF16(IDS_DONE)); - save_button_->SetIsDefault(true); - - views::Label* combobox_label = new views::Label( - l10n_util::GetStringUTF16(IDS_BOOKMARK_BUBBLE_FOLDER_TEXT)); - - parent_combobox_ = new UnsizedCombobox(&parent_model_); - parent_combobox_->set_listener(this); - parent_combobox_->SetAccessibleName( - l10n_util::GetStringUTF16(IDS_BOOKMARK_AX_BUBBLE_FOLDER_TEXT)); - - SetLayoutManager(new views::FillLayout); + SetLayoutManager(new views::FillLayout()); bookmark_contents_view_ = new views::View(); GridLayout* layout = new GridLayout(bookmark_contents_view_); bookmark_contents_view_->SetLayoutManager(layout); - // This column set is used for the labels and textfields as well as the - // buttons at the bottom. - const int cs_id = 0; - ColumnSet* cs = layout->AddColumnSet(cs_id); + // This column set is used for the labels and textfields. + constexpr int kColumnId = 0; + constexpr float kFixed = 0.f; + constexpr float kStretchy = 1.f; + views::ColumnSet* cs = layout->AddColumnSet(kColumnId); ChromeLayoutProvider* provider = ChromeLayoutProvider::Get(); - cs->AddColumn(provider->GetControlLabelGridAlignment(), GridLayout::CENTER, 0, - GridLayout::USE_PREF, 0, 0); - cs->AddPaddingColumn( - 0, provider->GetDistanceMetric(DISTANCE_UNRELATED_CONTROL_HORIZONTAL)); - - cs->AddColumn(GridLayout::FILL, GridLayout::CENTER, 0, GridLayout::USE_PREF, - 0, 0); - cs->AddPaddingColumn(1, provider->GetDistanceMetric( - DISTANCE_UNRELATED_CONTROL_HORIZONTAL_LARGE)); - - cs->AddColumn(GridLayout::LEADING, GridLayout::TRAILING, 0, - GridLayout::USE_PREF, 0, 0); - cs->AddPaddingColumn(0, provider->GetDistanceMetric( - views::DISTANCE_RELATED_BUTTON_HORIZONTAL)); - cs->AddColumn(GridLayout::LEADING, GridLayout::TRAILING, 0, + cs->AddColumn(provider->GetControlLabelGridAlignment(), GridLayout::CENTER, + kFixed, GridLayout::USE_PREF, 0, 0); + cs->AddPaddingColumn(kFixed, provider->GetDistanceMetric( + DISTANCE_UNRELATED_CONTROL_HORIZONTAL)); + cs->AddColumn(GridLayout::FILL, GridLayout::CENTER, kStretchy, GridLayout::USE_PREF, 0, 0); - layout->StartRow(0, cs_id); + layout->StartRow(kFixed, kColumnId); views::Label* label = new views::Label( - l10n_util::GetStringUTF16(IDS_BOOKMARK_BUBBLE_TITLE_TEXT)); + l10n_util::GetStringUTF16(IDS_BOOKMARK_BUBBLE_NAME_LABEL)); layout->AddView(label); + name_field_ = new views::Textfield(); name_field_->SetText(GetBookmarkName()); name_field_->SetAccessibleName( - l10n_util::GetStringUTF16(IDS_BOOKMARK_AX_BUBBLE_TITLE_TEXT)); + l10n_util::GetStringUTF16(IDS_BOOKMARK_AX_BUBBLE_NAME_LABEL)); + layout->AddView(name_field_); - layout->AddView(name_field_, 5, 1); - - layout->AddPaddingRow( - 0, provider->GetInsetsMetric(views::INSETS_DIALOG_CONTENTS).top()); - - layout->StartRow(0, cs_id); + layout->StartRowWithPadding( + kFixed, kColumnId, kFixed, + provider->GetDistanceMetric(views::DISTANCE_RELATED_CONTROL_VERTICAL)); + views::Label* combobox_label = new views::Label( + l10n_util::GetStringUTF16(IDS_BOOKMARK_BUBBLE_FOLDER_LABEL)); layout->AddView(combobox_label); - layout->AddView(parent_combobox_, 5, 1); + + parent_combobox_ = new UnsizedCombobox(&parent_model_); + parent_combobox_->set_listener(this); + parent_combobox_->SetAccessibleName( + l10n_util::GetStringUTF16(IDS_BOOKMARK_AX_BUBBLE_FOLDER_LABEL)); + layout->AddView(parent_combobox_); layout->AddPaddingRow( - 0, provider->GetDistanceMetric(views::DISTANCE_RELATED_CONTROL_VERTICAL)); - - layout->StartRow(0, cs_id); - layout->SkipColumns(2); - layout->AddView(remove_button_); - layout->AddView(edit_button_); - layout->AddView(save_button_); - - AddAccelerator(ui::Accelerator(ui::VKEY_RETURN, ui::EF_NONE)); - AddAccelerator(ui::Accelerator(ui::VKEY_E, ui::EF_ALT_DOWN)); - AddAccelerator(ui::Accelerator(ui::VKEY_R, ui::EF_ALT_DOWN)); + kFixed, + provider->GetInsetsMetric(views::INSETS_DIALOG_CONTENTS).bottom()); AddChildView(bookmark_contents_view_); } @@ -355,17 +361,7 @@ newly_bookmarked_(newly_bookmarked), parent_model_(BookmarkModelFactory::GetForBrowserContext(profile_), BookmarkModelFactory::GetForBrowserContext(profile_) - ->GetMostRecentlyAddedUserNodeForURL(url)), - remove_button_(nullptr), - edit_button_(nullptr), - save_button_(nullptr), - name_field_(nullptr), - parent_combobox_(nullptr), - ios_promo_view_(nullptr), - footnote_view_(nullptr), - remove_bookmark_(false), - apply_edits_(true), - is_showing_ios_promotion_(false) { + ->GetMostRecentlyAddedUserNodeForURL(url)) { chrome::RecordDialogCreation(chrome::DialogIdentifier::BOOKMARK); } @@ -381,32 +377,6 @@ return base::string16(); } -void BookmarkBubbleView::HandleButtonPressed(views::Button* sender) { - if (sender == remove_button_) { - base::RecordAction(UserMetricsAction("BookmarkBubble_Unstar")); - // Set this so we remove the bookmark after the window closes. - remove_bookmark_ = true; - apply_edits_ = false; - GetWidget()->Close(); - } else if (sender == edit_button_) { - base::RecordAction(UserMetricsAction("BookmarkBubble_Edit")); - ShowEditor(); - } else { - DCHECK_EQ(save_button_, sender); -#if defined(OS_WIN) - if (IsIOSPromotionEligible( - desktop_ios_promotion::PromotionEntryPoint::BOOKMARKS_BUBBLE)) { - ShowIOSPromotion( - desktop_ios_promotion::PromotionEntryPoint::BOOKMARKS_BUBBLE); - } else { - GetWidget()->Close(); - } -#else - GetWidget()->Close(); -#endif - } -} - void BookmarkBubbleView::ShowEditor() { const BookmarkNode* node = BookmarkModelFactory::GetForBrowserContext(profile_) @@ -457,6 +427,7 @@ void BookmarkBubbleView::ShowIOSPromotion( desktop_ios_promotion::PromotionEntryPoint entry_point) { DCHECK(!is_showing_ios_promotion_); + edit_button_->SetVisible(false); // Hide the contents, but don't delete. Its child views are accessed in the // destructor if there are edits to apply. bookmark_contents_view_->SetVisible(false); @@ -467,7 +438,7 @@ AddChildView(ios_promo_view_); GetWidget()->UpdateWindowIcon(); GetWidget()->UpdateWindowTitle(); - // Resize the bubble so it has the same width as the parent bubble. - ios_promo_view_->UpdateBubbleHeight(); + GetDialogClientView()->UpdateDialogButtons(); + SizeToContents(); } #endif
diff --git a/chrome/browser/ui/views/bookmarks/bookmark_bubble_view.h b/chrome/browser/ui/views/bookmarks/bookmark_bubble_view.h index 0f279d4..8adbc07 100644 --- a/chrome/browser/ui/views/bookmarks/bookmark_bubble_view.h +++ b/chrome/browser/ui/views/bookmarks/bookmark_bubble_view.h
@@ -66,16 +66,22 @@ ~BookmarkBubbleView() override; - // views::LocationBarBubbleDelegateView: + // LocationBarBubbleDelegateView: int GetDialogButtons() const override; + base::string16 GetDialogButtonLabel(ui::DialogButton button) const override; View* GetInitiallyFocusedView() override; base::string16 GetWindowTitle() const override; gfx::ImageSkia GetWindowIcon() override; bool ShouldShowWindowIcon() const override; void WindowClosing() override; - View* CreateFootnoteView() override; + views::View* CreateExtraView() override; + bool GetExtraViewPadding(int* padding) override; + views::View* CreateFootnoteView() override; + bool Cancel() override; + bool Accept() override; + bool Close() override; + void UpdateButton(views::LabelButton* button, ui::DialogButton type) override; const char* GetClassName() const override; - bool AcceleratorPressed(const ui::Accelerator& accelerator) override; void GetAccessibleNodeData(ui::AXNodeData* node_data) override; // views::ButtonListener: @@ -88,7 +94,7 @@ void OnIOSPromotionFootnoteLinkClicked() override; protected: - // views::LocationBarBubbleDelegateView: + // LocationBarBubbleDelegateView: void Init() override; private: @@ -106,9 +112,6 @@ // Returns the name of the bookmark. base::string16 GetBookmarkName(); - // Closes the bubble, opens the edit dialog, or shows the iOS promo. - void HandleButtonPressed(views::Button* sender); - // Shows the BookmarkEditor. void ShowEditor(); @@ -144,40 +147,34 @@ RecentlyUsedFoldersComboModel parent_model_; - // Button for removing the bookmark. - views::LabelButton* remove_button_; - // Button to bring up the editor. - views::LabelButton* edit_button_; - - // Button to save the bookmark. - views::LabelButton* save_button_; + views::LabelButton* edit_button_ = nullptr; // Textfield showing the name of the bookmark. - views::Textfield* name_field_; + views::Textfield* name_field_ = nullptr; // Combobox showing a handful of folders the user can choose from, including // the current parent. - views::Combobox* parent_combobox_; + views::Combobox* parent_combobox_ = nullptr; // The regular bookmark bubble contents, with all the edit fields and dialog // buttons. TODO(tapted): Move the buttons to the DialogClientView. - views::View* bookmark_contents_view_; + views::View* bookmark_contents_view_ = nullptr; // iOS promotion view. - DesktopIOSPromotionBubbleView* ios_promo_view_; + DesktopIOSPromotionBubbleView* ios_promo_view_ = nullptr; // Footnote view. - views::View* footnote_view_; + views::View* footnote_view_ = nullptr; // When the destructor is invoked should the bookmark be removed? - bool remove_bookmark_; + bool remove_bookmark_ = false; // When the destructor is invoked should edits be applied? - bool apply_edits_; + bool apply_edits_ = true; // Whether the Windows to iOS promotion is shown to the user. - bool is_showing_ios_promotion_; + bool is_showing_ios_promotion_ = false; DISALLOW_COPY_AND_ASSIGN(BookmarkBubbleView); };
diff --git a/chrome/browser/ui/views/bookmarks/bookmark_bubble_view_browsertest.cc b/chrome/browser/ui/views/bookmarks/bookmark_bubble_view_browsertest.cc index bbd98c4..2de79b0 100644 --- a/chrome/browser/ui/views/bookmarks/bookmark_bubble_view_browsertest.cc +++ b/chrome/browser/ui/views/bookmarks/bookmark_bubble_view_browsertest.cc
@@ -17,6 +17,7 @@ #include "components/bookmarks/browser/bookmark_model.h" #include "components/bookmarks/browser/bookmark_utils.h" #include "components/bookmarks/test/bookmark_test_helpers.h" +#include "ui/views/window/dialog_client_view.h" namespace { @@ -68,8 +69,11 @@ nullptr, nullptr, nullptr, profile_.get(), GURL(kTestBookmarkURL), true); if (name == "ios_promotion") { - BookmarkBubbleView::bookmark_bubble()->HandleButtonPressed( - BookmarkBubbleView::bookmark_bubble()->save_button_); + BookmarkBubbleView::bookmark_bubble() + ->GetWidget() + ->client_view() + ->AsDialogClientView() + ->AcceptWindow(); } }
diff --git a/chrome/browser/ui/views/desktop_ios_promotion/desktop_ios_promotion_bubble_view.cc b/chrome/browser/ui/views/desktop_ios_promotion/desktop_ios_promotion_bubble_view.cc index f95e3f2..0dbee7b 100644 --- a/chrome/browser/ui/views/desktop_ios_promotion/desktop_ios_promotion_bubble_view.cc +++ b/chrome/browser/ui/views/desktop_ios_promotion/desktop_ios_promotion_bubble_view.cc
@@ -96,19 +96,16 @@ GetWidget()->Close(); } -void DesktopIOSPromotionBubbleView::UpdateBubbleHeight() { - gfx::Rect old_bounds = GetWidget()->GetWindowBoundsInScreen(); - old_bounds.set_height( - GetWidget()->GetRootView()->GetHeightForWidth(old_bounds.width())); - GetWidget()->SetBounds(old_bounds); -} - void DesktopIOSPromotionBubbleView::UpdateRecoveryPhoneLabel() { std::string number = promotion_controller_->GetUsersRecoveryPhoneNumber(); if (!number.empty()) { promotion_text_label_->SetText(desktop_ios_promotion::GetPromoText( promotion_controller_->entry_point(), number)); Layout(); - UpdateBubbleHeight(); + views::Widget* widget = GetWidget(); + gfx::Rect old_bounds = widget->GetWindowBoundsInScreen(); + old_bounds.set_height( + widget->GetRootView()->GetHeightForWidth(old_bounds.width())); + widget->SetBounds(old_bounds); } }
diff --git a/chrome/browser/ui/views/desktop_ios_promotion/desktop_ios_promotion_bubble_view.h b/chrome/browser/ui/views/desktop_ios_promotion/desktop_ios_promotion_bubble_view.h index d1084c7b..7fc48fb 100644 --- a/chrome/browser/ui/views/desktop_ios_promotion/desktop_ios_promotion_bubble_view.h +++ b/chrome/browser/ui/views/desktop_ios_promotion/desktop_ios_promotion_bubble_view.h
@@ -28,9 +28,6 @@ desktop_ios_promotion::PromotionEntryPoint entry_point); ~DesktopIOSPromotionBubbleView() override; - // Update Bubble Height to fit the content. - void UpdateBubbleHeight(); - // DesktopIOSPromotionView: void UpdateRecoveryPhoneLabel() override;
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn index cd80d9b..627c81ce 100644 --- a/chrome/test/BUILD.gn +++ b/chrome/test/BUILD.gn
@@ -3171,6 +3171,7 @@ "../browser/password_manager/password_store_win_unittest.cc", "../browser/password_manager/password_store_x_unittest.cc", "../browser/permissions/chooser_context_base_unittest.cc", + "../browser/permissions/permission_context_base_feature_policy_unittest.cc", "../browser/permissions/permission_context_base_unittest.cc", "../browser/permissions/permission_decision_auto_blocker_unittest.cc", "../browser/permissions/permission_manager_unittest.cc",
diff --git a/components/security_state/core/security_state.cc b/components/security_state/core/security_state.cc index aed4e42..a958bf3 100644 --- a/components/security_state/core/security_state.cc +++ b/components/security_state/core/security_state.cc
@@ -17,12 +17,15 @@ namespace { -// Do not change or reorder this enum, and add new values at the end. It is used -// in the MarkHttpAs histogram. +// These values are written to logs. New enum values can be added, but existing +// enums must never be renumbered or deleted and reused. enum MarkHttpStatus { - NEUTRAL /* deprecated */, - NON_SECURE, - HTTP_SHOW_WARNING_ON_SENSITIVE_FIELDS, + NEUTRAL = 0, // Deprecated + NON_SECURE = 1, + HTTP_SHOW_WARNING_ON_SENSITIVE_FIELDS = 2, + NON_SECURE_AFTER_EDITING = 3, + NON_SECURE_WHILE_INCOGNITO = 4, + NON_SECURE_WHILE_INCOGNITO_OR_EDITING = 5, LAST_STATUS };
diff --git a/components/security_state/core/switches.cc b/components/security_state/core/switches.cc index c8542f8..dddd8ae 100644 --- a/components/security_state/core/switches.cc +++ b/components/security_state/core/switches.cc
@@ -10,6 +10,9 @@ // Use to opt-in to marking HTTP as non-secure. const char kMarkHttpAs[] = "mark-non-secure-as"; const char kMarkHttpAsDangerous[] = "non-secure"; - +const char kMarkHttpAsNonSecureAfterEditing[] = "non-secure-after-editing"; +const char kMarkHttpAsNonSecureWhileIncognito[] = "non-secure-while-incognito"; +const char kMarkHttpAsNonSecureWhileIncognitoOrEditing[] = + "non-secure-while-incognito-or-editing"; } // namespace switches } // namespace security_state
diff --git a/components/security_state/core/switches.h b/components/security_state/core/switches.h index 4216cd2..df27238 100644 --- a/components/security_state/core/switches.h +++ b/components/security_state/core/switches.h
@@ -10,6 +10,9 @@ extern const char kMarkHttpAs[]; extern const char kMarkHttpAsDangerous[]; +extern const char kMarkHttpAsNonSecureAfterEditing[]; +extern const char kMarkHttpAsNonSecureWhileIncognito[]; +extern const char kMarkHttpAsNonSecureWhileIncognitoOrEditing[]; } } // namespace security_state
diff --git a/content/browser/accessibility/browser_accessibility.cc b/content/browser/accessibility/browser_accessibility.cc index 00b6605e..b6f603b9 100644 --- a/content/browser/accessibility/browser_accessibility.cc +++ b/content/browser/accessibility/browser_accessibility.cc
@@ -1218,6 +1218,11 @@ return true; } + if (data.action == ui::AX_ACTION_FOCUS) { + manager_->SetFocus(*this); + return true; + } + return false; }
diff --git a/content/browser/accessibility/browser_accessibility_com_win.cc b/content/browser/accessibility/browser_accessibility_com_win.cc index 69a6ed8..ecd86fa 100644 --- a/content/browser/accessibility/browser_accessibility_com_win.cc +++ b/content/browser/accessibility/browser_accessibility_com_win.cc
@@ -780,16 +780,7 @@ if (!owner()) return E_FAIL; - auto* manager = Manager(); - if (!manager) - return E_FAIL; - - if (flags_sel & SELFLAG_TAKEFOCUS) { - manager->SetFocus(*owner()); - return S_OK; - } - - return S_FALSE; + return AXPlatformNodeWin::accSelect(flags_sel, var_id); } STDMETHODIMP
diff --git a/content/browser/battery_monitor_browsertest.cc b/content/browser/battery_monitor_browsertest.cc index c4bf62c..9977dea2 100644 --- a/content/browser/battery_monitor_browsertest.cc +++ b/content/browser/battery_monitor_browsertest.cc
@@ -44,22 +44,20 @@ private: // mojom::BatteryMonitor methods: - void QueryNextStatus(const QueryNextStatusCallback& callback) override { + void QueryNextStatus(QueryNextStatusCallback callback) override { if (!callback_.is_null()) { DVLOG(1) << "Overlapped call to QueryNextStatus!"; binding_.Close(); return; } - callback_ = callback; + callback_ = std::move(callback); if (status_to_report_) ReportStatus(); } void ReportStatus() { - callback_.Run(status_.Clone()); - callback_.Reset(); - + std::move(callback_).Run(status_.Clone()); status_to_report_ = false; }
diff --git a/content/browser/gpu/browser_gpu_memory_buffer_manager.cc b/content/browser/gpu/browser_gpu_memory_buffer_manager.cc index aab62d8..099367d7 100644 --- a/content/browser/gpu/browser_gpu_memory_buffer_manager.cc +++ b/content/browser/gpu/browser_gpu_memory_buffer_manager.cc
@@ -457,8 +457,6 @@ client_id); } -BrowserGpuMemoryBufferManager::BufferInfo::BufferInfo() = default; - BrowserGpuMemoryBufferManager::BufferInfo::BufferInfo( const gfx::Size& size, gfx::GpuMemoryBufferType type,
diff --git a/content/browser/gpu/browser_gpu_memory_buffer_manager.h b/content/browser/gpu/browser_gpu_memory_buffer_manager.h index a6468d77..b12409d 100644 --- a/content/browser/gpu/browser_gpu_memory_buffer_manager.h +++ b/content/browser/gpu/browser_gpu_memory_buffer_manager.h
@@ -69,7 +69,6 @@ private: struct BufferInfo { - BufferInfo(); BufferInfo(const gfx::Size& size, gfx::GpuMemoryBufferType type, gfx::BufferFormat format,
diff --git a/content/browser/loader/resource_dispatcher_host_impl.cc b/content/browser/loader/resource_dispatcher_host_impl.cc index 849b8ee..f29936f 100644 --- a/content/browser/loader/resource_dispatcher_host_impl.cc +++ b/content/browser/loader/resource_dispatcher_host_impl.cc
@@ -2022,9 +2022,6 @@ !is_external_protocol; if (is_shutdown_ || non_web_url_in_guest || - // TODO(davidben): Check ShouldServiceRequest here. This is important; it - // needs to be checked relative to the child that /requested/ the - // navigation. It's where file upload checks, etc., come in. (delegate_ && !delegate_->ShouldBeginRequest( info.common_params.method, info.common_params.url,
diff --git a/content/browser/permissions/permission_service_context.h b/content/browser/permissions/permission_service_context.h index 347808e..d08b8ed1 100644 --- a/content/browser/permissions/permission_service_context.h +++ b/content/browser/permissions/permission_service_context.h
@@ -6,6 +6,7 @@ #define CONTENT_BROWSER_PERMISSIONS_PERMISSION_SERVICE_CONTEXT_H_ #include "base/macros.h" +#include "content/common/content_export.h" #include "content/public/browser/permission_type.h" #include "content/public/browser/web_contents_observer.h" #include "mojo/public/cpp/bindings/strong_binding_set.h" @@ -29,7 +30,7 @@ // There is one PermissionServiceContext per RenderFrameHost/RenderProcessHost // which owns it. It then owns all PermissionServiceImpl associated to their // owner. -class PermissionServiceContext : public WebContentsObserver { +class CONTENT_EXPORT PermissionServiceContext : public WebContentsObserver { public: explicit PermissionServiceContext(RenderFrameHost* render_frame_host); explicit PermissionServiceContext(RenderProcessHost* render_process_host);
diff --git a/content/browser/permissions/permission_service_impl.cc b/content/browser/permissions/permission_service_impl.cc index a62913c..686bb31 100644 --- a/content/browser/permissions/permission_service_impl.cc +++ b/content/browser/permissions/permission_service_impl.cc
@@ -10,10 +10,14 @@ #include <utility> #include "base/bind.h" +#include "base/feature_list.h" #include "base/memory/ptr_util.h" #include "content/public/browser/browser_context.h" #include "content/public/browser/permission_manager.h" #include "content/public/browser/permission_type.h" +#include "content/public/browser/render_frame_host.h" +#include "content/public/common/content_features.h" +#include "third_party/WebKit/public/platform/WebFeaturePolicy.h" using blink::mojom::PermissionDescriptorPtr; using blink::mojom::PermissionName; @@ -56,8 +60,56 @@ return PermissionType::NUM; } -// This function allows the usage of the the multiple request map -// with single requests. +blink::WebFeaturePolicyFeature PermissionTypeToFeaturePolicyFeature( + PermissionType type) { + switch (type) { + case PermissionType::MIDI: + case PermissionType::MIDI_SYSEX: + return blink::WebFeaturePolicyFeature::kMidiFeature; + case PermissionType::GEOLOCATION: + return blink::WebFeaturePolicyFeature::kGeolocation; + case PermissionType::PROTECTED_MEDIA_IDENTIFIER: + return blink::WebFeaturePolicyFeature::kEme; + case PermissionType::AUDIO_CAPTURE: + return blink::WebFeaturePolicyFeature::kMicrophone; + case PermissionType::VIDEO_CAPTURE: + return blink::WebFeaturePolicyFeature::kCamera; + case PermissionType::PUSH_MESSAGING: + case PermissionType::NOTIFICATIONS: + case PermissionType::DURABLE_STORAGE: + case PermissionType::BACKGROUND_SYNC: + case PermissionType::FLASH: + case PermissionType::NUM: + // These aren't exposed by feature policy. + return blink::WebFeaturePolicyFeature::kNotFound; + } + + NOTREACHED(); + return blink::WebFeaturePolicyFeature::kNotFound; +} + +bool AllowedByFeaturePolicy(RenderFrameHost* rfh, PermissionType type) { + if (!base::FeatureList::IsEnabled( + features::kUseFeaturePolicyForPermissions)) { + // Default to ignoring the feature policy. + return true; + } + + blink::WebFeaturePolicyFeature feature_policy_feature = + PermissionTypeToFeaturePolicyFeature(type); + if (feature_policy_feature == blink::WebFeaturePolicyFeature::kNotFound) + return true; + + // If there is no frame, there is no policy, so disable the feature for + // safety. + if (!rfh) + return false; + + return rfh->IsFeatureEnabled(feature_policy_feature); +} + +// This function allows the usage of the the multiple request map with single +// requests. void PermissionRequestResponseCallbackWrapper( const base::Callback<void(PermissionStatus)>& callback, const std::vector<PermissionStatus>& vector) { @@ -67,20 +119,56 @@ } // anonymous namespace -PermissionServiceImpl::PendingRequest::PendingRequest( - const RequestPermissionsCallback& callback, - int request_count) - : callback(callback), - request_count(request_count) { -} +class PermissionServiceImpl::PendingRequest { + public: + PendingRequest(std::vector<PermissionType> types, + const RequestPermissionsCallback& callback) + : types_(types), + callback_(callback), + has_result_been_set_(types.size(), false), + results_(types.size(), PermissionStatus::DENIED) {} -PermissionServiceImpl::PendingRequest::~PendingRequest() { - if (callback.is_null()) - return; + ~PendingRequest() { + if (callback_.is_null()) + return; - std::vector<PermissionStatus> result(request_count, PermissionStatus::DENIED); - callback.Run(result); -} + std::vector<PermissionStatus> result(types_.size(), + PermissionStatus::DENIED); + callback_.Run(result); + } + + int id() const { return id_; } + void set_id(int id) { id_ = id; } + + size_t RequestSize() const { return types_.size(); } + + void SetResult(int index, PermissionStatus result) { + DCHECK_EQ(false, has_result_been_set_[index]); + has_result_been_set_[index] = true; + results_[index] = result; + } + + bool HasResultBeenSet(size_t index) const { + return has_result_been_set_[index]; + } + + void RunCallback() { + // Check that all results have been set. + DCHECK(std::find(has_result_been_set_.begin(), has_result_been_set_.end(), + false) == has_result_been_set_.end()); + callback_.Run(results_); + callback_.Reset(); + } + + private: + // Request ID received from the PermissionManager. + int id_; + std::vector<PermissionType> types_; + RequestPermissionsCallback callback_; + + std::vector<bool> has_result_been_set_; + std::vector<PermissionStatus> results_; +}; PermissionServiceImpl::PermissionServiceImpl(PermissionServiceContext* context) : context_(context), weak_factory_(this) {} @@ -96,7 +184,7 @@ // Cancel pending requests. for (RequestsMap::Iterator<PendingRequest> it(&pending_requests_); !it.IsAtEnd(); it.Advance()) { - permission_manager->CancelPermissionRequest(it.GetCurrentValue()->id); + permission_manager->CancelPermissionRequest(it.GetCurrentValue()->id()); } pending_requests_.Clear(); } @@ -140,30 +228,53 @@ for (size_t i = 0; i < types.size(); ++i) types[i] = PermissionDescriptorToPermissionType(permissions[i]); - int pending_request_id = pending_requests_.Add( - base::MakeUnique<PendingRequest>(callback, permissions.size())); + std::unique_ptr<PendingRequest> pending_request = + base::MakeUnique<PendingRequest>(types, callback); + std::vector<PermissionType> request_types; + for (size_t i = 0; i < types.size(); ++i) { + // Check feature policy. + if (!AllowedByFeaturePolicy(context_->render_frame_host(), types[i])) + pending_request->SetResult(i, PermissionStatus::DENIED); + else + request_types.push_back(types[i]); + } + + int pending_request_id = pending_requests_.Add(std::move(pending_request)); int id = browser_context->GetPermissionManager()->RequestPermissions( - types, context_->render_frame_host(), origin.GetURL(), user_gesture, + request_types, context_->render_frame_host(), origin.GetURL(), + user_gesture, base::Bind(&PermissionServiceImpl::OnRequestPermissionsResponse, weak_factory_.GetWeakPtr(), pending_request_id)); // Check if the request still exists. It may have been removed by the // the response callback. - PendingRequest* pending_request = pending_requests_.Lookup( - pending_request_id); - if (!pending_request) - return; - pending_request->id = id; + PendingRequest* in_progress_request = + pending_requests_.Lookup(pending_request_id); + if (!in_progress_request) + return; + in_progress_request->set_id(id); } void PermissionServiceImpl::OnRequestPermissionsResponse( int pending_request_id, - const std::vector<PermissionStatus>& result) { + const std::vector<PermissionStatus>& partial_result) { PendingRequest* request = pending_requests_.Lookup(pending_request_id); - RequestPermissionsCallback callback(request->callback); - request->callback.Reset(); + auto partial_result_it = partial_result.begin(); + // Fill in the unset results in the request. Some results in the request are + // set synchronously because they are blocked by feature policy. Others are + // determined by a call to RequestPermission. All unset results will be + // contained in |partial_result| in the same order that they were requested. + // We fill in the unset results in the request with |partial_result|. + for (size_t i = 0; i < request->RequestSize(); ++i) { + if (!request->HasResultBeenSet(i)) { + request->SetResult(i, *partial_result_it); + ++partial_result_it; + } + } + DCHECK(partial_result.end() == partial_result_it); + + request->RunCallback(); pending_requests_.Remove(pending_request_id); - callback.Run(result); } void PermissionServiceImpl::HasPermission( @@ -221,8 +332,10 @@ const url::Origin& origin) { BrowserContext* browser_context = context_->GetBrowserContext(); DCHECK(browser_context); - if (!browser_context->GetPermissionManager()) + if (!browser_context->GetPermissionManager() || + !AllowedByFeaturePolicy(context_->render_frame_host(), type)) { return PermissionStatus::DENIED; + } GURL requesting_origin(origin.Serialize()); // If the embedding_origin is empty we'll use |origin| instead.
diff --git a/content/browser/permissions/permission_service_impl.h b/content/browser/permissions/permission_service_impl.h index 9d8430d..bb7023a8 100644 --- a/content/browser/permissions/permission_service_impl.h +++ b/content/browser/permissions/permission_service_impl.h
@@ -10,6 +10,7 @@ #include "base/macros.h" #include "base/memory/weak_ptr.h" #include "content/browser/permissions/permission_service_context.h" +#include "content/common/content_export.h" #include "third_party/WebKit/public/platform/modules/permissions/permission.mojom.h" #include "url/origin.h" @@ -24,25 +25,19 @@ // to have some information about the current context. That enables the service // to know whether it can show UI and have knowledge of the associated // WebContents for example. -class PermissionServiceImpl : public blink::mojom::PermissionService { +class CONTENT_EXPORT PermissionServiceImpl + : public blink::mojom::PermissionService { public: PermissionServiceImpl(PermissionServiceContext* context); ~PermissionServiceImpl() override; private: + friend class PermissionServiceImplTest; + using PermissionStatusCallback = base::Callback<void(blink::mojom::PermissionStatus)>; - struct PendingRequest { - PendingRequest(const RequestPermissionsCallback& callback, - int request_count); - ~PendingRequest(); - - // Request ID received from the PermissionManager. - int id; - RequestPermissionsCallback callback; - int request_count; - }; + class PendingRequest; using RequestsMap = IDMap<std::unique_ptr<PendingRequest>>; // blink::mojom::PermissionService.
diff --git a/content/browser/permissions/permission_service_impl_unittest.cc b/content/browser/permissions/permission_service_impl_unittest.cc new file mode 100644 index 0000000..33f59d2 --- /dev/null +++ b/content/browser/permissions/permission_service_impl_unittest.cc
@@ -0,0 +1,189 @@ +// Copyright 2017 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 "content/browser/permissions/permission_service_impl.h" + +#include "base/run_loop.h" +#include "base/test/scoped_feature_list.h" +#include "content/browser/permissions/permission_service_context.h" +#include "content/public/browser/web_contents.h" +#include "content/public/common/content_features.h" +#include "content/public/test/test_browser_context.h" +#include "content/public/test/test_renderer_host.h" +#include "content/test/mock_permission_manager.h" +#include "mojo/public/cpp/bindings/interface_request.h" +#include "third_party/WebKit/public/platform/WebFeaturePolicy.h" +#include "third_party/WebKit/public/platform/modules/permissions/permission.mojom.h" +#include "url/origin.h" + +using blink::mojom::PermissionStatus; +using blink::mojom::PermissionName; + +namespace content { + +namespace { + +blink::mojom::PermissionDescriptorPtr CreatePermissionDescriptor( + PermissionName name) { + auto descriptor = blink::mojom::PermissionDescriptor::New(); + descriptor->name = name; + return descriptor; +} + +class TestPermissionManager : public MockPermissionManager { + public: + ~TestPermissionManager() override = default; + + PermissionStatus GetPermissionStatus(PermissionType permission, + const GURL& requesting_origin, + const GURL& embedding_origin) override { + // Always return granted. + return PermissionStatus::GRANTED; + } + + int RequestPermissions( + const std::vector<PermissionType>& permissions, + RenderFrameHost* render_frame_host, + const GURL& requesting_origin, + bool user_gesture, + const base::Callback<void(const std::vector<PermissionStatus>&)>& + callback) override { + callback.Run(std::vector<PermissionStatus>(permissions.size(), + PermissionStatus::GRANTED)); + return 0; + } +}; + +} // namespace + +class PermissionServiceImplTest : public RenderViewHostTestHarness { + public: + PermissionServiceImplTest() : origin_(GURL("https://www.google.com")) {} + + void SetUp() override { + RenderViewHostTestHarness::SetUp(); + static_cast<TestBrowserContext*>(browser_context()) + ->SetPermissionManager(base::MakeUnique<TestPermissionManager>()); + NavigateAndCommit(origin_.GetURL()); + service_context_.reset(new PermissionServiceContext(main_rfh())); + service_impl_.reset(new PermissionServiceImpl(service_context_.get())); + } + + void TearDown() override { + service_impl_.reset(); + service_context_.reset(); + RenderViewHostTestHarness::TearDown(); + } + + protected: + // The header policy should only be set once on page load, so we refresh the + // page to simulate that. + void RefreshPageAndSetHeaderPolicy(blink::WebFeaturePolicyFeature feature, + bool enabled) { + NavigateAndCommit(origin_.GetURL()); + std::vector<url::Origin> whitelist; + if (enabled) + whitelist.push_back(origin_); + RenderFrameHostTester::For(main_rfh()) + ->SimulateFeaturePolicyHeader(feature, whitelist); + } + + PermissionStatus HasPermission(PermissionName permission) { + base::Callback<void(PermissionStatus)> callback = + base::Bind(&PermissionServiceImplTest::PermissionStatusCallback, + base::Unretained(this)); + service_impl_->HasPermission(CreatePermissionDescriptor(permission), + origin_, callback); + EXPECT_EQ(1u, last_permission_statuses_.size()); + return last_permission_statuses_[0]; + } + + std::vector<PermissionStatus> RequestPermissions( + const std::vector<PermissionName>& permissions) { + std::vector<blink::mojom::PermissionDescriptorPtr> descriptors; + for (PermissionName name : permissions) + descriptors.push_back(CreatePermissionDescriptor(name)); + base::Callback<void(const std::vector<PermissionStatus>&)> callback = + base::Bind(&PermissionServiceImplTest::RequestPermissionsCallback, + base::Unretained(this)); + service_impl_->RequestPermissions(std::move(descriptors), origin_, + /*user_gesture=*/false, callback); + EXPECT_EQ(permissions.size(), last_permission_statuses_.size()); + return last_permission_statuses_; + } + + private: + void PermissionStatusCallback(blink::mojom::PermissionStatus status) { + last_permission_statuses_ = std::vector<PermissionStatus>{status}; + } + + void RequestPermissionsCallback( + const std::vector<PermissionStatus>& statuses) { + last_permission_statuses_ = statuses; + } + + url::Origin origin_; + + base::Closure quit_closure_; + + std::vector<PermissionStatus> last_permission_statuses_; + + std::unique_ptr<PermissionServiceImpl> service_impl_; + std::unique_ptr<PermissionServiceContext> service_context_; +}; + +// Basic tests for feature policy checks through the PermissionService. These +// tests are not meant to cover every edge case as the FeaturePolicy class +// itself is tested thoroughly in feature_policy_unittest.cc and in +// render_frame_host_feature_policy_unittest.cc. +TEST_F(PermissionServiceImplTest, HasPermissionWithFeaturePolicy) { + base::test::ScopedFeatureList feature_list; + feature_list.InitAndEnableFeature(features::kUseFeaturePolicyForPermissions); + // Geolocation should be enabled by default for a frame (if permission is + // granted). + EXPECT_EQ(PermissionStatus::GRANTED, + HasPermission(PermissionName::GEOLOCATION)); + + RefreshPageAndSetHeaderPolicy(blink::WebFeaturePolicyFeature::kGeolocation, + /*enabled=*/false); + EXPECT_EQ(PermissionStatus::DENIED, + HasPermission(PermissionName::GEOLOCATION)); + + // Midi should be allowed even though geolocation was disabled. + EXPECT_EQ(PermissionStatus::GRANTED, HasPermission(PermissionName::MIDI)); + + // Now block midi. + RefreshPageAndSetHeaderPolicy(blink::WebFeaturePolicyFeature::kMidiFeature, + /*enabled=*/false); + EXPECT_EQ(PermissionStatus::DENIED, HasPermission(PermissionName::MIDI)); + + // Ensure that the policy is ignored if kUseFeaturePolicyForPermissions is + // disabled. + base::test::ScopedFeatureList empty_feature_list; + empty_feature_list.Init(); + EXPECT_EQ(PermissionStatus::GRANTED, HasPermission(PermissionName::MIDI)); +} + +TEST_F(PermissionServiceImplTest, RequestPermissionsWithFeaturePolicy) { + base::test::ScopedFeatureList feature_list; + feature_list.InitAndEnableFeature(features::kUseFeaturePolicyForPermissions); + + // Disable midi. + RefreshPageAndSetHeaderPolicy(blink::WebFeaturePolicyFeature::kMidiFeature, + /*enabled=*/false); + + std::vector<PermissionStatus> result = + RequestPermissions(std::vector<PermissionName>{PermissionName::MIDI}); + EXPECT_EQ(1u, result.size()); + EXPECT_EQ(PermissionStatus::DENIED, result[0]); + + // Request midi along with geolocation. Geolocation should be granted. + result = RequestPermissions(std::vector<PermissionName>{ + PermissionName::MIDI, PermissionName::GEOLOCATION}); + EXPECT_EQ(2u, result.size()); + EXPECT_EQ(PermissionStatus::DENIED, result[0]); + EXPECT_EQ(PermissionStatus::GRANTED, result[1]); +} + +} // namespace \ No newline at end of file
diff --git a/content/browser/renderer_host/input/input_router_impl_unittest.cc b/content/browser/renderer_host/input/input_router_impl_unittest.cc index 24a68af..21fde0e9 100644 --- a/content/browser/renderer_host/input/input_router_impl_unittest.cc +++ b/content/browser/renderer_host/input/input_router_impl_unittest.cc
@@ -1022,7 +1022,12 @@ #if defined(USE_AURA) // Tests that the acked events have correct state. (ui::Events are used only on // windows and aura) -TEST_F(InputRouterImplTest, AckedTouchEventState) { +#if defined(OS_LINUX) || defined(OS_WIN) || defined(OS_CHROMEOS) +#define MAYBE_AckedTouchEventState DISABLED_AckedTouchEventState +#else +#define MAYBE_AckedTouchEventState AckedTouchEventState +#endif +TEST_F(InputRouterImplTest, MAYBE_AckedTouchEventState) { input_router_->OnMessageReceived(ViewHostMsg_HasTouchEventHandlers(0, true)); EXPECT_EQ(0U, GetSentMessageCountAndResetSink()); EXPECT_TRUE(TouchEventQueueEmpty());
diff --git a/content/browser/service_worker/embedded_worker_test_helper.cc b/content/browser/service_worker/embedded_worker_test_helper.cc index 7ef395e1..d1bb82f 100644 --- a/content/browser/service_worker/embedded_worker_test_helper.cc +++ b/content/browser/service_worker/embedded_worker_test_helper.cc
@@ -575,14 +575,16 @@ int provider_id) { EmbeddedWorkerInstance* worker = registry()->GetWorker(embedded_worker_id); ASSERT_TRUE(worker); + ASSERT_TRUE(embedded_worker_id_instance_host_ptr_map_[embedded_worker_id]); + // Prepare a provider host to be used by following OnThreadStarted(). std::unique_ptr<ServiceWorkerProviderHost> host = CreateProviderHostForServiceWorkerContext( worker->process_id(), provider_id, true /* is_parent_frame_secure */, - context()->AsWeakPtr()); + context()->AsWeakPtr(), + &embedded_worker_id_remote_provider_map_[embedded_worker_id]); context()->AddProviderHost(std::move(host)); - ASSERT_TRUE(embedded_worker_id_instance_host_ptr_map_[embedded_worker_id]); embedded_worker_id_instance_host_ptr_map_[embedded_worker_id] ->OnThreadStarted(thread_id, provider_id); base::RunLoop().RunUntilIdle();
diff --git a/content/browser/service_worker/embedded_worker_test_helper.h b/content/browser/service_worker/embedded_worker_test_helper.h index 6bc5789..c19b0773 100644 --- a/content/browser/service_worker/embedded_worker_test_helper.h +++ b/content/browser/service_worker/embedded_worker_test_helper.h
@@ -18,6 +18,7 @@ #include "base/memory/weak_ptr.h" #include "base/optional.h" #include "base/time/time.h" +#include "content/browser/service_worker/service_worker_test_utils.h" #include "content/common/service_worker/embedded_worker.mojom.h" #include "content/common/service_worker/service_worker_event_dispatcher.mojom.h" #include "content/common/service_worker/service_worker_status_code.h" @@ -348,6 +349,8 @@ int /* embedded_worker_id */, mojom::EmbeddedWorkerInstanceHostAssociatedPtr /* instance_host_ptr */> embedded_worker_id_instance_host_ptr_map_; + std::map<int /* embedded_worker_id */, ServiceWorkerRemoteProviderEndpoint> + embedded_worker_id_remote_provider_map_; std::vector<Event> events_;
diff --git a/content/browser/service_worker/foreign_fetch_request_handler_unittest.cc b/content/browser/service_worker/foreign_fetch_request_handler_unittest.cc index f77f486..9ea84249 100644 --- a/content/browser/service_worker/foreign_fetch_request_handler_unittest.cc +++ b/content/browser/service_worker/foreign_fetch_request_handler_unittest.cc
@@ -157,10 +157,12 @@ } void CreateWindowTypeProviderHost() { + remote_endpoints_.emplace_back(); std::unique_ptr<ServiceWorkerProviderHost> host = CreateProviderHostForWindow( helper_->mock_render_process_id(), kMockProviderId, - true /* is_parent_frame_secure */, helper_->context()->AsWeakPtr()); + true /* is_parent_frame_secure */, helper_->context()->AsWeakPtr(), + &remote_endpoints_.back()); EXPECT_FALSE( context()->GetProviderHost(host->process_id(), host->provider_id())); host->SetDocumentUrl(GURL("https://host/scope/")); @@ -169,10 +171,12 @@ } void CreateServiceWorkerTypeProviderHost() { + remote_endpoints_.emplace_back(); std::unique_ptr<ServiceWorkerProviderHost> host = CreateProviderHostForServiceWorkerContext( helper_->mock_render_process_id(), kMockProviderId, - true /* is_parent_frame_secure */, helper_->context()->AsWeakPtr()); + true /* is_parent_frame_secure */, helper_->context()->AsWeakPtr(), + &remote_endpoints_.back()); EXPECT_FALSE( context()->GetProviderHost(host->process_id(), host->provider_id())); provider_host_ = host->AsWeakPtr(); @@ -235,6 +239,7 @@ base::WeakPtr<ServiceWorkerProviderHost> provider_host_; storage::BlobStorageContext blob_storage_context_; std::unique_ptr<net::URLRequest> request_; + std::vector<ServiceWorkerRemoteProviderEndpoint> remote_endpoints_; DISALLOW_COPY_AND_ASSIGN(ForeignFetchRequestHandlerTest); };
diff --git a/content/browser/service_worker/link_header_support_unittest.cc b/content/browser/service_worker/link_header_support_unittest.cc index ad8e2cd..d589fea 100644 --- a/content/browser/service_worker/link_header_support_unittest.cc +++ b/content/browser/service_worker/link_header_support_unittest.cc
@@ -79,10 +79,12 @@ void CreateDocumentProviderHost() { // An empty host. + remote_endpoints_.emplace_back(); std::unique_ptr<ServiceWorkerProviderHost> host = CreateProviderHostForWindow(render_process_id(), kMockProviderId, true /* is_parent_frame_secure */, - context()->AsWeakPtr()); + context()->AsWeakPtr(), + &remote_endpoints_.back()); provider_host_ = host->AsWeakPtr(); EXPECT_FALSE( context()->GetProviderHost(host->process_id(), host->provider_id())); @@ -91,10 +93,12 @@ void CreateInsecureDocumentProviderHost() { // An empty host. + remote_endpoints_.emplace_back(); std::unique_ptr<ServiceWorkerProviderHost> host = CreateProviderHostForWindow(render_process_id(), kMockProviderId, false /* is_parent_frame_secure */, - context()->AsWeakPtr()); + context()->AsWeakPtr(), + &remote_endpoints_.back()); provider_host_ = host->AsWeakPtr(); EXPECT_FALSE( context()->GetProviderHost(host->process_id(), host->provider_id())); @@ -102,10 +106,12 @@ } void CreateServiceWorkerProviderHost() { + remote_endpoints_.emplace_back(); std::unique_ptr<ServiceWorkerProviderHost> host = CreateProviderHostForServiceWorkerContext( render_process_id(), kMockProviderId, - true /* is_parent_frame_secure */, context()->AsWeakPtr()); + true /* is_parent_frame_secure */, context()->AsWeakPtr(), + &remote_endpoints_.back()); provider_host_ = host->AsWeakPtr(); EXPECT_FALSE( context()->GetProviderHost(host->process_id(), host->provider_id())); @@ -161,7 +167,7 @@ return registrations; } - private: + protected: TestBrowserThreadBundle thread_bundle_; std::unique_ptr<EmbeddedWorkerTestHelper> helper_; net::TestURLRequestContext request_context_; @@ -169,6 +175,7 @@ MockResourceContext resource_context_; base::WeakPtr<ServiceWorkerProviderHost> provider_host_; storage::BlobStorageContext blob_storage_context_; + std::vector<ServiceWorkerRemoteProviderEndpoint> remote_endpoints_; }; TEST_F(LinkHeaderServiceWorkerTest, InstallServiceWorker_Basic) { @@ -397,10 +404,12 @@ InstallServiceWorker_FromWorkerWithControllees) { CreateServiceWorkerProviderHost(); + remote_endpoints_.emplace_back(); std::unique_ptr<ServiceWorkerProviderHost> controllee = CreateProviderHostForWindow(render_process_id(), kMockProviderId, true /* is_parent_frame_secure */, - context()->AsWeakPtr()); + context()->AsWeakPtr(), + &remote_endpoints_.back()); provider_host()->running_hosted_version()->AddControllee(controllee.get()); ProcessLinkHeaderForRequest(
diff --git a/content/browser/service_worker/service_worker_browsertest.cc b/content/browser/service_worker/service_worker_browsertest.cc index 3f96ad84..0a9c1c3 100644 --- a/content/browser/service_worker/service_worker_browsertest.cc +++ b/content/browser/service_worker/service_worker_browsertest.cc
@@ -573,6 +573,7 @@ void TearDownOnIOThread() override { registration_ = NULL; version_ = NULL; + remote_endpoints_.clear(); } void InstallTestHelper(const std::string& worker_url, @@ -668,11 +669,12 @@ void AddControlleeOnIOThread() { ASSERT_TRUE(BrowserThread::CurrentlyOn(BrowserThread::IO)); + remote_endpoints_.emplace_back(); std::unique_ptr<ServiceWorkerProviderHost> host = - CreateProviderHostForWindow(33 /* dummy render process id */, - 1 /* dummy provider_id */, - true /* is_parent_frame_secure */, - wrapper()->context()->AsWeakPtr()); + CreateProviderHostForWindow( + 33 /* dummy render process id */, 1 /* dummy provider_id */, + true /* is_parent_frame_secure */, + wrapper()->context()->AsWeakPtr(), &remote_endpoints_.back()); host->SetDocumentUrl( embedded_test_server()->GetURL("/service_worker/host")); host->AssociateRegistration(registration_.get(), @@ -919,6 +921,7 @@ scoped_refptr<ServiceWorkerVersion> version_; scoped_refptr<ChromeBlobStorageContext> blob_context_; std::unique_ptr<ServiceWorkerFetchDispatcher> fetch_dispatcher_; + std::vector<ServiceWorkerRemoteProviderEndpoint> remote_endpoints_; }; IN_PROC_BROWSER_TEST_F(ServiceWorkerVersionBrowserTest, StartAndStop) {
diff --git a/content/browser/service_worker/service_worker_context_request_handler_unittest.cc b/content/browser/service_worker/service_worker_context_request_handler_unittest.cc index 73e6040..5f08d23 100644 --- a/content/browser/service_worker/service_worker_context_request_handler_unittest.cc +++ b/content/browser/service_worker/service_worker_context_request_handler_unittest.cc
@@ -91,7 +91,8 @@ std::unique_ptr<ServiceWorkerProviderHost> host = CreateProviderHostForServiceWorkerContext( helper_->mock_render_process_id(), 1 /* provider_id */, - true /* is_parent_frame_secure */, context()->AsWeakPtr()); + true /* is_parent_frame_secure */, context()->AsWeakPtr(), + &remote_endpoint_); provider_host_ = host->AsWeakPtr(); context()->AddProviderHost(std::move(host)); provider_host_->running_hosted_version_ = version_; @@ -136,6 +137,7 @@ GURL scope_; GURL script_url_; GURL import_script_url_; + ServiceWorkerRemoteProviderEndpoint remote_endpoint_; storage::BlobStorageContext blob_storage_context_; };
diff --git a/content/browser/service_worker/service_worker_context_unittest.cc b/content/browser/service_worker/service_worker_context_unittest.cc index 15aac69..ec1e0d4 100644 --- a/content/browser/service_worker/service_worker_context_unittest.cc +++ b/content/browser/service_worker/service_worker_context_unittest.cc
@@ -583,33 +583,38 @@ const GURL kOrigin1 = GURL("http://www.example.com/"); const GURL kOrigin2 = GURL("https://www.example.com/"); int provider_id = 1; + std::vector<ServiceWorkerRemoteProviderEndpoint> remote_endpoints; // Host1 (provider_id=1): process_id=1, origin1. + remote_endpoints.emplace_back(); std::unique_ptr<ServiceWorkerProviderHost> host1 = - CreateProviderHostForWindow(kRenderProcessId1, provider_id++, - true /* is_parent_frame_secure */, - context()->AsWeakPtr()); + CreateProviderHostForWindow( + kRenderProcessId1, provider_id++, true /* is_parent_frame_secure */, + context()->AsWeakPtr(), &remote_endpoints.back()); host1->SetDocumentUrl(kOrigin1); // Host2 (provider_id=2): process_id=2, origin2. + remote_endpoints.emplace_back(); std::unique_ptr<ServiceWorkerProviderHost> host2 = - CreateProviderHostForWindow(kRenderProcessId2, provider_id++, - true /* is_parent_frame_secure */, - context()->AsWeakPtr()); + CreateProviderHostForWindow( + kRenderProcessId2, provider_id++, true /* is_parent_frame_secure */, + context()->AsWeakPtr(), &remote_endpoints.back()); host2->SetDocumentUrl(kOrigin2); // Host3 (provider_id=3): process_id=2, origin1. + remote_endpoints.emplace_back(); std::unique_ptr<ServiceWorkerProviderHost> host3 = - CreateProviderHostForWindow(kRenderProcessId2, provider_id++, - true /* is_parent_frame_secure */, - context()->AsWeakPtr()); + CreateProviderHostForWindow( + kRenderProcessId2, provider_id++, true /* is_parent_frame_secure */, + context()->AsWeakPtr(), &remote_endpoints.back()); host3->SetDocumentUrl(kOrigin1); // Host4 (provider_id=4): process_id=2, origin2, for ServiceWorker. + remote_endpoints.emplace_back(); std::unique_ptr<ServiceWorkerProviderHost> host4 = CreateProviderHostForServiceWorkerContext( kRenderProcessId2, provider_id++, true /* is_parent_frame_secure */, - context()->AsWeakPtr()); + context()->AsWeakPtr(), &remote_endpoints.back()); host4->SetDocumentUrl(kOrigin2); ServiceWorkerProviderHost* host1_raw = host1.get();
diff --git a/content/browser/service_worker/service_worker_controllee_request_handler_unittest.cc b/content/browser/service_worker/service_worker_controllee_request_handler_unittest.cc index fa176b91..5c4be2f 100644 --- a/content/browser/service_worker/service_worker_controllee_request_handler_unittest.cc +++ b/content/browser/service_worker/service_worker_controllee_request_handler_unittest.cc
@@ -110,10 +110,12 @@ EmbeddedWorkerTestHelper::CreateHttpResponseInfo()); // An empty host. + remote_endpoints_.emplace_back(); std::unique_ptr<ServiceWorkerProviderHost> host = CreateProviderHostForWindow( helper_->mock_render_process_id(), kMockProviderId, - true /* is_parent_frame_secure */, helper_->context()->AsWeakPtr()); + true /* is_parent_frame_secure */, helper_->context()->AsWeakPtr(), + &remote_endpoints_.back()); provider_host_ = host->AsWeakPtr(); context()->AddProviderHost(std::move(host)); @@ -140,6 +142,7 @@ MockResourceContext mock_resource_context_; GURL scope_; GURL script_url_; + std::vector<ServiceWorkerRemoteProviderEndpoint> remote_endpoints_; }; class ServiceWorkerTestContentBrowserClient : public TestContentBrowserClient {
diff --git a/content/browser/service_worker/service_worker_dispatcher_host.cc b/content/browser/service_worker/service_worker_dispatcher_host.cc index 8fe3b3df..537f97cd 100644 --- a/content/browser/service_worker/service_worker_dispatcher_host.cc +++ b/content/browser/service_worker/service_worker_dispatcher_host.cc
@@ -967,7 +967,7 @@ // Otherwise, completed the initialization of the pre-created host. DCHECK_EQ(SERVICE_WORKER_PROVIDER_FOR_WINDOW, info.type); provider_host->CompleteNavigationInitialized(render_process_id_, - info.route_id, this); + std::move(info), this); GetContext()->AddProviderHost(std::move(provider_host)); } else { if (ServiceWorkerUtils::IsBrowserAssignedProviderId(info.provider_id)) { @@ -980,26 +980,6 @@ } } -void ServiceWorkerDispatcherHost::OnProviderDestroyed(int provider_id) { - TRACE_EVENT0("ServiceWorker", - "ServiceWorkerDispatcherHost::OnProviderDestroyed"); - if (!GetContext()) - return; - if (!GetContext()->GetProviderHost(render_process_id_, provider_id)) { - // PlzNavigate: in some cancellation of navigation cases, it is possible - // for the pre-created hoist to have been destroyed before being claimed by - // the renderer. The provider is then destroyed in the renderer, and no - // matching host will be found. - if (!IsBrowserSideNavigationEnabled() || - !ServiceWorkerUtils::IsBrowserAssignedProviderId(provider_id)) { - bad_message::ReceivedBadMessage( - this, bad_message::SWDH_PROVIDER_DESTROYED_NO_HOST); - } - return; - } - GetContext()->RemoveProviderHost(render_process_id_, provider_id); -} - void ServiceWorkerDispatcherHost::OnSetHostedVersionId(int provider_id, int64_t version_id, int embedded_worker_id) {
diff --git a/content/browser/service_worker/service_worker_dispatcher_host.h b/content/browser/service_worker/service_worker_dispatcher_host.h index 4615fb6..f813389 100644 --- a/content/browser/service_worker/service_worker_dispatcher_host.h +++ b/content/browser/service_worker/service_worker_dispatcher_host.h
@@ -101,7 +101,6 @@ // mojom::ServiceWorkerDispatcherHost implementation void OnProviderCreated(ServiceWorkerProviderHostInfo info) override; - void OnProviderDestroyed(int provider_id) override; void OnSetHostedVersionId(int provider_id, int64_t version_id, int embedded_worker_id) override;
diff --git a/content/browser/service_worker/service_worker_dispatcher_host_unittest.cc b/content/browser/service_worker/service_worker_dispatcher_host_unittest.cc index 46d5a7a4..5ccbba1 100644 --- a/content/browser/service_worker/service_worker_dispatcher_host_unittest.cc +++ b/content/browser/service_worker/service_worker_dispatcher_host_unittest.cc
@@ -22,11 +22,13 @@ #include "content/browser/service_worker/service_worker_context_core.h" #include "content/browser/service_worker/service_worker_context_wrapper.h" #include "content/browser/service_worker/service_worker_handle.h" +#include "content/browser/service_worker/service_worker_navigation_handle_core.h" #include "content/browser/service_worker/service_worker_test_utils.h" #include "content/common/service_worker/embedded_worker_messages.h" #include "content/common/service_worker/service_worker_messages.h" #include "content/common/service_worker/service_worker_types.h" #include "content/common/service_worker/service_worker_utils.h" +#include "content/public/common/browser_side_navigation_policy.h" #include "content/public/common/content_switches.h" #include "content/public/test/mock_resource_context.h" #include "content/public/test/test_browser_thread_bundle.h" @@ -50,6 +52,47 @@ ports->push_back(MessagePort(std::move(pipe.handle0))); } +struct RemoteProviderInfo { + mojom::ServiceWorkerProviderHostAssociatedPtr host_ptr; + mojom::ServiceWorkerProviderAssociatedRequest client_request; +}; + +RemoteProviderInfo SetupProviderHostInfoPtrs( + ServiceWorkerProviderHostInfo* host_info) { + RemoteProviderInfo remote_info; + mojom::ServiceWorkerProviderAssociatedPtr browser_side_client_ptr; + remote_info.client_request = + mojo::MakeIsolatedRequest(&browser_side_client_ptr); + host_info->host_request = mojo::MakeIsolatedRequest(&remote_info.host_ptr); + host_info->client_ptr_info = browser_side_client_ptr.PassInterface(); + EXPECT_TRUE(host_info->host_request.is_pending()); + EXPECT_TRUE(host_info->client_ptr_info.is_valid()); + EXPECT_TRUE(remote_info.host_ptr.is_bound()); + EXPECT_TRUE(remote_info.client_request.is_pending()); + return remote_info; +} + +std::unique_ptr<ServiceWorkerNavigationHandleCore> CreateNavigationHandleCore( + ServiceWorkerContextWrapper* context_wrapper) { + std::unique_ptr<ServiceWorkerNavigationHandleCore> navigation_handle_core; + BrowserThread::PostTaskAndReplyWithResult( + BrowserThread::UI, FROM_HERE, + base::Bind( + [](ServiceWorkerContextWrapper* wrapper) { + return base::MakeUnique<ServiceWorkerNavigationHandleCore>(nullptr, + wrapper); + }, + context_wrapper), + base::Bind( + [](std::unique_ptr<ServiceWorkerNavigationHandleCore>* dest, + std::unique_ptr<ServiceWorkerNavigationHandleCore> src) { + *dest = std::move(src); + }, + &navigation_handle_core)); + base::RunLoop().RunUntilIdle(); + return navigation_handle_core; +} + } // namespace static const int kRenderFrameId = 1; @@ -168,6 +211,8 @@ const int64_t kProviderId = 99; ServiceWorkerProviderHostInfo info(kProviderId, MSG_ROUTING_NONE, type, true /* is_parent_frame_secure */); + remote_endpoint_.BindWithProviderHostInfo(&info); + dispatcher_host_->OnProviderCreated(std::move(info)); helper_->SimulateAddProcessToPattern(pattern, helper_->mock_render_process_id()); @@ -253,7 +298,7 @@ int provider_id) { return CreateProviderHostWithDispatcherHost( helper_->mock_render_process_id(), provider_id, context()->AsWeakPtr(), - kRenderFrameId, dispatcher_host_.get()); + kRenderFrameId, dispatcher_host_.get(), &remote_endpoint_); } TestBrowserThreadBundle browser_thread_bundle_; @@ -263,6 +308,7 @@ scoped_refptr<ServiceWorkerRegistration> registration_; scoped_refptr<ServiceWorkerVersion> version_; ServiceWorkerProviderHost* provider_host_; + ServiceWorkerRemoteProviderEndpoint remote_endpoint_; }; class ServiceWorkerTestContentBrowserClient : public TestContentBrowserClient { @@ -509,32 +555,66 @@ } TEST_F(ServiceWorkerDispatcherHostTest, ProviderCreatedAndDestroyed) { - const int kProviderId = 1001; + // |kProviderId| must be -2 when PlzNavigate is enabled to match the + // pre-created provider host. Otherwise |kProviderId| is just a dummy value. + const int kProviderId = (IsBrowserSideNavigationEnabled() ? -2 : 1001); int process_id = helper_->mock_render_process_id(); - dispatcher_host_->OnProviderCreated(ServiceWorkerProviderHostInfo( - kProviderId, MSG_ROUTING_NONE, SERVICE_WORKER_PROVIDER_FOR_WINDOW, - true /* is_parent_frame_secure */)); + // Setup ServiceWorkerProviderHostInfo. + ServiceWorkerProviderHostInfo host_info_1(kProviderId, 1 /* route_id */, + SERVICE_WORKER_PROVIDER_FOR_WINDOW, + true /* is_parent_frame_secure */); + ServiceWorkerProviderHostInfo host_info_2(kProviderId, 1 /* route_id */, + SERVICE_WORKER_PROVIDER_FOR_WINDOW, + true /* is_parent_frame_secure */); + ServiceWorkerProviderHostInfo host_info_3(kProviderId, 1 /* route_id */, + SERVICE_WORKER_PROVIDER_FOR_WINDOW, + true /* is_parent_frame_secure */); + RemoteProviderInfo remote_info_1 = SetupProviderHostInfoPtrs(&host_info_1); + RemoteProviderInfo remote_info_2 = SetupProviderHostInfoPtrs(&host_info_2); + RemoteProviderInfo remote_info_3 = SetupProviderHostInfoPtrs(&host_info_3); + + // PlzNavigate + std::unique_ptr<ServiceWorkerNavigationHandleCore> navigation_handle_core; + if (IsBrowserSideNavigationEnabled()) { + navigation_handle_core = + CreateNavigationHandleCore(helper_->context_wrapper()); + ASSERT_TRUE(navigation_handle_core); + // ProviderHost should be created before OnProviderCreated. + navigation_handle_core->DidPreCreateProviderHost( + ServiceWorkerProviderHost::PreCreateNavigationHost( + helper_->context()->AsWeakPtr(), true /* are_ancestors_secure */, + base::Callback<WebContents*(void)>())); + } + + dispatcher_host_->OnProviderCreated(std::move(host_info_1)); EXPECT_TRUE(context()->GetProviderHost(process_id, kProviderId)); // Two with the same ID should be seen as a bad message. - dispatcher_host_->OnProviderCreated(ServiceWorkerProviderHostInfo( - kProviderId, MSG_ROUTING_NONE, SERVICE_WORKER_PROVIDER_FOR_WINDOW, - true /* is_parent_frame_secure */)); + dispatcher_host_->OnProviderCreated(std::move(host_info_2)); EXPECT_EQ(1, dispatcher_host_->bad_messages_received_count_); - dispatcher_host_->OnProviderDestroyed(kProviderId); + // Releasing the interface pointer destroys the counterpart. + remote_info_1.host_ptr.reset(); + base::RunLoop().RunUntilIdle(); EXPECT_FALSE(context()->GetProviderHost(process_id, kProviderId)); - // Destroying an ID that does not exist warrants a bad message. - dispatcher_host_->OnProviderDestroyed(kProviderId); - EXPECT_EQ(2, dispatcher_host_->bad_messages_received_count_); + // PlzNavigate + // Prepare another navigation handle to create another provider host. + if (IsBrowserSideNavigationEnabled()) { + navigation_handle_core = + CreateNavigationHandleCore(helper_->context_wrapper()); + ASSERT_TRUE(navigation_handle_core); + // ProviderHost should be created before OnProviderCreated. + navigation_handle_core->DidPreCreateProviderHost( + ServiceWorkerProviderHost::PreCreateNavigationHost( + helper_->context()->AsWeakPtr(), true /* are_ancestors_secure */, + base::Callback<WebContents*(void)>())); + } // Deletion of the dispatcher_host should cause providers for that // process to get deleted as well. - dispatcher_host_->OnProviderCreated(ServiceWorkerProviderHostInfo( - kProviderId, MSG_ROUTING_NONE, SERVICE_WORKER_PROVIDER_FOR_WINDOW, - true /* is_parent_frame_secure */)); + dispatcher_host_->OnProviderCreated(std::move(host_info_3)); EXPECT_TRUE(context()->GetProviderHost(process_id, kProviderId)); EXPECT_TRUE(dispatcher_host_->HasOneRef()); dispatcher_host_ = nullptr; @@ -669,9 +749,12 @@ // To show the new dispatcher can operate, simulate provider creation. Since // the old dispatcher cleaned up the old provider host, the new one won't // complain. - new_dispatcher_host->OnProviderCreated(ServiceWorkerProviderHostInfo( - provider_id, MSG_ROUTING_NONE, SERVICE_WORKER_PROVIDER_FOR_WINDOW, - true /* is_parent_frame_secure */)); + ServiceWorkerProviderHostInfo host_info(provider_id, MSG_ROUTING_NONE, + SERVICE_WORKER_PROVIDER_FOR_WINDOW, + true /* is_parent_frame_secure */); + ServiceWorkerRemoteProviderEndpoint remote_endpoint; + remote_endpoint.BindWithProviderHostInfo(&host_info); + new_dispatcher_host->OnProviderCreated(std::move(host_info)); EXPECT_EQ(0, new_dispatcher_host->bad_messages_received_count_); }
diff --git a/content/browser/service_worker/service_worker_handle_unittest.cc b/content/browser/service_worker/service_worker_handle_unittest.cc index 4a76ba5d..f3cdc0969 100644 --- a/content/browser/service_worker/service_worker_handle_unittest.cc +++ b/content/browser/service_worker/service_worker_handle_unittest.cc
@@ -113,8 +113,8 @@ provider_host_ = CreateProviderHostWithDispatcherHost( helper_->mock_render_process_id(), 1 /* provider_id */, - helper_->context()->AsWeakPtr(), kRenderFrameId, - dispatcher_host_.get()); + helper_->context()->AsWeakPtr(), kRenderFrameId, dispatcher_host_.get(), + &remote_endpoint_); helper_->SimulateAddProcessToPattern(pattern, helper_->mock_render_process_id()); } @@ -137,6 +137,7 @@ scoped_refptr<ServiceWorkerRegistration> registration_; scoped_refptr<ServiceWorkerVersion> version_; scoped_refptr<TestingServiceWorkerDispatcherHost> dispatcher_host_; + ServiceWorkerRemoteProviderEndpoint remote_endpoint_; private: DISALLOW_COPY_AND_ASSIGN(ServiceWorkerHandleTest);
diff --git a/content/browser/service_worker/service_worker_job_unittest.cc b/content/browser/service_worker/service_worker_job_unittest.cc index ca7aea28..c8fed4d 100644 --- a/content/browser/service_worker/service_worker_job_unittest.cc +++ b/content/browser/service_worker/service_worker_job_unittest.cc
@@ -173,6 +173,7 @@ TestBrowserThreadBundle browser_thread_bundle_; std::unique_ptr<EmbeddedWorkerTestHelper> helper_; + std::vector<ServiceWorkerRemoteProviderEndpoint> remote_endpoints_; }; scoped_refptr<ServiceWorkerRegistration> ServiceWorkerJobTest::RunRegisterJob( @@ -219,9 +220,11 @@ std::unique_ptr<ServiceWorkerProviderHost> ServiceWorkerJobTest::CreateControllee() { + remote_endpoints_.emplace_back(); std::unique_ptr<ServiceWorkerProviderHost> host = CreateProviderHostForWindow( 33 /* dummy render process id */, 1 /* dummy provider_id */, - true /* is_parent_frame_secure */, helper_->context()->AsWeakPtr()); + true /* is_parent_frame_secure */, helper_->context()->AsWeakPtr(), + &remote_endpoints_.back()); return host; }
diff --git a/content/browser/service_worker/service_worker_navigation_handle_core.h b/content/browser/service_worker/service_worker_navigation_handle_core.h index 88e5013..8e98b05 100644 --- a/content/browser/service_worker/service_worker_navigation_handle_core.h +++ b/content/browser/service_worker/service_worker_navigation_handle_core.h
@@ -10,6 +10,7 @@ #include "base/macros.h" #include "base/memory/ref_counted.h" #include "base/memory/weak_ptr.h" +#include "content/common/content_export.h" namespace content { @@ -24,7 +25,7 @@ // pendant of ServiceWorkerNavigationHandle. See the // ServiceWorkerNavigationHandle header for more details about the lifetime of // both classes. -class ServiceWorkerNavigationHandleCore { +class CONTENT_EXPORT ServiceWorkerNavigationHandleCore { public: ServiceWorkerNavigationHandleCore( base::WeakPtr<ServiceWorkerNavigationHandle> ui_handle,
diff --git a/content/browser/service_worker/service_worker_provider_host.cc b/content/browser/service_worker/service_worker_provider_host.cc index 4dd89cd..7671561 100644 --- a/content/browser/service_worker/service_worker_provider_host.cc +++ b/content/browser/service_worker/service_worker_provider_host.cc
@@ -61,8 +61,8 @@ // Called via custom URLRequestJobFactory. net::URLRequestJob* MaybeCreateJob( net::URLRequest* request, - net::NetworkDelegate* network_delegate, - ResourceContext* resource_context) override { + net::NetworkDelegate* /* network_delegate */, + ResourceContext* /* resource_context */) override { // |provider_host_| may have been deleted when the request is resumed. if (!provider_host_) return nullptr; @@ -76,6 +76,26 @@ DISALLOW_COPY_AND_ASSIGN(ServiceWorkerURLTrackingRequestHandler); }; +void RemoveProviderHost(base::WeakPtr<ServiceWorkerContextCore> context, + int process_id, + int provider_id) { + TRACE_EVENT0("ServiceWorker", + "ServiceWorkerProviderHost::RemoveProviderHost"); + if (!context) + return; + if (!context->GetProviderHost(process_id, provider_id)) { + // PlzNavigate: in some cancellation of navigation cases, it is possible + // for the pre-created host, whose |provider_id| is assigned by the browser + // process, to have been destroyed before being claimed by the renderer. The + // provider is then destroyed in the renderer, and no matching host will be + // found. + DCHECK(IsBrowserSideNavigationEnabled() && + ServiceWorkerUtils::IsBrowserAssignedProviderId(provider_id)); + return; + } + context->RemoveProviderHost(process_id, provider_id); +} + } // anonymous namespace ServiceWorkerProviderHost::OneShotGetReadyCallback::OneShotGetReadyCallback( @@ -152,7 +172,8 @@ info_(std::move(info)), context_(context), dispatcher_host_(dispatcher_host), - allow_association_(true) { + allow_association_(true), + binding_(this) { DCHECK_NE(SERVICE_WORKER_PROVIDER_UNKNOWN, info_.type); // PlzNavigate @@ -164,6 +185,18 @@ render_thread_id_ = kInvalidEmbeddedWorkerThreadId; } context_->RegisterProviderHostByClientID(client_uuid_, this); + + // PlzNavigate + // |provider_| and |binding_| will be bound on CompleteNavigationInitialized. + if (IsBrowserSideNavigationEnabled()) { + DCHECK(!info.client_ptr_info.is_valid() && !info.host_request.is_pending()); + return; + } + + provider_.Bind(std::move(info_.client_ptr_info)); + binding_.Bind(std::move(info_.host_request)); + binding_.set_connection_error_handler(base::Bind( + &RemoveProviderHost, context_, render_process_id, info_.provider_id)); } ServiceWorkerProviderHost::~ServiceWorkerProviderHost() { @@ -210,7 +243,7 @@ void ServiceWorkerProviderHost::OnVersionAttributesChanged( ServiceWorkerRegistration* registration, ChangedVersionAttributesMask changed_mask, - const ServiceWorkerRegistrationInfo& info) { + const ServiceWorkerRegistrationInfo& /* info */) { if (!get_ready_callback_ || get_ready_callback_->called) return; if (changed_mask.active_changed() && registration->active_version()) { @@ -538,7 +571,10 @@ std::unique_ptr<ServiceWorkerProviderHost> provisional_host = base::WrapUnique(new ServiceWorkerProviderHost( - process_id(), std::move(info_), context_, dispatcher_host())); + process_id(), + ServiceWorkerProviderHostInfo(std::move(info_), binding_.Unbind(), + provider_.PassInterface()), + context_, dispatcher_host())); for (const GURL& pattern : associated_patterns_) DecreaseProcessReference(pattern); @@ -568,6 +604,15 @@ render_thread_id_ = kDocumentMainThreadId; info_ = std::move(provisional_host->info_); + // Take the connection over from the provisional host. + DCHECK(!provider_.is_bound()); + DCHECK(!binding_.is_bound()); + provider_.Bind(provisional_host->provider_.PassInterface()); + binding_.Bind(provisional_host->binding_.Unbind()); + binding_.set_connection_error_handler( + base::Bind(&RemoveProviderHost, context_, provisional_host->process_id(), + provider_id())); + FinalizeInitialization(provisional_host->process_id(), provisional_host->dispatcher_host()); } @@ -575,7 +620,7 @@ // PlzNavigate void ServiceWorkerProviderHost::CompleteNavigationInitialized( int process_id, - int frame_routing_id, + ServiceWorkerProviderHostInfo info, ServiceWorkerDispatcherHost* dispatcher_host) { CHECK(IsBrowserSideNavigationEnabled()); DCHECK_EQ(ChildProcessHost::kInvalidUniqueID, render_process_id_); @@ -583,9 +628,18 @@ DCHECK_EQ(kDocumentMainThreadId, render_thread_id_); DCHECK_NE(ChildProcessHost::kInvalidUniqueID, process_id); - DCHECK_NE(MSG_ROUTING_NONE, frame_routing_id); + DCHECK_EQ(info_.provider_id, info.provider_id); + DCHECK_NE(MSG_ROUTING_NONE, info.route_id); - info_.route_id = frame_routing_id; + // Connect with the provider on the renderer. + DCHECK(!provider_.is_bound()); + DCHECK(!binding_.is_bound()); + provider_.Bind(std::move(info.client_ptr_info)); + binding_.Bind(std::move(info.host_request)); + binding_.set_connection_error_handler( + base::Bind(&RemoveProviderHost, context_, process_id, provider_id())); + info_.route_id = info.route_id; + FinalizeInitialization(process_id, dispatcher_host); }
diff --git a/content/browser/service_worker/service_worker_provider_host.h b/content/browser/service_worker/service_worker_provider_host.h index 4aa814af..bf34ff1 100644 --- a/content/browser/service_worker/service_worker_provider_host.h +++ b/content/browser/service_worker/service_worker_provider_host.h
@@ -22,11 +22,13 @@ #include "content/browser/service_worker/service_worker_registration.h" #include "content/common/content_export.h" #include "content/common/service_worker/service_worker_provider_host_info.h" +#include "content/common/service_worker/service_worker_provider_interfaces.mojom.h" #include "content/common/service_worker/service_worker_types.h" #include "content/common/worker_url_loader_factory_provider.mojom.h" #include "content/public/common/request_context_frame_type.h" #include "content/public/common/request_context_type.h" #include "content/public/common/resource_type.h" +#include "mojo/public/cpp/bindings/associated_binding.h" namespace storage { class BlobStorageContext; @@ -54,9 +56,20 @@ // // For providers hosting a running service worker, this class will observe // resource loads made directly by the service worker. +// +// This instance is created when navigation is started and +// ServiceWorkerNetworkProvider is create on the renderer process. Mojo's +// connection from ServiceWorkerNetworkProvider is established on the creation +// time, and the instance is destroyed on disconnection from the renderer side. +// If PlzNavigate is turned on, this instance is pre-created on the browser +// before ServiceWorkerNetworkProvider is created on the renderer because +// navigation is possible to be initiated on the browser side. In that case, +// establishment of Mojo's connection will be deferred until +// ServiceWorkerNetworkProvider is created on the renderer. class CONTENT_EXPORT ServiceWorkerProviderHost : public NON_EXPORTED_BASE(ServiceWorkerRegistration::Listener), - public base::SupportsWeakPtr<ServiceWorkerProviderHost> { + public base::SupportsWeakPtr<ServiceWorkerProviderHost>, + public NON_EXPORTED_BASE(mojom::ServiceWorkerProviderHost) { public: using GetRegistrationForReadyCallback = base::Callback<void(ServiceWorkerRegistration* reigstration)>; @@ -84,7 +97,7 @@ base::WeakPtr<ServiceWorkerContextCore> context, ServiceWorkerDispatcherHost* dispatcher_host); - virtual ~ServiceWorkerProviderHost(); + ~ServiceWorkerProviderHost() override; const std::string& client_uuid() const { return client_uuid_; } int process_id() const { return render_process_id_; } @@ -250,7 +263,7 @@ // Completes initialization of provider hosts used for navigation requests. void CompleteNavigationInitialized( int process_id, - int frame_routing_id, + ServiceWorkerProviderHostInfo info, ServiceWorkerDispatcherHost* dispatcher_host); // Sends event messages to the renderer. Events for the worker are queued up @@ -318,7 +331,7 @@ ~OneShotGetReadyCallback(); }; - ServiceWorkerProviderHost(int render_process_id, + ServiceWorkerProviderHost(int process_id, ServiceWorkerProviderHostInfo info, base::WeakPtr<ServiceWorkerContextCore> context, ServiceWorkerDispatcherHost* dispatcher_host); @@ -402,6 +415,14 @@ ServiceWorkerDispatcherHost* dispatcher_host_; bool allow_association_; + // |provider_| is the renderer-side Mojo endpoint for provider. + mojom::ServiceWorkerProviderAssociatedPtr provider_; + // |binding_| is the Mojo binding that keeps the connection to the + // renderer-side counterpart (content::ServiceWorkerNetworkProvider). When the + // connection bound on |binding_| gets killed from the renderer side, this + // content::ServiceWorkerProviderHost will be destroyed. + mojo::AssociatedBinding<mojom::ServiceWorkerProviderHost> binding_; + std::vector<base::Closure> queued_events_; // Keeps ServiceWorkerWorkerClient pointers of dedicated or shared workers
diff --git a/content/browser/service_worker/service_worker_provider_host_unittest.cc b/content/browser/service_worker/service_worker_provider_host_unittest.cc index b41b982..b100d3b 100644 --- a/content/browser/service_worker/service_worker_provider_host_unittest.cc +++ b/content/browser/service_worker/service_worker_provider_host_unittest.cc
@@ -18,6 +18,7 @@ #include "content/browser/service_worker/service_worker_test_utils.h" #include "content/browser/service_worker/service_worker_version.h" #include "content/common/url_schemes.h" +#include "content/public/common/browser_side_navigation_policy.h" #include "content/public/common/child_process_host.h" #include "content/public/common/origin_util.h" #include "content/public/test/test_browser_thread_bundle.h" @@ -41,7 +42,8 @@ protected: ServiceWorkerProviderHostTest() : thread_bundle_(TestBrowserThreadBundle::IO_MAINLOOP), - next_provider_id_(1) { + next_renderer_provided_id_(1), + next_browser_provided_id_(-2) { SetContentClient(&test_content_client_); } ~ServiceWorkerProviderHostTest() override {} @@ -76,10 +78,26 @@ } ServiceWorkerProviderHost* CreateProviderHost(const GURL& document_url) { - std::unique_ptr<ServiceWorkerProviderHost> host = - CreateProviderHostForWindow( - helper_->mock_render_process_id(), next_provider_id_++, - true /* is_parent_frame_secure */, helper_->context()->AsWeakPtr()); + remote_endpoints_.emplace_back(); + std::unique_ptr<ServiceWorkerProviderHost> host; + if (IsBrowserSideNavigationEnabled()) { + host = ServiceWorkerProviderHost::PreCreateNavigationHost( + helper_->context()->AsWeakPtr(), true, + base::Callback<WebContents*(void)>()); + ServiceWorkerProviderHostInfo info(next_browser_provided_id_--, + 1 /* route_id */, + SERVICE_WORKER_PROVIDER_FOR_WINDOW, + true /* is_parent_frame_secure */); + remote_endpoints_.back().BindWithProviderHostInfo(&info); + host->CompleteNavigationInitialized(helper_->mock_render_process_id(), + std::move(info), nullptr); + } else { + host = CreateProviderHostForWindow( + helper_->mock_render_process_id(), next_renderer_provided_id_++, + true /* is_parent_frame_secure */, helper_->context()->AsWeakPtr(), + &remote_endpoints_.back()); + } + ServiceWorkerProviderHost* host_raw = host.get(); host->SetDocumentUrl(document_url); context_->AddProviderHost(std::move(host)); @@ -88,11 +106,12 @@ ServiceWorkerProviderHost* CreateProviderHostWithInsecureParentFrame( const GURL& document_url) { + remote_endpoints_.emplace_back(); std::unique_ptr<ServiceWorkerProviderHost> host = - CreateProviderHostForWindow(helper_->mock_render_process_id(), - next_provider_id_++, - false /* is_parent_frame_secure */, - helper_->context()->AsWeakPtr()); + CreateProviderHostForWindow( + helper_->mock_render_process_id(), next_renderer_provided_id_++, + false /* is_parent_frame_secure */, helper_->context()->AsWeakPtr(), + &remote_endpoints_.back()); ServiceWorkerProviderHost* host_raw = host.get(); host->SetDocumentUrl(document_url); context_->AddProviderHost(std::move(host)); @@ -109,7 +128,9 @@ ServiceWorkerTestContentClient test_content_client_; TestContentBrowserClient test_content_browser_client_; ContentBrowserClient* old_content_browser_client_; - int next_provider_id_; + int next_renderer_provided_id_; + int next_browser_provided_id_; + std::vector<ServiceWorkerRemoteProviderEndpoint> remote_endpoints_; private: DISALLOW_COPY_AND_ASSIGN(ServiceWorkerProviderHostTest); @@ -226,6 +247,8 @@ } TEST_F(ServiceWorkerProviderHostTest, CrossSiteTransfer) { + if (IsBrowserSideNavigationEnabled()) + return; ServiceWorkerProviderHost* provider_host = CreateProviderHost(GURL("https://www.example.com/example.html")); const int process_id = provider_host->process_id(); @@ -269,4 +292,19 @@ EXPECT_EQ(nullptr, provisional_host->dispatcher_host()); } +TEST_F(ServiceWorkerProviderHostTest, RemoveProvider) { + // Create a provider host connected with the renderer process. + ServiceWorkerProviderHost* provider_host = + CreateProviderHost(GURL("https://www.example.com/example1.html")); + int process_id = provider_host->process_id(); + int provider_id = provider_host->provider_id(); + EXPECT_TRUE(context_->GetProviderHost(process_id, provider_id)); + + // Disconnect the mojo pipe from the renderer side. + ASSERT_TRUE(remote_endpoints_.back().host_ptr()->is_bound()); + remote_endpoints_.back().host_ptr()->reset(); + base::RunLoop().RunUntilIdle(); + EXPECT_FALSE(context_->GetProviderHost(process_id, provider_id)); +} + } // namespace content
diff --git a/content/browser/service_worker/service_worker_registration_unittest.cc b/content/browser/service_worker/service_worker_registration_unittest.cc index 6e1b444..490f6ec 100644 --- a/content/browser/service_worker/service_worker_registration_unittest.cc +++ b/content/browser/service_worker/service_worker_registration_unittest.cc
@@ -258,7 +258,8 @@ // Give the active version a controllee. host_ = CreateProviderHostForWindow( 33 /* dummy render process id */, 1 /* dummy provider_id */, - true /* is_parent_frame_secure */, context()->AsWeakPtr()); + true /* is_parent_frame_secure */, context()->AsWeakPtr(), + &remote_endpoint_); version_1->AddControllee(host_.get()); // Give the active version an in-flight request. @@ -292,6 +293,7 @@ private: scoped_refptr<ServiceWorkerRegistration> registration_; std::unique_ptr<ServiceWorkerProviderHost> host_; + ServiceWorkerRemoteProviderEndpoint remote_endpoint_; int inflight_request_id_ = -1; };
diff --git a/content/browser/service_worker/service_worker_request_handler_unittest.cc b/content/browser/service_worker/service_worker_request_handler_unittest.cc index bc1e378..86f0d4d 100644 --- a/content/browser/service_worker/service_worker_request_handler_unittest.cc +++ b/content/browser/service_worker/service_worker_request_handler_unittest.cc
@@ -44,9 +44,10 @@ // An empty host. std::unique_ptr<ServiceWorkerProviderHost> host = - CreateProviderHostForWindow( - helper_->mock_render_process_id(), kMockProviderId, - true /* is_parent_frame_secure */, context()->AsWeakPtr()); + CreateProviderHostForWindow(helper_->mock_render_process_id(), + kMockProviderId, + true /* is_parent_frame_secure */, + context()->AsWeakPtr(), &remote_endpoint_); provider_host_ = host->AsWeakPtr(); context()->AddProviderHost(std::move(host)); } @@ -111,6 +112,7 @@ net::URLRequestContext url_request_context_; net::TestDelegate url_request_delegate_; storage::BlobStorageContext blob_storage_context_; + ServiceWorkerRemoteProviderEndpoint remote_endpoint_; }; TEST_F(ServiceWorkerRequestHandlerTest, InitializeHandler_FTP) {
diff --git a/content/browser/service_worker/service_worker_storage_unittest.cc b/content/browser/service_worker/service_worker_storage_unittest.cc index 262ba79..c74f8b9 100644 --- a/content/browser/service_worker/service_worker_storage_unittest.cc +++ b/content/browser/service_worker/service_worker_storage_unittest.cc
@@ -1346,9 +1346,11 @@ registration_->SetActiveVersion(registration_->waiting_version()); storage()->UpdateToActiveState( registration_.get(), base::Bind(&ServiceWorkerUtils::NoOpStatusCallback)); + ServiceWorkerRemoteProviderEndpoint remote_endpoint; std::unique_ptr<ServiceWorkerProviderHost> host = CreateProviderHostForWindow( 33 /* dummy render process id */, 1 /* dummy provider_id */, - true /* is_parent_frame_secure */, context()->AsWeakPtr()); + true /* is_parent_frame_secure */, context()->AsWeakPtr(), + &remote_endpoint); registration_->active_version()->AddControllee(host.get()); bool was_called = false; @@ -1396,9 +1398,11 @@ registration_->SetWaitingVersion(NULL); storage()->UpdateToActiveState( registration_.get(), base::Bind(&ServiceWorkerUtils::NoOpStatusCallback)); + ServiceWorkerRemoteProviderEndpoint remote_endpoint; std::unique_ptr<ServiceWorkerProviderHost> host = CreateProviderHostForWindow( 33 /* dummy render process id */, 1 /* dummy provider_id */, - true /* is_parent_frame_secure */, context()->AsWeakPtr()); + true /* is_parent_frame_secure */, context()->AsWeakPtr(), + &remote_endpoint); registration_->active_version()->AddControllee(host.get()); bool was_called = false; @@ -1554,9 +1558,11 @@ registration_->SetActiveVersion(registration_->waiting_version()); storage()->UpdateToActiveState( registration_.get(), base::Bind(&ServiceWorkerUtils::NoOpStatusCallback)); + ServiceWorkerRemoteProviderEndpoint remote_endpoint; std::unique_ptr<ServiceWorkerProviderHost> host = CreateProviderHostForWindow( 33 /* dummy render process id */, 1 /* dummy provider_id */, - true /* is_parent_frame_secure */, helper_->context()->AsWeakPtr()); + true /* is_parent_frame_secure */, helper_->context()->AsWeakPtr(), + &remote_endpoint); registration_->active_version()->AddControllee(host.get()); bool was_called = false;
diff --git a/content/browser/service_worker/service_worker_test_utils.cc b/content/browser/service_worker/service_worker_test_utils.cc index 5b0ee5d9..4c8f7390 100644 --- a/content/browser/service_worker/service_worker_test_utils.cc +++ b/content/browser/service_worker/service_worker_test_utils.cc
@@ -10,14 +10,32 @@ namespace content { +ServiceWorkerRemoteProviderEndpoint::ServiceWorkerRemoteProviderEndpoint() {} +ServiceWorkerRemoteProviderEndpoint::ServiceWorkerRemoteProviderEndpoint( + ServiceWorkerRemoteProviderEndpoint&& other) + : host_ptr_(std::move(other.host_ptr_)), + client_request_(std::move(other.client_request_)) {} + +ServiceWorkerRemoteProviderEndpoint::~ServiceWorkerRemoteProviderEndpoint() {} + +void ServiceWorkerRemoteProviderEndpoint::BindWithProviderHostInfo( + content::ServiceWorkerProviderHostInfo* info) { + mojom::ServiceWorkerProviderAssociatedPtr client_ptr; + client_request_ = mojo::MakeIsolatedRequest(&client_ptr); + info->client_ptr_info = client_ptr.PassInterface(); + info->host_request = mojo::MakeIsolatedRequest(&host_ptr_); +} + std::unique_ptr<ServiceWorkerProviderHost> CreateProviderHostForWindow( int process_id, int provider_id, bool is_parent_frame_secure, - base::WeakPtr<ServiceWorkerContextCore> context) { + base::WeakPtr<ServiceWorkerContextCore> context, + ServiceWorkerRemoteProviderEndpoint* output_endpoint) { ServiceWorkerProviderHostInfo info(provider_id, 1 /* route_id */, SERVICE_WORKER_PROVIDER_FOR_WINDOW, is_parent_frame_secure); + output_endpoint->BindWithProviderHostInfo(&info); return ServiceWorkerProviderHost::Create(process_id, std::move(info), std::move(context), nullptr); } @@ -27,10 +45,12 @@ int process_id, int provider_id, bool is_parent_frame_secure, - base::WeakPtr<ServiceWorkerContextCore> context) { + base::WeakPtr<ServiceWorkerContextCore> context, + ServiceWorkerRemoteProviderEndpoint* output_endpoint) { ServiceWorkerProviderHostInfo info(provider_id, MSG_ROUTING_NONE, SERVICE_WORKER_PROVIDER_FOR_CONTROLLER, is_parent_frame_secure); + output_endpoint->BindWithProviderHostInfo(&info); return ServiceWorkerProviderHost::Create(process_id, std::move(info), std::move(context), nullptr); } @@ -40,9 +60,11 @@ int provider_id, base::WeakPtr<ServiceWorkerContextCore> context, int route_id, - ServiceWorkerDispatcherHost* dispatcher_host) { + ServiceWorkerDispatcherHost* dispatcher_host, + ServiceWorkerRemoteProviderEndpoint* output_endpoint) { ServiceWorkerProviderHostInfo info(provider_id, route_id, SERVICE_WORKER_PROVIDER_FOR_WINDOW, true); + output_endpoint->BindWithProviderHostInfo(&info); return ServiceWorkerProviderHost::Create(process_id, std::move(info), std::move(context), dispatcher_host); }
diff --git a/content/browser/service_worker/service_worker_test_utils.h b/content/browser/service_worker/service_worker_test_utils.h index 279faf3..2c9a086 100644 --- a/content/browser/service_worker/service_worker_test_utils.h +++ b/content/browser/service_worker/service_worker_test_utils.h
@@ -11,6 +11,7 @@ #include "base/callback.h" #include "base/command_line.h" #include "base/memory/weak_ptr.h" +#include "content/common/service_worker/service_worker_provider_interfaces.mojom.h" #include "content/public/browser/browser_thread.h" #include "content/public/common/content_switches.h" #include "testing/gtest/include/gtest/gtest.h" @@ -20,6 +21,7 @@ class ServiceWorkerContextCore; class ServiceWorkerDispatcherHost; class ServiceWorkerProviderHost; +struct ServiceWorkerProviderHostInfo; template <typename Arg> void ReceiveResult(BrowserThread::ID run_quit_thread, @@ -46,25 +48,58 @@ return base::Bind(&ReceiveResult<Arg>, id, quit, out); } +// Container for keeping the Mojo connection to the service worker provider on +// the renderer alive. +class ServiceWorkerRemoteProviderEndpoint { + public: + ServiceWorkerRemoteProviderEndpoint(); + ServiceWorkerRemoteProviderEndpoint( + ServiceWorkerRemoteProviderEndpoint&& other); + ~ServiceWorkerRemoteProviderEndpoint(); + + void BindWithProviderHostInfo(ServiceWorkerProviderHostInfo* info); + + mojom::ServiceWorkerProviderHostAssociatedPtr* host_ptr() { + return &host_ptr_; + } + + mojom::ServiceWorkerProviderAssociatedRequest* client_request() { + return &client_request_; + } + + private: + // Bound with content::ServiceWorkerProviderHost. The provider host will be + // removed asynchronously when this pointer is closed. + mojom::ServiceWorkerProviderHostAssociatedPtr host_ptr_; + // This is the other end of ServiceWorkerProviderAssociatedPtr owned by + // content::ServiceWorkerProviderHost. + mojom::ServiceWorkerProviderAssociatedRequest client_request_; + + DISALLOW_COPY_AND_ASSIGN(ServiceWorkerRemoteProviderEndpoint); +}; + std::unique_ptr<ServiceWorkerProviderHost> CreateProviderHostForWindow( int process_id, int provider_id, bool is_parent_frame_secure, - base::WeakPtr<ServiceWorkerContextCore> context); + base::WeakPtr<ServiceWorkerContextCore> context, + ServiceWorkerRemoteProviderEndpoint* output_endpoint); std::unique_ptr<ServiceWorkerProviderHost> CreateProviderHostForServiceWorkerContext( int process_id, int provider_id, bool is_parent_frame_secure, - base::WeakPtr<ServiceWorkerContextCore> context); + base::WeakPtr<ServiceWorkerContextCore> context, + ServiceWorkerRemoteProviderEndpoint* output_endpoint); std::unique_ptr<ServiceWorkerProviderHost> CreateProviderHostWithDispatcherHost( int process_id, int provider_id, base::WeakPtr<ServiceWorkerContextCore> context, int route_id, - ServiceWorkerDispatcherHost* dispatcher_host); + ServiceWorkerDispatcherHost* dispatcher_host, + ServiceWorkerRemoteProviderEndpoint* output_endpoint); } // namespace content
diff --git a/content/browser/service_worker/service_worker_url_request_job_unittest.cc b/content/browser/service_worker/service_worker_url_request_job_unittest.cc index 33378da..1c8ae85 100644 --- a/content/browser/service_worker/service_worker_url_request_job_unittest.cc +++ b/content/browser/service_worker/service_worker_url_request_job_unittest.cc
@@ -196,7 +196,8 @@ std::unique_ptr<ServiceWorkerProviderHost> provider_host = CreateProviderHostForWindow( helper_->mock_render_process_id(), kProviderID, - true /* is_parent_frame_secure */, helper_->context()->AsWeakPtr()); + true /* is_parent_frame_secure */, helper_->context()->AsWeakPtr(), + &remote_endpoint_); provider_host_ = provider_host->AsWeakPtr(); provider_host->SetDocumentUrl(GURL("https://example.com/")); registration_->SetActiveVersion(version_); @@ -355,6 +356,8 @@ int times_prepare_to_restart_invoked_ = 0; base::WeakPtr<ServiceWorkerProviderHost> provider_host_; + ServiceWorkerRemoteProviderEndpoint remote_endpoint_; + // Not owned. MockHttpProtocolHandler* http_protocol_handler_;
diff --git a/content/browser/service_worker/service_worker_version_unittest.cc b/content/browser/service_worker/service_worker_version_unittest.cc index 4531d0e..5dc91dc 100644 --- a/content/browser/service_worker/service_worker_version_unittest.cc +++ b/content/browser/service_worker/service_worker_version_unittest.cc
@@ -627,9 +627,11 @@ // Adding a controllee resets the idle time. version_->idle_time_ -= kOneSecond; idle_time = version_->idle_time_; + ServiceWorkerRemoteProviderEndpoint remote_endpoint; std::unique_ptr<ServiceWorkerProviderHost> host = CreateProviderHostForWindow( 33 /* dummy render process id */, 1 /* dummy provider_id */, - true /* is_parent_frame_secure */, helper_->context()->AsWeakPtr()); + true /* is_parent_frame_secure */, helper_->context()->AsWeakPtr(), + &remote_endpoint); version_->AddControllee(host.get()); EXPECT_TRUE(version_->timeout_timer_.IsRunning()); EXPECT_LT(idle_time, version_->idle_time_);
diff --git a/content/browser/service_worker/service_worker_write_to_cache_job_unittest.cc b/content/browser/service_worker/service_worker_write_to_cache_job_unittest.cc index 6940bdc..544ba639 100644 --- a/content/browser/service_worker/service_worker_write_to_cache_job_unittest.cc +++ b/content/browser/service_worker/service_worker_write_to_cache_job_unittest.cc
@@ -293,7 +293,7 @@ std::unique_ptr<ServiceWorkerProviderHost> host = CreateProviderHostForServiceWorkerContext( process_id, provider_id, true /* is_parent_frame_secure */, - context()->AsWeakPtr()); + context()->AsWeakPtr(), &remote_endpoint_); base::WeakPtr<ServiceWorkerProviderHost> provider_host = host->AsWeakPtr(); context()->AddProviderHost(std::move(host)); provider_host->running_hosted_version_ = version; @@ -441,6 +441,7 @@ storage::BlobStorageContext blob_storage_context_; content::MockResourceContext resource_context_; + ServiceWorkerRemoteProviderEndpoint remote_endpoint_; net::TestDelegate url_request_delegate_; int next_provider_id_ = 1;
diff --git a/content/browser/vibration_browsertest.cc b/content/browser/vibration_browsertest.cc index 0115896..1739fabd 100644 --- a/content/browser/vibration_browsertest.cc +++ b/content/browser/vibration_browsertest.cc
@@ -60,12 +60,12 @@ private: // device::mojom::VibrationManager: - void Vibrate(int64_t milliseconds, const VibrateCallback& callback) override { + void Vibrate(int64_t milliseconds, VibrateCallback callback) override { vibrate_milliseconds_ = milliseconds; - callback.Run(); + std::move(callback).Run(); vibrate_done_.Run(); } - void Cancel(const CancelCallback& callback) override { callback.Run(); } + void Cancel(CancelCallback callback) override { std::move(callback).Run(); } int64_t vibrate_milliseconds_ = -1; base::Closure vibrate_done_;
diff --git a/content/child/service_worker/service_worker_dispatcher_unittest.cc b/content/child/service_worker/service_worker_dispatcher_unittest.cc index 9f15581..93d25c2 100644 --- a/content/child/service_worker/service_worker_dispatcher_unittest.cc +++ b/content/child/service_worker/service_worker_dispatcher_unittest.cc
@@ -2,15 +2,16 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#include "content/child/service_worker/service_worker_dispatcher.h" #include "base/macros.h" #include "base/message_loop/message_loop.h" -#include "content/child/service_worker/service_worker_dispatcher.h" #include "content/child/service_worker/service_worker_handle_reference.h" #include "content/child/service_worker/service_worker_provider_context.h" #include "content/child/service_worker/web_service_worker_impl.h" #include "content/child/service_worker/web_service_worker_registration_impl.h" #include "content/child/thread_safe_sender.h" #include "content/common/service_worker/service_worker_messages.h" +#include "content/common/service_worker/service_worker_provider_interfaces.mojom.h" #include "content/common/service_worker/service_worker_types.h" #include "ipc/ipc_sync_message_filter.h" #include "ipc/ipc_test_sink.h" @@ -195,9 +196,10 @@ // Set up ServiceWorkerProviderContext for ServiceWorkerGlobalScope. const int kProviderId = 10; scoped_refptr<ServiceWorkerProviderContext> provider_context( - new ServiceWorkerProviderContext(kProviderId, - SERVICE_WORKER_PROVIDER_FOR_CONTROLLER, - thread_safe_sender())); + new ServiceWorkerProviderContext( + kProviderId, SERVICE_WORKER_PROVIDER_FOR_CONTROLLER, + mojom::ServiceWorkerProviderAssociatedRequest(), + thread_safe_sender())); // The passed references should be adopted and owned by the provider context. OnAssociateRegistration(kDocumentMainThreadId, kProviderId, info, attrs); @@ -228,9 +230,10 @@ // Set up ServiceWorkerProviderContext for a document context. const int kProviderId = 10; scoped_refptr<ServiceWorkerProviderContext> provider_context( - new ServiceWorkerProviderContext(kProviderId, - SERVICE_WORKER_PROVIDER_FOR_WINDOW, - thread_safe_sender())); + new ServiceWorkerProviderContext( + kProviderId, SERVICE_WORKER_PROVIDER_FOR_WINDOW, + mojom::ServiceWorkerProviderAssociatedRequest(), + thread_safe_sender())); // The passed references should be adopted and only the registration reference // should be owned by the provider context. @@ -277,9 +280,10 @@ // the provider, the passed referecence should be adopted and owned by the // provider context. scoped_refptr<ServiceWorkerProviderContext> provider_context( - new ServiceWorkerProviderContext(kProviderId, - SERVICE_WORKER_PROVIDER_FOR_WINDOW, - thread_safe_sender())); + new ServiceWorkerProviderContext( + kProviderId, SERVICE_WORKER_PROVIDER_FOR_WINDOW, + mojom::ServiceWorkerProviderAssociatedRequest(), + thread_safe_sender())); OnAssociateRegistration(kDocumentMainThreadId, kProviderId, info, attrs); ipc_sink()->ClearMessages(); OnSetControllerServiceWorker(kDocumentMainThreadId, kProviderId, attrs.active, @@ -326,7 +330,8 @@ // provider client and immediately released due to limitation of the mock // implementation. provider_context = new ServiceWorkerProviderContext( - kProviderId, SERVICE_WORKER_PROVIDER_FOR_WINDOW, thread_safe_sender()); + kProviderId, SERVICE_WORKER_PROVIDER_FOR_WINDOW, + mojom::ServiceWorkerProviderAssociatedRequest(), thread_safe_sender()); OnAssociateRegistration(kDocumentMainThreadId, kProviderId, info, attrs); provider_client.reset( new MockWebServiceWorkerProviderClientImpl(kProviderId, dispatcher())); @@ -356,9 +361,10 @@ std::unique_ptr<MockWebServiceWorkerProviderClientImpl> provider_client( new MockWebServiceWorkerProviderClientImpl(kProviderId, dispatcher())); scoped_refptr<ServiceWorkerProviderContext> provider_context( - new ServiceWorkerProviderContext(kProviderId, - SERVICE_WORKER_PROVIDER_FOR_WINDOW, - thread_safe_sender())); + new ServiceWorkerProviderContext( + kProviderId, SERVICE_WORKER_PROVIDER_FOR_WINDOW, + mojom::ServiceWorkerProviderAssociatedRequest(), + thread_safe_sender())); OnAssociateRegistration(kDocumentMainThreadId, kProviderId, info, attrs);
diff --git a/content/child/service_worker/service_worker_network_provider.cc b/content/child/service_worker/service_worker_network_provider.cc index ffac7fc..82556e6 100644 --- a/content/child/service_worker/service_worker_network_provider.cc +++ b/content/child/service_worker/service_worker_network_provider.cc
@@ -15,6 +15,7 @@ #include "content/common/service_worker/service_worker_utils.h" #include "content/public/common/browser_side_navigation_policy.h" #include "ipc/ipc_sync_channel.h" +#include "mojo/public/cpp/bindings/associated_group.h" #include "third_party/WebKit/public/platform/WebSecurityOrigin.h" #include "third_party/WebKit/public/platform/modules/serviceworker/WebServiceWorkerNetworkProvider.h" #include "third_party/WebKit/public/web/WebLocalFrame.h" @@ -176,14 +177,21 @@ return; if (!ChildThreadImpl::current()) return; // May be null in some tests. - ServiceWorkerProviderHostInfo provider_info( - provider_id_, route_id, provider_type, is_parent_frame_secure); + + ServiceWorkerProviderHostInfo host_info(provider_id_, route_id, provider_type, + is_parent_frame_secure); + host_info.host_request = mojo::MakeRequest(&provider_host_); + mojom::ServiceWorkerProviderAssociatedRequest client_request = + mojo::MakeRequest(&host_info.client_ptr_info); + + DCHECK(host_info.host_request.is_pending()); + DCHECK(host_info.host_request.handle().is_valid()); context_ = new ServiceWorkerProviderContext( - provider_id_, provider_type, + provider_id_, provider_type, std::move(client_request), ChildThreadImpl::current()->thread_safe_sender()); ChildThreadImpl::current()->channel()->GetRemoteAssociatedInterface( &dispatcher_host_); - dispatcher_host_->OnProviderCreated(std::move(provider_info)); + dispatcher_host_->OnProviderCreated(std::move(host_info)); } ServiceWorkerNetworkProvider::ServiceWorkerNetworkProvider( @@ -203,7 +211,7 @@ return; if (!ChildThreadImpl::current()) return; // May be null in some tests. - dispatcher_host_->OnProviderDestroyed(provider_id()); + provider_host_.reset(); } void ServiceWorkerNetworkProvider::SetServiceWorkerVersionId(
diff --git a/content/child/service_worker/service_worker_network_provider.h b/content/child/service_worker/service_worker_network_provider.h index 94afa943..113d260 100644 --- a/content/child/service_worker/service_worker_network_provider.h +++ b/content/child/service_worker/service_worker_network_provider.h
@@ -78,6 +78,7 @@ const int provider_id_; scoped_refptr<ServiceWorkerProviderContext> context_; mojom::ServiceWorkerDispatcherHostAssociatedPtr dispatcher_host_; + mojom::ServiceWorkerProviderHostAssociatedPtr provider_host_; DISALLOW_COPY_AND_ASSIGN(ServiceWorkerNetworkProvider); };
diff --git a/content/child/service_worker/service_worker_provider_context.cc b/content/child/service_worker/service_worker_provider_context.cc index 9203346..5b20204 100644 --- a/content/child/service_worker/service_worker_provider_context.cc +++ b/content/child/service_worker/service_worker_provider_context.cc
@@ -46,9 +46,9 @@ void AssociateRegistration( std::unique_ptr<ServiceWorkerRegistrationHandleReference> registration, - std::unique_ptr<ServiceWorkerHandleReference> installing, - std::unique_ptr<ServiceWorkerHandleReference> waiting, - std::unique_ptr<ServiceWorkerHandleReference> active) override { + std::unique_ptr<ServiceWorkerHandleReference> /* installing */, + std::unique_ptr<ServiceWorkerHandleReference> /* waiting */, + std::unique_ptr<ServiceWorkerHandleReference> /* active */) override { DCHECK(!registration_); registration_ = std::move(registration); } @@ -69,8 +69,8 @@ bool HasAssociatedRegistration() override { return !!registration_; } void GetAssociatedRegistration( - ServiceWorkerRegistrationObjectInfo* info, - ServiceWorkerVersionAttributes* attrs) override { + ServiceWorkerRegistrationObjectInfo* /* info */, + ServiceWorkerVersionAttributes* /* attrs */) override { NOTREACHED(); } @@ -111,7 +111,7 @@ } void SetController( - std::unique_ptr<ServiceWorkerHandleReference> controller) override { + std::unique_ptr<ServiceWorkerHandleReference> /* controller */) override { NOTREACHED(); } @@ -147,10 +147,12 @@ ServiceWorkerProviderContext::ServiceWorkerProviderContext( int provider_id, ServiceWorkerProviderType provider_type, + mojom::ServiceWorkerProviderAssociatedRequest request, ThreadSafeSender* thread_safe_sender) : provider_id_(provider_id), main_thread_task_runner_(base::ThreadTaskRunnerHandle::Get()), - thread_safe_sender_(thread_safe_sender) { + thread_safe_sender_(thread_safe_sender), + binding_(this, std::move(request)) { if (provider_type == SERVICE_WORKER_PROVIDER_FOR_CONTROLLER) delegate_.reset(new ControllerDelegate); else
diff --git a/content/child/service_worker/service_worker_provider_context.h b/content/child/service_worker/service_worker_provider_context.h index 192be96..26c8c0e 100644 --- a/content/child/service_worker/service_worker_provider_context.h +++ b/content/child/service_worker/service_worker_provider_context.h
@@ -13,7 +13,9 @@ #include "base/memory/ref_counted.h" #include "base/sequenced_task_runner_helpers.h" #include "content/common/content_export.h" +#include "content/common/service_worker/service_worker_provider_interfaces.mojom.h" #include "content/common/service_worker/service_worker_types.h" +#include "mojo/public/cpp/bindings/associated_binding.h" namespace base { class SingleThreadTaskRunner; @@ -44,11 +46,19 @@ // ControlleeDelegate and ControllerDelegate. class CONTENT_EXPORT ServiceWorkerProviderContext : public base::RefCountedThreadSafe<ServiceWorkerProviderContext, - ServiceWorkerProviderContextDeleter> { + ServiceWorkerProviderContextDeleter>, + NON_EXPORTED_BASE(public mojom::ServiceWorkerProvider) { public: - ServiceWorkerProviderContext(int provider_id, - ServiceWorkerProviderType provider_type, - ThreadSafeSender* thread_safe_sender); + // |provider_id| specifies which host will receive the message from this + // provider. |provider_type| changes the behavior of this provider + // context. |request| is an endpoint which is connected to + // content::ServiceWorkerProviderHost which notifies changes of the + // registration's and workers' status. |request| is bound with |binding_|. + ServiceWorkerProviderContext( + int provider_id, + ServiceWorkerProviderType provider_type, + mojom::ServiceWorkerProviderAssociatedRequest request, + ThreadSafeSender* thread_safe_sender); // Called from ServiceWorkerDispatcher. void OnAssociateRegistration( @@ -84,12 +94,16 @@ class ControlleeDelegate; class ControllerDelegate; - ~ServiceWorkerProviderContext(); + ~ServiceWorkerProviderContext() override; void DestructOnMainThread() const; const int provider_id_; scoped_refptr<base::SingleThreadTaskRunner> main_thread_task_runner_; scoped_refptr<ThreadSafeSender> thread_safe_sender_; + // Mojo binding for the |request| passed to the constructor. This keeps the + // connection to the content::ServiceWorkerProviderHost in the browser process + // alive. + mojo::AssociatedBinding<mojom::ServiceWorkerProvider> binding_; std::unique_ptr<Delegate> delegate_;
diff --git a/content/common/BUILD.gn b/content/common/BUILD.gn index e5e245e..144e2a2 100644 --- a/content/common/BUILD.gn +++ b/content/common/BUILD.gn
@@ -618,6 +618,7 @@ "service_worker/service_worker.mojom", "service_worker/service_worker_event_dispatcher.mojom", "service_worker/service_worker_provider.mojom", + "service_worker/service_worker_provider_interfaces.mojom", "service_worker/service_worker_types.mojom", "storage_partition_service.mojom", "url_loader.mojom",
diff --git a/content/common/service_worker/service_worker.mojom b/content/common/service_worker/service_worker.mojom index cadade0..185e018 100644 --- a/content/common/service_worker/service_worker.mojom +++ b/content/common/service_worker/service_worker.mojom
@@ -6,12 +6,19 @@ import "content/common/service_worker/service_worker_provider.mojom"; -// Per-process browser-side interface bound to ServiceWorkerDispatcherHost. -// Each InterfacePtrs on the same render process will be bound to the same -// ServiceWorkerDispatcherHost. +// Per-process browser-side interface bound to +// content::ServiceWorkerDispatcherHost. +// All InterfacePtrs on the same render process are bound to the same +// content::ServiceWorkerDispatcherHost. The renderer uses this interface to +// tell the browser when potential service worker clients are created and when +// service workers are starting up. interface ServiceWorkerDispatcherHost { + // Creates a content::ServiceWorkerProviderHost on the browser + // process. |provider_info| has Mojo endpoints to connect the provider host + // and the provider on the renderer together. The lifetime of + // ServiceWorkerProviderHost will be tied to the + // mojom::ServiceWorkerProviderHost interface. OnProviderCreated(ServiceWorkerProviderHostInfo provider_info); - OnProviderDestroyed(int32 provider_id); // Informs the browser that a service worker is starting up. |provider_id| // identifies the ServiceWorkerProviderHost hosting the service
diff --git a/content/common/service_worker/service_worker_provider.mojom b/content/common/service_worker/service_worker_provider.mojom index 6e512ed..6c897f8 100644 --- a/content/common/service_worker/service_worker_provider.mojom +++ b/content/common/service_worker/service_worker_provider.mojom
@@ -4,10 +4,12 @@ module content.mojom; +import "content/common/service_worker/service_worker_provider_interfaces.mojom"; import "content/common/service_worker/service_worker_types.mojom"; // A container object carried from the renderer to the browser process. -// This contains the params for the constructor of ServiceWorkerProviderHost. +// This contains the params for the constructor of +// content::ServiceWorkerProviderHost. // See also comments in // content/common/service_worker/service_worker_provider_host_info.h. struct ServiceWorkerProviderHostInfo { @@ -15,4 +17,6 @@ int32 route_id; ServiceWorkerProviderType type; bool is_parent_frame_secure; + associated ServiceWorkerProviderHost& host_request; + associated ServiceWorkerProvider client_ptr_info; }; \ No newline at end of file
diff --git a/content/common/service_worker/service_worker_provider_host_info.cc b/content/common/service_worker/service_worker_provider_host_info.cc index d1cc98a..23ac817 100644 --- a/content/common/service_worker/service_worker_provider_host_info.cc +++ b/content/common/service_worker/service_worker_provider_host_info.cc
@@ -30,7 +30,24 @@ : provider_id(other.provider_id), route_id(other.route_id), type(other.type), - is_parent_frame_secure(other.is_parent_frame_secure) { + is_parent_frame_secure(other.is_parent_frame_secure), + host_request(std::move(other.host_request)), + client_ptr_info(std::move(other.client_ptr_info)) { + SetDefaultValues(&other); +} + +ServiceWorkerProviderHostInfo::ServiceWorkerProviderHostInfo( + ServiceWorkerProviderHostInfo&& other, + mojom::ServiceWorkerProviderHostAssociatedRequest host_request, + mojom::ServiceWorkerProviderAssociatedPtrInfo client_ptr_info) + : provider_id(other.provider_id), + route_id(other.route_id), + type(other.type), + is_parent_frame_secure(other.is_parent_frame_secure), + host_request(std::move(host_request)), + client_ptr_info(std::move(client_ptr_info)) { + DCHECK(!other.host_request.is_pending()); + DCHECK(!other.client_ptr_info.is_valid()); SetDefaultValues(&other); }
diff --git a/content/common/service_worker/service_worker_provider_host_info.h b/content/common/service_worker/service_worker_provider_host_info.h index b89e2cdf..e4418fa 100644 --- a/content/common/service_worker/service_worker_provider_host_info.h +++ b/content/common/service_worker/service_worker_provider_host_info.h
@@ -5,6 +5,7 @@ #ifndef CONTENT_COMMON_SERVICE_WORKER_SERVICE_WORKER_PROVIDER_HOST_INFO_H_ #define CONTENT_COMMON_SERVICE_WORKER_SERVICE_WORKER_PROVIDER_HOST_INFO_H_ +#include "content/common/service_worker/service_worker_provider_interfaces.mojom.h" #include "content/common/service_worker/service_worker_types.h" namespace content { @@ -13,6 +14,10 @@ struct CONTENT_EXPORT ServiceWorkerProviderHostInfo { ServiceWorkerProviderHostInfo(); ServiceWorkerProviderHostInfo(ServiceWorkerProviderHostInfo&& other); + ServiceWorkerProviderHostInfo( + ServiceWorkerProviderHostInfo&& other, + mojom::ServiceWorkerProviderHostAssociatedRequest host_request, + mojom::ServiceWorkerProviderAssociatedPtrInfo client_ptr_info); ServiceWorkerProviderHostInfo(int provider_id, int route_id, ServiceWorkerProviderType type, @@ -48,6 +53,19 @@ // is_parent_frame_secure| is true. bool is_parent_frame_secure; + // Mojo endpoint to send a message from the renderer to the browser. This + // will be associated with ServiceWorkerDisptacherHost. |host_request| should + // be valid when ServiceWorkerProviderHostInfo is passed to any Mojo methods. + // After used to create the ServiceWorkerProviderHost, this will be invalid. + mojom::ServiceWorkerProviderHostAssociatedRequest host_request; + + // Mojo endpoint to send a message from the browser to the renderer. This + // will be associated with ServiceWorkerDisptacherHost. |client_ptr_info| + // should be valid when ServiceWorkerProviderHostInfo is passed to any Mojo + // methods. + // After used to create the ServiceWorkerProviderHost, this will be invalid. + mojom::ServiceWorkerProviderAssociatedPtrInfo client_ptr_info; + private: DISALLOW_COPY_AND_ASSIGN(ServiceWorkerProviderHostInfo); };
diff --git a/content/common/service_worker/service_worker_provider_interfaces.mojom b/content/common/service_worker/service_worker_provider_interfaces.mojom new file mode 100644 index 0000000..9f62bba --- /dev/null +++ b/content/common/service_worker/service_worker_provider_interfaces.mojom
@@ -0,0 +1,27 @@ +// Copyright 2017 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. + +module content.mojom; + +// mojom::ServiceWorkerProviderHost is a browser-side interface. The renderer +// uses this interface to request the browser to do operations involving service +// worker registrations. +interface ServiceWorkerProviderHost { + // TODO(shimazu): implement them. + // register() => (); + // getRegistation() => (); + // getRegistrations() => (); + // getRegistrationForReady() => (); +}; + +// mojom::ServiceWorkerProvider is a renderer-side interface. +// The browser process uses this interface to send messages to pages or +// the service worker's context. +interface ServiceWorkerProvider { + // TODO(shimazu): implement them. + // associateRegistration(); + // disassociateRegistration(); + // setControllerServiceWorker(); + // messageToDocument(); +}; \ No newline at end of file
diff --git a/content/common/service_worker/service_worker_provider_struct_traits.cc b/content/common/service_worker/service_worker_provider_struct_traits.cc index c4a4307..5202e30 100644 --- a/content/common/service_worker/service_worker_provider_struct_traits.cc +++ b/content/common/service_worker/service_worker_provider_struct_traits.cc
@@ -16,6 +16,10 @@ out->provider_id = in.provider_id(); out->route_id = in.route_id(); out->is_parent_frame_secure = in.is_parent_frame_secure(); + out->host_request = in.TakeHostRequest< + content::mojom::ServiceWorkerProviderHostAssociatedRequest>(); + out->client_ptr_info = in.TakeClientPtrInfo< + content::mojom::ServiceWorkerProviderAssociatedPtrInfo>(); return true; }
diff --git a/content/common/service_worker/service_worker_provider_struct_traits.h b/content/common/service_worker/service_worker_provider_struct_traits.h index e8cff44..bdab91f 100644 --- a/content/common/service_worker/service_worker_provider_struct_traits.h +++ b/content/common/service_worker/service_worker_provider_struct_traits.h
@@ -31,6 +31,16 @@ return info.is_parent_frame_secure; } + static content::mojom::ServiceWorkerProviderHostAssociatedRequest& + host_request(content::ServiceWorkerProviderHostInfo& info) { + return info.host_request; + } + + static content::mojom::ServiceWorkerProviderAssociatedPtrInfo& + client_ptr_info(content::ServiceWorkerProviderHostInfo& info) { + return info.client_ptr_info; + } + static bool Read(content::mojom::ServiceWorkerProviderHostInfoDataView in, content::ServiceWorkerProviderHostInfo* out); };
diff --git a/content/public/android/java/src/org/chromium/content/browser/ChildProcessLauncher.java b/content/public/android/java/src/org/chromium/content/browser/ChildProcessLauncher.java index 65f3796..9e5db35 100644 --- a/content/public/android/java/src/org/chromium/content/browser/ChildProcessLauncher.java +++ b/content/public/android/java/src/org/chromium/content/browser/ChildProcessLauncher.java
@@ -56,8 +56,8 @@ ChildProcessCreationParams creationParams, ChildProcessConnection.StartCallback startCallback, ChildProcessConnection.DeathCallback deathCallback) { - boolean bindToCallerCheck = isServiceExternalFromCreationParams( - creationParams, true /* sandboxed */); + boolean bindToCallerCheck = + creationParams == null ? false : creationParams.getBindToCallerCheck(); ChildConnectionAllocator allocator = getConnectionAllocator(context, creationParams, true /* sandboxed */); return ChildProcessLauncher.allocateBoundConnection(context, allocator,
diff --git a/content/public/android/javatests/src/org/chromium/content/browser/ChildProcessLauncherTest.java b/content/public/android/javatests/src/org/chromium/content/browser/ChildProcessLauncherTest.java index 7238025..d22e89e 100644 --- a/content/public/android/javatests/src/org/chromium/content/browser/ChildProcessLauncherTest.java +++ b/content/public/android/javatests/src/org/chromium/content/browser/ChildProcessLauncherTest.java
@@ -408,10 +408,11 @@ }); } - @Test - @MediumTest - @Feature({"ProcessManagement"}) - public void testWarmUp() { + private void testWarmUpWithCreationParams(ChildProcessCreationParams creationParams) { + if (creationParams != null) { + ChildProcessCreationParams.registerDefault(creationParams); + } + Context context = InstrumentationRegistry.getInstrumentation().getTargetContext(); warmUpOnUiThreadBlocking(context); @@ -419,7 +420,7 @@ ChildProcessLauncherHelper launcherHelper = ChildProcessLauncherTestUtils.startForTesting( context, true /* sandboxed */, false /* alwaysInForeground */, new String[0], - new FileDescriptorInfo[0], null); + new FileDescriptorInfo[0], creationParams /* creationParams */); final ChildProcessConnection conn = retrieveConnection(launcherHelper); @@ -442,6 +443,25 @@ }); } + @Test + @MediumTest + @Feature({"ProcessManagement"}) + public void testWarmUp() { + // Use the default creation parameters. + testWarmUpWithCreationParams(null /* creationParams */); + } + + @Test + @MediumTest + @Feature({"ProcessManagement"}) + public void testWarmUpWithBindToCaller() { + Context context = InstrumentationRegistry.getInstrumentation().getTargetContext(); + ChildProcessCreationParams creationParams = new ChildProcessCreationParams( + context.getPackageName(), false /* isExternalService */, + LibraryProcessType.PROCESS_CHILD, true /* bindToCallerCheck */); + testWarmUpWithCreationParams(creationParams); + } + // Tests that the warm-up connection is freed from its allocator if it crashes. @Test @MediumTest
diff --git a/content/public/common/content_features.cc b/content/public/common/content_features.cc index 3588134..3b2ec58 100644 --- a/content/public/common/content_features.cc +++ b/content/public/common/content_features.cc
@@ -254,6 +254,11 @@ const base::Feature kTouchpadAndWheelScrollLatching{ "TouchpadAndWheelScrollLatching", base::FEATURE_DISABLED_BY_DEFAULT}; +// Use Feature Policy to gate the use of permission features like midi, +// geolocation, camera, microphone, etc. +const base::Feature kUseFeaturePolicyForPermissions{ + "UseFeaturePolicyForPermissions", base::FEATURE_DISABLED_BY_DEFAULT}; + // Controls whether vibrate requires user gesture. const base::Feature kVibrateRequiresUserGesture{ "VibrateRequiresUserGesture", base::FEATURE_ENABLED_BY_DEFAULT};
diff --git a/content/public/common/content_features.h b/content/public/common/content_features.h index 65da672..50f9f2f 100644 --- a/content/public/common/content_features.h +++ b/content/public/common/content_features.h
@@ -67,6 +67,7 @@ CONTENT_EXPORT extern const base::Feature kTokenBinding; CONTENT_EXPORT extern const base::Feature kTopDocumentIsolation; CONTENT_EXPORT extern const base::Feature kTouchpadAndWheelScrollLatching; +CONTENT_EXPORT extern const base::Feature kUseFeaturePolicyForPermissions; CONTENT_EXPORT extern const base::Feature kVibrateRequiresUserGesture; CONTENT_EXPORT extern const base::Feature kVrShell; CONTENT_EXPORT extern const base::Feature kWebAssembly;
diff --git a/content/public/test/test_browser_thread_bundle.cc b/content/public/test/test_browser_thread_bundle.cc index 6d0d4df2..fa85212 100644 --- a/content/public/test/test_browser_thread_bundle.cc +++ b/content/public/test/test_browser_thread_bundle.cc
@@ -10,7 +10,6 @@ #include "base/run_loop.h" #include "base/task_scheduler/task_scheduler.h" #include "base/test/scoped_async_task_scheduler.h" -#include "base/threading/sequenced_worker_pool.h" #include "content/browser/browser_thread_impl.h" #include "content/public/browser/browser_thread.h" #include "content/public/test/test_browser_thread.h" @@ -79,12 +78,6 @@ // for DestructionObservers hooked to |message_loop_| to be able to invoke // BrowserThread::CurrentlyOn() -- ref. ~TestBrowserThread(). message_loop_.reset(); - - // Disable redirection of SequencedWorkerPools to TaskScheduler. This is - // required in order to reset global state so that tests following this one in - // this process can still manage their own SequencedWorkerPool without using - // TestBrowserThreadBundle. - base::SequencedWorkerPool::EnableForProcess(); } void TestBrowserThreadBundle::Init() { @@ -130,9 +123,6 @@ base::MakeUnique<base::test::ScopedAsyncTaskScheduler>(); } - // Enable redirection of SequencedWorkerPools to TaskScheduler. - base::SequencedWorkerPool::EnableWithRedirectionToTaskSchedulerForProcess(); - if (options_ & REAL_DB_THREAD) { db_thread_ = base::MakeUnique<TestBrowserThread>(BrowserThread::DB); db_thread_->Start();
diff --git a/content/public/test/test_renderer_host.h b/content/public/test/test_renderer_host.h index 8dc63b8..09c3e26 100644 --- a/content/public/test/test_renderer_host.h +++ b/content/public/test/test_renderer_host.h
@@ -139,6 +139,14 @@ // Simulate a renderer-initiated navigation up until commit. virtual void NavigateAndCommitRendererInitiated(bool did_create_new_entry, const GURL& url) = 0; + + // Set the feature policy header for the RenderFrameHost for test. Currently + // this is limited to setting a whitelist for a single feature. This function + // can be generalized as needed. Setting a header policy should only be done + // once per navigation of the RFH. + virtual void SimulateFeaturePolicyHeader( + blink::WebFeaturePolicyFeature feature, + const std::vector<url::Origin>& whitelist) = 0; }; // An interface and utility for driving tests of RenderViewHost.
diff --git a/content/renderer/BUILD.gn b/content/renderer/BUILD.gn index 3373c12..dee0774 100644 --- a/content/renderer/BUILD.gn +++ b/content/renderer/BUILD.gn
@@ -239,6 +239,8 @@ "media/media_permission_dispatcher.h", "media/midi_message_filter.cc", "media/midi_message_filter.h", + "media/mojo_audio_output_ipc.cc", + "media/mojo_audio_output_ipc.h", "media/render_media_client.cc", "media/render_media_client.h", "media/render_media_log.cc",
diff --git a/content/renderer/media/mojo_audio_output_ipc.cc b/content/renderer/media/mojo_audio_output_ipc.cc new file mode 100644 index 0000000..f3c6a54 --- /dev/null +++ b/content/renderer/media/mojo_audio_output_ipc.cc
@@ -0,0 +1,200 @@ +// Copyright 2017 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 "content/renderer/media/mojo_audio_output_ipc.h" + +#include <utility> + +#include "media/audio/audio_device_description.h" +#include "mojo/public/cpp/system/platform_handle.h" + +namespace content { + +namespace { + +void TrivialAuthorizedCallback(media::OutputDeviceStatus, + const media::AudioParameters&, + const std::string&) {} + +} // namespace + +MojoAudioOutputIPC::MojoAudioOutputIPC(FactoryAccessorCB factory_accessor) + : factory_accessor_(std::move(factory_accessor)), weak_factory_(this) { + DETACH_FROM_THREAD(thread_checker_); +} + +MojoAudioOutputIPC::~MojoAudioOutputIPC() { + DCHECK(!AuthorizationRequested() && !StreamCreationRequested()) + << "CloseStream must be called before destructing the AudioOutputIPC"; + // No thread check. + // Destructing |weak_factory_| on any thread is safe since it's not used after + // the final call to CloseStream, where its pointers are invalidated. +} + +void MojoAudioOutputIPC::RequestDeviceAuthorization( + media::AudioOutputIPCDelegate* delegate, + int session_id, + const std::string& device_id, + const url::Origin& security_origin) { + DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); + DCHECK(delegate); + DCHECK(!delegate_); + DCHECK(!AuthorizationRequested()); + DCHECK(!StreamCreationRequested()); + delegate_ = delegate; + + // We pass in a ScopedClosureRunner to detect the case when the mojo + // connection is terminated prior to receiving the response. In this case, + // the closure runner will be destructed and call ReceivedDeviceAuthorization + // with an error. + DoRequestDeviceAuthorization( + session_id, device_id, + base::BindOnce( + &MojoAudioOutputIPC::ReceivedDeviceAuthorization, + weak_factory_.GetWeakPtr(), + base::ScopedClosureRunner(base::Bind( + &MojoAudioOutputIPC::ReceivedDeviceAuthorization, + weak_factory_.GetWeakPtr(), + base::Passed(base::ScopedClosureRunner()), + media::OutputDeviceStatus::OUTPUT_DEVICE_STATUS_ERROR_INTERNAL, + media::AudioParameters::UnavailableDeviceParams(), + std::string())))); +} + +void MojoAudioOutputIPC::CreateStream(media::AudioOutputIPCDelegate* delegate, + const media::AudioParameters& params) { + DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); + DCHECK(delegate); + DCHECK(!StreamCreationRequested()); + if (!AuthorizationRequested()) { + DCHECK(!delegate_); + delegate_ = delegate; + // No authorization requested yet. Request one for the default device. + // Since the delegate didn't explicitly request authorization, we shouldn't + // send a callback to it. + if (!DoRequestDeviceAuthorization( + 0, media::AudioDeviceDescription::kDefaultDeviceId, + base::Bind(&TrivialAuthorizedCallback))) { + return; + } + } + + DCHECK_EQ(delegate_, delegate); + // Since the creation callback won't fire if the provider binding is gone + // and |this| owns |stream_provider_|, unretained is safe. + stream_provider_->Acquire( + mojo::MakeRequest(&stream_), params, + base::Bind(&MojoAudioOutputIPC::StreamCreated, base::Unretained(this))); + + // Unretained is safe because |delegate_| must remain valid until + // CloseStream is called, and |stream_provider_| is reset in CloseStream. + stream_.set_connection_error_handler(base::Bind( + &media::AudioOutputIPCDelegate::OnError, base::Unretained(delegate_))); +} + +void MojoAudioOutputIPC::PlayStream() { + DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); + if (stream_.is_bound()) + stream_->Play(); +} + +void MojoAudioOutputIPC::PauseStream() { + DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); + if (stream_.is_bound()) + stream_->Pause(); +} + +void MojoAudioOutputIPC::CloseStream() { + DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); + stream_provider_.reset(); + stream_.reset(); + delegate_ = nullptr; + + // Cancel any pending callbacks for this stream. + weak_factory_.InvalidateWeakPtrs(); +} + +void MojoAudioOutputIPC::SetVolume(double volume) { + DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); + if (stream_.is_bound()) + stream_->SetVolume(volume); +} + +bool MojoAudioOutputIPC::AuthorizationRequested() { + return stream_provider_.is_bound(); +} + +bool MojoAudioOutputIPC::StreamCreationRequested() { + return stream_.is_bound(); +} + +media::mojom::AudioOutputStreamProviderRequest +MojoAudioOutputIPC::MakeProviderRequest() { + DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); + DCHECK(!AuthorizationRequested()); + media::mojom::AudioOutputStreamProviderRequest request = + mojo::MakeRequest(&stream_provider_); + + // Unretained is safe because |delegate_| must remain valid until + // CloseStream is called, and |stream_provider_| is reset in CloseStream. + stream_provider_.set_connection_error_handler(base::Bind( + &media::AudioOutputIPCDelegate::OnError, base::Unretained(delegate_))); + return request; +} + +bool MojoAudioOutputIPC::DoRequestDeviceAuthorization( + int session_id, + const std::string& device_id, + AuthorizationCB callback) { + DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); + auto* factory = factory_accessor_.Run(); + if (!factory) { + LOG(ERROR) << "MojoAudioOutputIPC failed to acquire factory"; + + media::AudioOutputIPCDelegate* delegate = delegate_; + CloseStream(); + delegate->OnIPCClosed(); // deletes |this|. + return false; + } + + factory->RequestDeviceAuthorization(MakeProviderRequest(), session_id, + device_id, std::move(callback)); + return true; +} + +void MojoAudioOutputIPC::ReceivedDeviceAuthorization( + base::ScopedClosureRunner fallback_closure, + media::OutputDeviceStatus status, + const media::AudioParameters& params, + const std::string& device_id) const { + DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); + DCHECK(delegate_); + ignore_result(fallback_closure.Release()); + delegate_->OnDeviceAuthorized(status, params, device_id); +} + +void MojoAudioOutputIPC::StreamCreated( + mojo::ScopedSharedBufferHandle shared_memory, + mojo::ScopedHandle socket) { + DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); + DCHECK(delegate_); + DCHECK(socket.is_valid()); + DCHECK(shared_memory.is_valid()); + + base::PlatformFile socket_handle; + auto result = mojo::UnwrapPlatformFile(std::move(socket), &socket_handle); + DCHECK_EQ(result, MOJO_RESULT_OK); + + base::SharedMemoryHandle memory_handle; + bool read_only = false; + size_t memory_length = 0; + result = mojo::UnwrapSharedMemoryHandle( + std::move(shared_memory), &memory_handle, &memory_length, &read_only); + DCHECK_EQ(result, MOJO_RESULT_OK); + DCHECK(!read_only); + + delegate_->OnStreamCreated(memory_handle, socket_handle, memory_length); +} + +} // namespace content
diff --git a/content/renderer/media/mojo_audio_output_ipc.h b/content/renderer/media/mojo_audio_output_ipc.h new file mode 100644 index 0000000..b014e50 --- /dev/null +++ b/content/renderer/media/mojo_audio_output_ipc.h
@@ -0,0 +1,85 @@ +// Copyright 2017 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 CONTENT_RENDERER_MEDIA_MOJO_AUDIO_OUTPUT_IPC_H_ +#define CONTENT_RENDERER_MEDIA_MOJO_AUDIO_OUTPUT_IPC_H_ + +#include <string> + +#include "base/callback_helpers.h" +#include "base/macros.h" +#include "base/memory/weak_ptr.h" +#include "base/threading/thread_checker.h" +#include "content/common/content_export.h" +#include "content/common/media/renderer_audio_output_stream_factory.mojom.h" +#include "media/audio/audio_output_ipc.h" + +namespace content { + +// MojoAudioOutputIPC is a renderer-side class for handling creation, +// initialization and control of an output stream. May only be used on a single +// thread. +class CONTENT_EXPORT MojoAudioOutputIPC : public media::AudioOutputIPC { + public: + using FactoryAccessorCB = + base::RepeatingCallback<mojom::RendererAudioOutputStreamFactory*()>; + + // |factory_accessor| is required to provide a + // RendererAudioOutputStreamFactory* if IPC is possible. + explicit MojoAudioOutputIPC(FactoryAccessorCB factory_accessor); + + ~MojoAudioOutputIPC() override; + + // AudioOutputIPC implementation. + void RequestDeviceAuthorization(media::AudioOutputIPCDelegate* delegate, + int session_id, + const std::string& device_id, + const url::Origin& security_origin) override; + void CreateStream(media::AudioOutputIPCDelegate* delegate, + const media::AudioParameters& params) override; + void PlayStream() override; + void PauseStream() override; + void CloseStream() override; + void SetVolume(double volume) override; + + private: + using AuthorizationCB = mojom::RendererAudioOutputStreamFactory:: + RequestDeviceAuthorizationCallback; + + bool AuthorizationRequested(); + bool StreamCreationRequested(); + media::mojom::AudioOutputStreamProviderRequest MakeProviderRequest(); + + // Tries to acquire a RendererAudioOutputStreamFactory, returns true on + // success. On failure, |this| has been deleted, so returning immediately + // is required. + bool DoRequestDeviceAuthorization(int session_id, + const std::string& device_id, + AuthorizationCB callback); + + void ReceivedDeviceAuthorization(base::ScopedClosureRunner fallback_closure, + media::OutputDeviceStatus status, + const media::AudioParameters& params, + const std::string& device_id) const; + + void StreamCreated(mojo::ScopedSharedBufferHandle shared_memory, + mojo::ScopedHandle socket); + + const FactoryAccessorCB factory_accessor_; + + THREAD_CHECKER(thread_checker_); + media::mojom::AudioOutputStreamProviderPtr stream_provider_; + media::mojom::AudioOutputStreamPtr stream_; + media::AudioOutputIPCDelegate* delegate_ = nullptr; + + // To make sure we don't send an "authorization completed" callback for a + // stream after it's closed, we use this weak factory. + base::WeakPtrFactory<MojoAudioOutputIPC> weak_factory_; + + DISALLOW_COPY_AND_ASSIGN(MojoAudioOutputIPC); +}; + +} // namespace content + +#endif // CONTENT_RENDERER_MEDIA_MOJO_AUDIO_OUTPUT_IPC_H_
diff --git a/content/renderer/media/mojo_audio_output_ipc_unittest.cc b/content/renderer/media/mojo_audio_output_ipc_unittest.cc new file mode 100644 index 0000000..eac0ca0 --- /dev/null +++ b/content/renderer/media/mojo_audio_output_ipc_unittest.cc
@@ -0,0 +1,556 @@ +// Copyright 2017 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 "content/renderer/media/mojo_audio_output_ipc.h" + +#include <algorithm> +#include <memory> +#include <string> +#include <utility> + +#include "base/message_loop/message_loop.h" +#include "base/optional.h" +#include "base/run_loop.h" +#include "base/test/gtest_util.h" +#include "media/audio/audio_device_description.h" +#include "media/base/audio_parameters.h" +#include "mojo/public/cpp/bindings/binding.h" +#include "mojo/public/cpp/system/platform_handle.h" +#include "testing/gmock/include/gmock/gmock.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "url/origin.h" + +using testing::_; +using testing::AtLeast; +using testing::Mock; +using testing::StrictMock; + +namespace content { + +namespace { + +const int kSessionId = 1234; +const size_t kMemoryLength = 4321; +const char kDeviceId[] = "device_id"; +const char kReturnedDeviceId[] = "returned_device_id"; +const double kNewVolume = 0.271828; + +url::Origin Origin() { + return {}; +} + +media::AudioParameters Params() { + return media::AudioParameters::UnavailableDeviceParams(); +} + +MojoAudioOutputIPC::FactoryAccessorCB NullAccessor() { + return base::BindRepeating( + []() -> mojom::RendererAudioOutputStreamFactory* { return nullptr; }); +} + +class TestRemoteFactory : public mojom::RendererAudioOutputStreamFactory { + public: + TestRemoteFactory() + : expect_request_(false), + binding_(this, mojo::MakeRequest(&this_proxy_)) {} + + ~TestRemoteFactory() override {} + + void RequestDeviceAuthorization( + media::mojom::AudioOutputStreamProviderRequest stream_provider_request, + int64_t session_id, + const std::string& device_id, + RequestDeviceAuthorizationCallback callback) override { + EXPECT_EQ(session_id, expected_session_id_); + EXPECT_EQ(device_id, expected_device_id_); + EXPECT_TRUE(expect_request_); + if (provider_) { + std::move(callback).Run( + media::OutputDeviceStatus::OUTPUT_DEVICE_STATUS_OK, Params(), + std::string(kReturnedDeviceId)); + provider_binding_.emplace(provider_.get(), + std::move(stream_provider_request)); + } else { + std::move(callback).Run( + media::OutputDeviceStatus::OUTPUT_DEVICE_STATUS_ERROR_NOT_AUTHORIZED, + Params(), std::string("")); + } + expect_request_ = false; + } + + void PrepareProviderForAuthorization( + int64_t session_id, + const std::string& device_id, + std::unique_ptr<media::mojom::AudioOutputStreamProvider> provider) { + EXPECT_FALSE(expect_request_); + expect_request_ = true; + expected_session_id_ = session_id; + expected_device_id_ = device_id; + provider_binding_.reset(); + std::swap(provider_, provider); + } + + void RefuseNextRequest(int64_t session_id, const std::string& device_id) { + EXPECT_FALSE(expect_request_); + expect_request_ = true; + expected_session_id_ = session_id; + expected_device_id_ = device_id; + } + + void Disconnect() { + binding_.Close(); + this_proxy_.reset(); + binding_.Bind(mojo::MakeRequest(&this_proxy_)); + provider_binding_.reset(); + provider_.reset(); + expect_request_ = false; + } + + MojoAudioOutputIPC::FactoryAccessorCB GetAccessor() { + return base::BindRepeating(&TestRemoteFactory::get, base::Unretained(this)); + } + + private: + mojom::RendererAudioOutputStreamFactory* get() { return this_proxy_.get(); } + + bool expect_request_; + int64_t expected_session_id_; + std::string expected_device_id_; + + mojom::RendererAudioOutputStreamFactoryPtr this_proxy_; + mojo::Binding<mojom::RendererAudioOutputStreamFactory> binding_; + std::unique_ptr<media::mojom::AudioOutputStreamProvider> provider_; + base::Optional<mojo::Binding<media::mojom::AudioOutputStreamProvider>> + provider_binding_; +}; + +class TestStreamProvider : public media::mojom::AudioOutputStreamProvider { + public: + explicit TestStreamProvider(media::mojom::AudioOutputStream* stream) + : stream_(stream) {} + + ~TestStreamProvider() override { + // If we expected a stream to be acquired, make sure it is so. + if (stream_) + EXPECT_TRUE(binding_); + } + + void Acquire(media::mojom::AudioOutputStreamRequest stream_request, + const media::AudioParameters& params, + const AcquireCallback& callback) override { + EXPECT_EQ(binding_, base::nullopt); + EXPECT_NE(stream_, nullptr); + binding_.emplace(stream_, std::move(stream_request)); + base::CancelableSyncSocket foreign_socket; + EXPECT_TRUE( + base::CancelableSyncSocket::CreatePair(&socket_, &foreign_socket)); + std::move(callback).Run(mojo::SharedBufferHandle::Create(kMemoryLength), + mojo::WrapPlatformFile(foreign_socket.Release())); + } + + private: + media::mojom::AudioOutputStream* stream_; + base::Optional<mojo::Binding<media::mojom::AudioOutputStream>> binding_; + base::CancelableSyncSocket socket_; +}; + +class MockStream : public media::mojom::AudioOutputStream { + public: + MOCK_METHOD0(Play, void()); + MOCK_METHOD0(Pause, void()); + MOCK_METHOD1(SetVolume, void(double)); +}; + +class MockDelegate : public media::AudioOutputIPCDelegate { + public: + MockDelegate() {} + ~MockDelegate() override {} + + void OnStreamCreated(base::SharedMemoryHandle mem_handle, + base::SyncSocket::Handle socket_handle, + int length) { + base::SharedMemory sh_mem( + mem_handle, /*read_only*/ false); // Releases the shared memory handle. + base::SyncSocket socket(socket_handle); // Releases the socket descriptor. + GotOnStreamCreated(length); + } + + MOCK_METHOD0(OnError, void()); + MOCK_METHOD3(OnDeviceAuthorized, + void(media::OutputDeviceStatus device_status, + const media::AudioParameters& output_params, + const std::string& matched_device_id)); + MOCK_METHOD1(GotOnStreamCreated, void(int length)); + MOCK_METHOD0(OnIPCClosed, void()); +}; + +} // namespace + +TEST(MojoAudioOutputIPC, AuthorizeWithoutFactory_CallsOnIPCClosed) { + base::MessageLoopForIO message_loop; + StrictMock<MockDelegate> delegate; + + const std::unique_ptr<media::AudioOutputIPC> ipc = + base::MakeUnique<MojoAudioOutputIPC>(NullAccessor()); + + EXPECT_CALL(delegate, OnIPCClosed()); + ipc->RequestDeviceAuthorization(&delegate, kSessionId, kDeviceId, Origin()); + base::RunLoop().RunUntilIdle(); +} + +TEST(MojoAudioOutputIPC, CreateWithoutFactoryOrAuthorization_CallsOnIPCClosed) { + base::MessageLoopForIO message_loop; + StrictMock<MockDelegate> delegate; + + const std::unique_ptr<media::AudioOutputIPC> ipc = + base::MakeUnique<MojoAudioOutputIPC>(NullAccessor()); + + EXPECT_CALL(delegate, OnIPCClosed()); + ipc->CreateStream(&delegate, Params()); + base::RunLoop().RunUntilIdle(); +} + +TEST(MojoAudioOutputIPC, DeviceAuthorized_Propagates) { + base::MessageLoopForIO message_loop; + TestRemoteFactory stream_factory; + StrictMock<MockDelegate> delegate; + + const std::unique_ptr<media::AudioOutputIPC> ipc = + base::MakeUnique<MojoAudioOutputIPC>(stream_factory.GetAccessor()); + stream_factory.PrepareProviderForAuthorization( + kSessionId, kDeviceId, base::MakeUnique<TestStreamProvider>(nullptr)); + + ipc->RequestDeviceAuthorization(&delegate, kSessionId, kDeviceId, Origin()); + + EXPECT_CALL(delegate, OnDeviceAuthorized( + media::OutputDeviceStatus::OUTPUT_DEVICE_STATUS_OK, + _, std::string(kReturnedDeviceId))); + base::RunLoop().RunUntilIdle(); + + ipc->CloseStream(); + base::RunLoop().RunUntilIdle(); +} + +TEST(MojoAudioOutputIPC, OnDeviceCreated_Propagates) { + base::MessageLoopForIO message_loop; + TestRemoteFactory stream_factory; + StrictMock<MockStream> stream; + StrictMock<MockDelegate> delegate; + + const std::unique_ptr<media::AudioOutputIPC> ipc = + base::MakeUnique<MojoAudioOutputIPC>(stream_factory.GetAccessor()); + stream_factory.PrepareProviderForAuthorization( + kSessionId, kDeviceId, base::MakeUnique<TestStreamProvider>(&stream)); + + ipc->RequestDeviceAuthorization(&delegate, kSessionId, kDeviceId, Origin()); + ipc->CreateStream(&delegate, Params()); + + EXPECT_CALL(delegate, OnDeviceAuthorized( + media::OutputDeviceStatus::OUTPUT_DEVICE_STATUS_OK, + _, std::string(kReturnedDeviceId))); + EXPECT_CALL(delegate, GotOnStreamCreated(kMemoryLength)); + base::RunLoop().RunUntilIdle(); + + ipc->CloseStream(); + base::RunLoop().RunUntilIdle(); +} + +TEST(MojoAudioOutputIPC, + CreateWithoutAuthorization_RequestsAuthorizationFirst) { + base::MessageLoopForIO message_loop; + TestRemoteFactory stream_factory; + StrictMock<MockStream> stream; + StrictMock<MockDelegate> delegate; + const std::unique_ptr<media::AudioOutputIPC> ipc = + base::MakeUnique<MojoAudioOutputIPC>(stream_factory.GetAccessor()); + + // Note: This call implicitly EXPECTs that authorization is requested, + // and constructing the TestStreamProvider with a |&stream| EXPECTs that the + // stream is created. This implicit request should always be for the default + // device and no session id. + stream_factory.PrepareProviderForAuthorization( + 0, std::string(media::AudioDeviceDescription::kDefaultDeviceId), + base::MakeUnique<TestStreamProvider>(&stream)); + + ipc->CreateStream(&delegate, Params()); + + EXPECT_CALL(delegate, GotOnStreamCreated(kMemoryLength)); + base::RunLoop().RunUntilIdle(); + + ipc->CloseStream(); + base::RunLoop().RunUntilIdle(); +} + +TEST(MojoAudioOutputIPC, IsReusable) { + base::MessageLoopForIO message_loop; + TestRemoteFactory stream_factory; + StrictMock<MockStream> stream; + StrictMock<MockDelegate> delegate; + + const std::unique_ptr<media::AudioOutputIPC> ipc = + base::MakeUnique<MojoAudioOutputIPC>(stream_factory.GetAccessor()); + + for (int i = 0; i < 5; ++i) { + stream_factory.PrepareProviderForAuthorization( + kSessionId, kDeviceId, base::MakeUnique<TestStreamProvider>(&stream)); + + ipc->RequestDeviceAuthorization(&delegate, kSessionId, kDeviceId, Origin()); + ipc->CreateStream(&delegate, Params()); + + EXPECT_CALL( + delegate, + OnDeviceAuthorized(media::OutputDeviceStatus::OUTPUT_DEVICE_STATUS_OK, + _, std::string(kReturnedDeviceId))); + EXPECT_CALL(delegate, GotOnStreamCreated(kMemoryLength)); + base::RunLoop().RunUntilIdle(); + Mock::VerifyAndClearExpectations(&delegate); + + ipc->CloseStream(); + base::RunLoop().RunUntilIdle(); + } +} + +TEST(MojoAudioOutputIPC, IsReusableAfterError) { + base::MessageLoopForIO message_loop; + TestRemoteFactory stream_factory; + StrictMock<MockStream> stream; + StrictMock<MockDelegate> delegate; + + const std::unique_ptr<media::AudioOutputIPC> ipc = + base::MakeUnique<MojoAudioOutputIPC>(stream_factory.GetAccessor()); + + stream_factory.PrepareProviderForAuthorization( + kSessionId, kDeviceId, base::MakeUnique<TestStreamProvider>(nullptr)); + ipc->RequestDeviceAuthorization(&delegate, kSessionId, kDeviceId, Origin()); + + EXPECT_CALL(delegate, OnDeviceAuthorized( + media::OutputDeviceStatus::OUTPUT_DEVICE_STATUS_OK, + _, std::string(kReturnedDeviceId))); + base::RunLoop().RunUntilIdle(); + Mock::VerifyAndClearExpectations(&delegate); + + EXPECT_CALL(delegate, OnError()).Times(AtLeast(1)); + stream_factory.Disconnect(); + base::RunLoop().RunUntilIdle(); + Mock::VerifyAndClearExpectations(&delegate); + + ipc->CloseStream(); + base::RunLoop().RunUntilIdle(); + + for (int i = 0; i < 5; ++i) { + stream_factory.PrepareProviderForAuthorization( + kSessionId, kDeviceId, base::MakeUnique<TestStreamProvider>(&stream)); + + ipc->RequestDeviceAuthorization(&delegate, kSessionId, kDeviceId, Origin()); + ipc->CreateStream(&delegate, Params()); + + EXPECT_CALL( + delegate, + OnDeviceAuthorized(media::OutputDeviceStatus::OUTPUT_DEVICE_STATUS_OK, + _, std::string(kReturnedDeviceId))); + EXPECT_CALL(delegate, GotOnStreamCreated(kMemoryLength)); + base::RunLoop().RunUntilIdle(); + Mock::VerifyAndClearExpectations(&delegate); + + ipc->CloseStream(); + base::RunLoop().RunUntilIdle(); + } +} + +TEST(MojoAudioOutputIPC, DeviceNotAuthorized_Propagates) { + base::MessageLoopForIO message_loop; + TestRemoteFactory stream_factory; + StrictMock<MockDelegate> delegate; + + const std::unique_ptr<media::AudioOutputIPC> ipc = + base::MakeUnique<MojoAudioOutputIPC>(stream_factory.GetAccessor()); + stream_factory.RefuseNextRequest(kSessionId, kDeviceId); + + ipc->RequestDeviceAuthorization(&delegate, kSessionId, kDeviceId, Origin()); + + EXPECT_CALL( + delegate, + OnDeviceAuthorized( + media::OutputDeviceStatus::OUTPUT_DEVICE_STATUS_ERROR_NOT_AUTHORIZED, + _, std::string())); + EXPECT_CALL(delegate, OnError()).Times(AtLeast(0)); + base::RunLoop().RunUntilIdle(); + + ipc->CloseStream(); + base::RunLoop().RunUntilIdle(); +} + +TEST(MojoAudioOutputIPC, + FactoryDisconnectedBeforeAuthorizationReply_CallsAuthorizedAnyways) { + // The authorization IPC message might be aborted by the remote end + // disconnecting. In this case, the MojoAudioOutputIPC object must still + // send a notification to unblock the AudioOutputIPCDelegate. + base::MessageLoopForIO message_loop; + TestRemoteFactory stream_factory; + StrictMock<MockDelegate> delegate; + + const std::unique_ptr<media::AudioOutputIPC> ipc = + base::MakeUnique<MojoAudioOutputIPC>(stream_factory.GetAccessor()); + + ipc->RequestDeviceAuthorization(&delegate, kSessionId, kDeviceId, Origin()); + + EXPECT_CALL( + delegate, + OnDeviceAuthorized( + media::OutputDeviceStatus::OUTPUT_DEVICE_STATUS_ERROR_INTERNAL, _, + std::string())); + stream_factory.Disconnect(); + EXPECT_CALL(delegate, OnError()); + base::RunLoop().RunUntilIdle(); + + ipc->CloseStream(); + base::RunLoop().RunUntilIdle(); +} + +TEST(MojoAudioOutputIPC, + FactoryDisconnectedAfterAuthorizationReply_CallsAuthorizedOnlyOnce) { + // This test makes sure that the MojoAudioOutputIPC doesn't callback for + // authorization when the factory disconnects if it already got a callback + // for authorization. + base::MessageLoopForIO message_loop; + TestRemoteFactory stream_factory; + stream_factory.PrepareProviderForAuthorization( + kSessionId, kDeviceId, base::MakeUnique<TestStreamProvider>(nullptr)); + StrictMock<MockDelegate> delegate; + + const std::unique_ptr<media::AudioOutputIPC> ipc = + base::MakeUnique<MojoAudioOutputIPC>(stream_factory.GetAccessor()); + + ipc->RequestDeviceAuthorization(&delegate, kSessionId, kDeviceId, Origin()); + + EXPECT_CALL(delegate, OnDeviceAuthorized( + media::OutputDeviceStatus::OUTPUT_DEVICE_STATUS_OK, + _, std::string(kReturnedDeviceId))); + base::RunLoop().RunUntilIdle(); + + stream_factory.Disconnect(); + EXPECT_CALL(delegate, OnError()); + base::RunLoop().RunUntilIdle(); + + ipc->CloseStream(); + base::RunLoop().RunUntilIdle(); +} + +TEST(MojoAudioOutputIPC, AuthorizeNoClose_DCHECKs) { + base::MessageLoopForIO message_loop; + TestRemoteFactory stream_factory; + StrictMock<MockDelegate> delegate; + + stream_factory.PrepareProviderForAuthorization( + kSessionId, kDeviceId, base::MakeUnique<TestStreamProvider>(nullptr)); + + std::unique_ptr<media::AudioOutputIPC> ipc = + base::MakeUnique<MojoAudioOutputIPC>(stream_factory.GetAccessor()); + + ipc->RequestDeviceAuthorization(&delegate, kSessionId, kDeviceId, Origin()); + EXPECT_DCHECK_DEATH(ipc.reset()); + ipc->CloseStream(); + ipc.reset(); + base::RunLoop().RunUntilIdle(); +} + +TEST(MojoAudioOutputIPC, CreateNoClose_DCHECKs) { + base::MessageLoopForIO message_loop; + TestRemoteFactory stream_factory; + StrictMock<MockDelegate> delegate; + StrictMock<MockStream> stream; + + stream_factory.PrepareProviderForAuthorization( + 0, std::string(media::AudioDeviceDescription::kDefaultDeviceId), + base::MakeUnique<TestStreamProvider>(&stream)); + + std::unique_ptr<media::AudioOutputIPC> ipc = + base::MakeUnique<MojoAudioOutputIPC>(stream_factory.GetAccessor()); + + ipc->CreateStream(&delegate, Params()); + EXPECT_DCHECK_DEATH(ipc.reset()); + ipc->CloseStream(); + ipc.reset(); + base::RunLoop().RunUntilIdle(); +} + +TEST(MojoAudioOutputIPC, Play_Plays) { + base::MessageLoopForIO message_loop; + TestRemoteFactory stream_factory; + StrictMock<MockStream> stream; + StrictMock<MockDelegate> delegate; + + const std::unique_ptr<media::AudioOutputIPC> ipc = + base::MakeUnique<MojoAudioOutputIPC>(stream_factory.GetAccessor()); + stream_factory.PrepareProviderForAuthorization( + kSessionId, kDeviceId, base::MakeUnique<TestStreamProvider>(&stream)); + + ipc->RequestDeviceAuthorization(&delegate, kSessionId, kDeviceId, Origin()); + ipc->CreateStream(&delegate, Params()); + ipc->PlayStream(); + + EXPECT_CALL(delegate, OnDeviceAuthorized( + media::OutputDeviceStatus::OUTPUT_DEVICE_STATUS_OK, + _, std::string(kReturnedDeviceId))); + EXPECT_CALL(delegate, GotOnStreamCreated(kMemoryLength)); + EXPECT_CALL(stream, Play()); + base::RunLoop().RunUntilIdle(); + + ipc->CloseStream(); + base::RunLoop().RunUntilIdle(); +} + +TEST(MojoAudioOutputIPC, Pause_Pauses) { + base::MessageLoopForIO message_loop; + TestRemoteFactory stream_factory; + StrictMock<MockStream> stream; + StrictMock<MockDelegate> delegate; + + const std::unique_ptr<media::AudioOutputIPC> ipc = + base::MakeUnique<MojoAudioOutputIPC>(stream_factory.GetAccessor()); + stream_factory.PrepareProviderForAuthorization( + kSessionId, kDeviceId, base::MakeUnique<TestStreamProvider>(&stream)); + + ipc->RequestDeviceAuthorization(&delegate, kSessionId, kDeviceId, Origin()); + ipc->CreateStream(&delegate, Params()); + ipc->PauseStream(); + + EXPECT_CALL(delegate, OnDeviceAuthorized( + media::OutputDeviceStatus::OUTPUT_DEVICE_STATUS_OK, + _, std::string(kReturnedDeviceId))); + EXPECT_CALL(delegate, GotOnStreamCreated(kMemoryLength)); + EXPECT_CALL(stream, Pause()); + base::RunLoop().RunUntilIdle(); + + ipc->CloseStream(); + base::RunLoop().RunUntilIdle(); +} + +TEST(MojoAudioOutputIPC, SetVolume_SetsVolume) { + base::MessageLoopForIO message_loop; + TestRemoteFactory stream_factory; + StrictMock<MockStream> stream; + StrictMock<MockDelegate> delegate; + + const std::unique_ptr<media::AudioOutputIPC> ipc = + base::MakeUnique<MojoAudioOutputIPC>(stream_factory.GetAccessor()); + stream_factory.PrepareProviderForAuthorization( + kSessionId, kDeviceId, base::MakeUnique<TestStreamProvider>(&stream)); + + ipc->RequestDeviceAuthorization(&delegate, kSessionId, kDeviceId, Origin()); + ipc->CreateStream(&delegate, Params()); + ipc->SetVolume(kNewVolume); + + EXPECT_CALL(delegate, OnDeviceAuthorized( + media::OutputDeviceStatus::OUTPUT_DEVICE_STATUS_OK, + _, std::string(kReturnedDeviceId))); + EXPECT_CALL(delegate, GotOnStreamCreated(kMemoryLength)); + EXPECT_CALL(stream, SetVolume(kNewVolume)); + base::RunLoop().RunUntilIdle(); + + ipc->CloseStream(); + base::RunLoop().RunUntilIdle(); +} + +} // namespace content
diff --git a/content/test/BUILD.gn b/content/test/BUILD.gn index d2ec9636..ff1edbd 100644 --- a/content/test/BUILD.gn +++ b/content/test/BUILD.gn
@@ -1219,6 +1219,7 @@ "../browser/payments/payment_app_content_unittest_base.h", "../browser/payments/payment_app_provider_impl_unittest.cc", "../browser/payments/payment_manager_unittest.cc", + "../browser/permissions/permission_service_impl_unittest.cc", "../browser/presentation/presentation_service_impl_unittest.cc", "../browser/renderer_host/clipboard_message_filter_unittest.cc", "../browser/renderer_host/compositor_resize_lock_unittest.cc", @@ -1416,6 +1417,7 @@ "../renderer/media/audio_renderer_sink_cache_unittest.cc", "../renderer/media/mock_audio_device_factory.cc", "../renderer/media/mock_audio_device_factory.h", + "../renderer/media/mojo_audio_output_ipc_unittest.cc", "../renderer/media/render_media_log_unittest.cc", "../renderer/media/renderer_webaudiodevice_impl_unittest.cc", "../renderer/media/video_capture_impl_manager_unittest.cc",
diff --git a/content/test/test_render_frame_host.cc b/content/test/test_render_frame_host.cc index 8924bc2..f18014f7 100644 --- a/content/test/test_render_frame_host.cc +++ b/content/test/test_render_frame_host.cc
@@ -267,6 +267,16 @@ SendNavigate(0, did_create_new_entry, url); } +void TestRenderFrameHost::SimulateFeaturePolicyHeader( + blink::WebFeaturePolicyFeature feature, + const std::vector<url::Origin>& whitelist) { + content::ParsedFeaturePolicyHeader header(1); + header[0].feature = feature; + header[0].matches_all_origins = false; + header[0].origins = whitelist; + OnDidSetFeaturePolicyHeader(header); +} + void TestRenderFrameHost::SendNavigate(int nav_entry_id, bool did_create_new_entry, const GURL& url) {
diff --git a/content/test/test_render_frame_host.h b/content/test/test_render_frame_host.h index 8ec23008..bf9d93ed 100644 --- a/content/test/test_render_frame_host.h +++ b/content/test/test_render_frame_host.h
@@ -78,6 +78,9 @@ void SimulateSwapOutACK() override; void NavigateAndCommitRendererInitiated(bool did_create_new_entry, const GURL& url) override; + void SimulateFeaturePolicyHeader( + blink::WebFeaturePolicyFeature feature, + const std::vector<url::Origin>& whitelist) override; void SendNavigateWithReplacement(int nav_entry_id, bool did_create_new_entry,
diff --git a/device/geolocation/geolocation_service_impl.cc b/device/geolocation/geolocation_service_impl.cc index 357d9cd..ebc145d 100644 --- a/device/geolocation/geolocation_service_impl.cc +++ b/device/geolocation/geolocation_service_impl.cc
@@ -118,14 +118,14 @@ } void GeolocationServiceImpl::QueryNextPosition( - const QueryNextPositionCallback& callback) { + QueryNextPositionCallback callback) { if (!position_callback_.is_null()) { DVLOG(1) << "Overlapped call to QueryNextPosition!"; OnConnectionError(); // Simulate a connection error. return; } - position_callback_ = callback; + position_callback_ = std::move(callback); if (has_position_to_report_) ReportCurrentPosition(); @@ -178,8 +178,7 @@ } void GeolocationServiceImpl::ReportCurrentPosition() { - position_callback_.Run(current_position_.Clone()); - position_callback_.Reset(); + std::move(position_callback_).Run(current_position_.Clone()); has_position_to_report_ = false; }
diff --git a/device/geolocation/geolocation_service_impl.h b/device/geolocation/geolocation_service_impl.h index 60ce117..a377cce 100644 --- a/device/geolocation/geolocation_service_impl.h +++ b/device/geolocation/geolocation_service_impl.h
@@ -42,7 +42,7 @@ private: // mojom::GeolocationService: void SetHighAccuracy(bool high_accuracy) override; - void QueryNextPosition(const QueryNextPositionCallback& callback) override; + void QueryNextPosition(QueryNextPositionCallback callback) override; void OnConnectionError();
diff --git a/device/geolocation/public/interfaces/BUILD.gn b/device/geolocation/public/interfaces/BUILD.gn index 974c18e..65276d1 100644 --- a/device/geolocation/public/interfaces/BUILD.gn +++ b/device/geolocation/public/interfaces/BUILD.gn
@@ -9,9 +9,6 @@ "geolocation.mojom", ] - # TODO(crbug.com/714018): Convert the implementation to use OnceCallback. - use_once_callback = false - # TODO(crbug.com/699569): Convert to use the new JS bindings. use_new_js_bindings = false }
diff --git a/gpu/command_buffer/service/scheduler.cc b/gpu/command_buffer/service/scheduler.cc index b77287d..e854cf1 100644 --- a/gpu/command_buffer/service/scheduler.cc +++ b/gpu/command_buffer/service/scheduler.cc
@@ -25,6 +25,10 @@ SequenceId sequence_id() const { return sequence_id_; } + const scoped_refptr<SyncPointOrderData>& order_data() const { + return order_data_; + } + const SchedulingState& scheduling_state() const { return scheduling_state_; } bool enabled() const { return enabled_; } @@ -49,9 +53,8 @@ // Sets running state to SCHEDULED. void SetScheduled(); - // Called before running the next task on the sequence. Returns the closure - // for the task. Sets running state to RUNNING. - base::OnceClosure BeginTask(); + // Returns the next order number and closure. Sets running state to RUNNING. + uint32_t BeginTask(base::OnceClosure* closure); // Called after running the closure returned by BeginTask. Sets running state // to IDLE. @@ -206,7 +209,9 @@ void Scheduler::Sequence::ContinueTask(base::OnceClosure closure) { DCHECK_EQ(running_state_, RUNNING); - tasks_.push_front({std::move(closure), order_data_->current_order_num()}); + uint32_t order_num = order_data_->current_order_num(); + tasks_.push_front({std::move(closure), order_num}); + order_data_->PauseProcessingOrderNumber(order_num); } uint32_t Scheduler::Sequence::ScheduleTask(base::OnceClosure closure) { @@ -215,32 +220,25 @@ return order_num; } -base::OnceClosure Scheduler::Sequence::BeginTask() { +uint32_t Scheduler::Sequence::BeginTask(base::OnceClosure* closure) { + DCHECK(closure); DCHECK(!tasks_.empty()); DCHECK_EQ(running_state_, SCHEDULED); running_state_ = RUNNING; - base::OnceClosure closure = std::move(tasks_.front().closure); + *closure = std::move(tasks_.front().closure); uint32_t order_num = tasks_.front().order_num; tasks_.pop_front(); - order_data_->BeginProcessingOrderNumber(order_num); - UpdateSchedulingState(); - return closure; + return order_num; } void Scheduler::Sequence::FinishTask() { DCHECK_EQ(running_state_, RUNNING); running_state_ = IDLE; - uint32_t order_num = order_data_->current_order_num(); - if (!tasks_.empty() && tasks_.front().order_num == order_num) { - order_data_->PauseProcessingOrderNumber(order_num); - } else { - order_data_->FinishProcessingOrderNumber(order_num); - } UpdateSchedulingState(); } @@ -472,16 +470,26 @@ TRACE_EVENT1("gpu", "Scheduler::RunNextTask", "state", state.AsValue()); - DCHECK(GetSequence(state.sequence_id)); - base::OnceClosure closure = GetSequence(state.sequence_id)->BeginTask(); + Sequence* sequence = GetSequence(state.sequence_id); + DCHECK(sequence); + base::OnceClosure closure; + uint32_t order_num = sequence->BeginTask(&closure); + DCHECK_EQ(order_num, state.order_num); + + // Begin/FinishProcessingOrderNumber must be called with the lock released + // because they can renter the scheduler in Enable/DisableSequence. + scoped_refptr<SyncPointOrderData> order_data = sequence->order_data(); { base::AutoUnlock auto_unlock(lock_); + order_data->BeginProcessingOrderNumber(order_num); std::move(closure).Run(); + if (order_data->IsProcessingOrderNumber()) + order_data->FinishProcessingOrderNumber(order_num); } // Check if sequence hasn't been destroyed. - Sequence* sequence = GetSequence(state.sequence_id); + sequence = GetSequence(state.sequence_id); if (sequence) { sequence->FinishTask(); if (sequence->IsRunnable()) {
diff --git a/gpu/command_buffer/service/scheduler_unittest.cc b/gpu/command_buffer/service/scheduler_unittest.cc index 43328b3..dd0adad 100644 --- a/gpu/command_buffer/service/scheduler_unittest.cc +++ b/gpu/command_buffer/service/scheduler_unittest.cc
@@ -331,5 +331,59 @@ release_state->Destroy(); } +TEST_F(SchedulerTest, ReentrantEnableSequenceShouldNotDeadlock) { + SequenceId sequence_id1 = + scheduler()->CreateSequence(SchedulingPriority::kHigh); + CommandBufferNamespace namespace_id = CommandBufferNamespace::GPU_IO; + CommandBufferId command_buffer_id1 = CommandBufferId::FromUnsafeValue(1); + scoped_refptr<SyncPointClientState> release_state1 = + sync_point_manager()->CreateSyncPointClientState( + namespace_id, command_buffer_id1, sequence_id1); + + SequenceId sequence_id2 = + scheduler()->CreateSequence(SchedulingPriority::kNormal); + CommandBufferId command_buffer_id2 = CommandBufferId::FromUnsafeValue(2); + scoped_refptr<SyncPointClientState> release_state2 = + sync_point_manager()->CreateSyncPointClientState( + namespace_id, command_buffer_id2, sequence_id2); + + uint64_t release = 1; + SyncToken sync_token(namespace_id, 0 /* extra_data_field */, + command_buffer_id2, release); + + bool ran1, ran2 = false; + + // Schedule task on sequence 2 first so that the sync token wait isn't a nop. + // BeginProcessingOrderNumber for this task will run the EnableSequence + // callback. This should not deadlock. + scheduler()->ScheduleTask(sequence_id2, GetClosure([&] { ran2 = true; }), + std::vector<SyncToken>()); + + // This will run first because of the higher priority and no scheduling sync + // token dependencies. + scheduler()->ScheduleTask( + sequence_id1, GetClosure([&] { + ran1 = true; + release_state1->Wait( + sync_token, + base::Bind(&Scheduler::EnableSequence, + base::Unretained(scheduler()), sequence_id1)); + scheduler()->DisableSequence(sequence_id1); + }), + std::vector<SyncToken>()); + + task_runner()->RunPendingTasks(); + EXPECT_TRUE(ran1); + EXPECT_FALSE(ran2); + EXPECT_FALSE(sync_point_manager()->IsSyncTokenReleased(sync_token)); + + task_runner()->RunPendingTasks(); + EXPECT_TRUE(ran2); + EXPECT_FALSE(sync_point_manager()->IsSyncTokenReleased(sync_token)); + + release_state1->Destroy(); + release_state2->Destroy(); +} + } // namespace } // namespace gpu
diff --git a/media/audio/audio_output_ipc.h b/media/audio/audio_output_ipc.h index 403081b..b6fd58d 100644 --- a/media/audio/audio_output_ipc.h +++ b/media/audio/audio_output_ipc.h
@@ -97,7 +97,10 @@ virtual void PauseStream() = 0; // Closes the audio stream which should shut down the corresponding - // AudioOutputController in the peer process. + // AudioOutputController in the peer process. Usage of an AudioOutputIPC must + // always end with a call to CloseStream(), and the |delegate| passed to other + // method must remain valid until then. An exception is if OnIPCClosed is + // called first. virtual void CloseStream() = 0; // Sets the volume of the audio stream.
diff --git a/media/mojo/interfaces/audio_output_stream.mojom b/media/mojo/interfaces/audio_output_stream.mojom index 67926c3d..5dccd9d4 100644 --- a/media/mojo/interfaces/audio_output_stream.mojom +++ b/media/mojo/interfaces/audio_output_stream.mojom
@@ -25,7 +25,12 @@ interface AudioOutputStreamProvider { // Creates a new AudioOutputStream using |params|. |shared_buffer| and // |socket_descriptor| are used to write data to the stream as defined - // in AudioDeviceThread. Can only be called once. + // in AudioDeviceThread. This means: + // * |shared_buffer| will be a writable handle to memory of the size needed + // by AudioDeviceThread for the specified params. + // * |socket_descriptor| is a descriptor from which a + // base::CancelableSyncSocket can be constructed. + // Can only be called once. Acquire(AudioOutputStream& output_stream, AudioParameters params) => (handle<shared_buffer> shared_buffer, handle socket_descriptor); };
diff --git a/mojo/edk/system/core_unittest.cc b/mojo/edk/system/core_unittest.cc index 0d60b48a..36b965b 100644 --- a/mojo/edk/system/core_unittest.cc +++ b/mojo/edk/system/core_unittest.cc
@@ -258,50 +258,6 @@ } } -// These test invalid arguments that should cause death if we're being paranoid -// about checking arguments (which we would want to do if, e.g., we were in a -// true "kernel" situation, but we might not want to do otherwise for -// performance reasons). Probably blatant errors like passing in null pointers -// (for required pointer arguments) will still cause death, but perhaps not -// predictably. -TEST_F(CoreTest, InvalidArgumentsDeath) { -#if defined(OFFICIAL_BUILD) - const char kMemoryCheckFailedRegex[] = ""; -#else - const char kMemoryCheckFailedRegex[] = "Check failed"; -#endif - - // |CreateMessagePipe()|: - { - MojoHandle h; - ASSERT_DEATH_IF_SUPPORTED( - core()->CreateMessagePipe(nullptr, nullptr, nullptr), - kMemoryCheckFailedRegex); - ASSERT_DEATH_IF_SUPPORTED( - core()->CreateMessagePipe(nullptr, &h, nullptr), - kMemoryCheckFailedRegex); - ASSERT_DEATH_IF_SUPPORTED( - core()->CreateMessagePipe(nullptr, nullptr, &h), - kMemoryCheckFailedRegex); - } - - // |ReadMessage()|: - // Only check arguments checked by |Core|, namely |handle|, |handles|, and - // |num_handles|. - { - MockHandleInfo info; - MojoHandle h = CreateMockHandle(&info); - - uint32_t handle_count = 1; - ASSERT_DEATH_IF_SUPPORTED( - core()->ReadMessage(h, nullptr, nullptr, nullptr, &handle_count, - MOJO_READ_MESSAGE_FLAG_NONE), - kMemoryCheckFailedRegex); - - ASSERT_EQ(MOJO_RESULT_OK, core()->Close(h)); - } -} - TEST_F(CoreTest, MessagePipe) { MojoHandle h[2]; MojoHandleSignalsState hss[2];
diff --git a/services/device/battery/battery_monitor_impl.cc b/services/device/battery/battery_monitor_impl.cc index 1a134c0..f1f8dcb5 100644 --- a/services/device/battery/battery_monitor_impl.cc +++ b/services/device/battery/battery_monitor_impl.cc
@@ -30,14 +30,13 @@ BatteryMonitorImpl::~BatteryMonitorImpl() {} -void BatteryMonitorImpl::QueryNextStatus( - const QueryNextStatusCallback& callback) { +void BatteryMonitorImpl::QueryNextStatus(QueryNextStatusCallback callback) { if (!callback_.is_null()) { DVLOG(1) << "Overlapped call to QueryNextStatus!"; binding_->Close(); return; } - callback_ = callback; + callback_ = std::move(callback); if (status_to_report_) ReportStatus(); @@ -54,8 +53,7 @@ } void BatteryMonitorImpl::ReportStatus() { - callback_.Run(status_.Clone()); - callback_.Reset(); + std::move(callback_).Run(status_.Clone()); status_to_report_ = false; }
diff --git a/services/device/battery/battery_monitor_impl.h b/services/device/battery/battery_monitor_impl.h index 1b8d67f..511e7396 100644 --- a/services/device/battery/battery_monitor_impl.h +++ b/services/device/battery/battery_monitor_impl.h
@@ -23,7 +23,7 @@ private: // mojom::BatteryMonitor methods: - void QueryNextStatus(const QueryNextStatusCallback& callback) override; + void QueryNextStatus(QueryNextStatusCallback callback) override; void RegisterSubscription(); void DidChange(const mojom::BatteryStatus& battery_status);
diff --git a/services/device/fingerprint/fingerprint_chromeos.cc b/services/device/fingerprint/fingerprint_chromeos.cc index 777f7e2..80ab433 100644 --- a/services/device/fingerprint/fingerprint_chromeos.cc +++ b/services/device/fingerprint/fingerprint_chromeos.cc
@@ -17,9 +17,9 @@ constexpr int64_t kFingerprintSessionTimeoutMs = 150; // Used to convert mojo Callback to VoidDbusMethodCallback. -void RunFingerprintCallback(const base::Callback<void(bool)>& callback, +void RunFingerprintCallback(base::OnceCallback<void(bool)> callback, chromeos::DBusMethodCallStatus result) { - callback.Run(result == chromeos::DBUS_METHOD_CALL_SUCCESS); + std::move(callback).Run(result == chromeos::DBUS_METHOD_CALL_SUCCESS); } chromeos::BiodClient* GetBiodClient() { @@ -44,10 +44,11 @@ void FingerprintChromeOS::GetRecordsForUser( const std::string& user_id, - const GetRecordsForUserCallback& callback) { + GetRecordsForUserCallback callback) { chromeos::DBusThreadManager::Get()->GetBiodClient()->GetRecordsForUser( - user_id, base::Bind(&FingerprintChromeOS::OnGetRecordsForUser, - weak_ptr_factory_.GetWeakPtr(), callback)); + user_id, + base::Bind(&FingerprintChromeOS::OnGetRecordsForUser, + weak_ptr_factory_.GetWeakPtr(), base::Passed(&callback))); } void FingerprintChromeOS::StartEnrollSession(const std::string& user_id, @@ -85,35 +86,37 @@ } void FingerprintChromeOS::CancelCurrentEnrollSession( - const CancelCurrentEnrollSessionCallback& callback) { + CancelCurrentEnrollSessionCallback callback) { if (opened_session_ == FingerprintSession::ENROLL) { GetBiodClient()->CancelEnrollSession( - base::Bind(&RunFingerprintCallback, callback)); + base::Bind(&RunFingerprintCallback, base::Passed(&callback))); opened_session_ = FingerprintSession::NONE; } else { - callback.Run(true); + std::move(callback).Run(true); } } void FingerprintChromeOS::RequestRecordLabel( const std::string& record_path, - const RequestRecordLabelCallback& callback) { - GetBiodClient()->RequestRecordLabel(dbus::ObjectPath(record_path), callback); + RequestRecordLabelCallback callback) { + GetBiodClient()->RequestRecordLabel( + dbus::ObjectPath(record_path), + base::AdaptCallbackForRepeating(std::move(callback))); } -void FingerprintChromeOS::SetRecordLabel( - const std::string& new_label, - const std::string& record_path, - const SetRecordLabelCallback& callback) { +void FingerprintChromeOS::SetRecordLabel(const std::string& new_label, + const std::string& record_path, + SetRecordLabelCallback callback) { GetBiodClient()->SetRecordLabel( dbus::ObjectPath(record_path), new_label, - base::Bind(&RunFingerprintCallback, callback)); + base::Bind(&RunFingerprintCallback, base::Passed(&callback))); } void FingerprintChromeOS::RemoveRecord(const std::string& record_path, - const RemoveRecordCallback& callback) { - GetBiodClient()->RemoveRecord(dbus::ObjectPath(record_path), - base::Bind(&RunFingerprintCallback, callback)); + RemoveRecordCallback callback) { + GetBiodClient()->RemoveRecord( + dbus::ObjectPath(record_path), + base::Bind(&RunFingerprintCallback, base::Passed(&callback))); } void FingerprintChromeOS::StartAuthSession() { @@ -146,24 +149,25 @@ } void FingerprintChromeOS::EndCurrentAuthSession( - const EndCurrentAuthSessionCallback& callback) { + EndCurrentAuthSessionCallback callback) { if (opened_session_ == FingerprintSession::AUTH) { GetBiodClient()->EndAuthSession( - base::Bind(&RunFingerprintCallback, callback)); + base::Bind(&RunFingerprintCallback, base::Passed(&callback))); opened_session_ = FingerprintSession::NONE; } else { - callback.Run(true); + std::move(callback).Run(true); } } void FingerprintChromeOS::DestroyAllRecords( - const DestroyAllRecordsCallback& callback) { + DestroyAllRecordsCallback callback) { GetBiodClient()->DestroyAllRecords( - base::Bind(&RunFingerprintCallback, callback)); + base::Bind(&RunFingerprintCallback, base::Passed(&callback))); } -void FingerprintChromeOS::RequestType(const RequestTypeCallback& callback) { - GetBiodClient()->RequestType(callback); +void FingerprintChromeOS::RequestType(RequestTypeCallback callback) { + GetBiodClient()->RequestType( + base::AdaptCallbackForRepeating(std::move(callback))); } void FingerprintChromeOS::AddFingerprintObserver( @@ -242,28 +246,28 @@ } void FingerprintChromeOS::OnGetRecordsForUser( - const GetRecordsForUserCallback& callback, + GetRecordsForUserCallback callback, const std::vector<dbus::ObjectPath>& records) { records_path_to_label_.clear(); if (records.size() == 0) - callback.Run(records_path_to_label_); + std::move(callback).Run(records_path_to_label_); for (auto& record : records) { GetBiodClient()->RequestRecordLabel( record, base::Bind(&FingerprintChromeOS::OnGetLabelFromRecordPath, - weak_ptr_factory_.GetWeakPtr(), callback, - records.size(), record)); + weak_ptr_factory_.GetWeakPtr(), + base::Passed(&callback), records.size(), record)); } } void FingerprintChromeOS::OnGetLabelFromRecordPath( - const GetRecordsForUserCallback& callback, + GetRecordsForUserCallback callback, size_t num_records, const dbus::ObjectPath& record_path, const std::string& label) { records_path_to_label_[record_path.value()] = label; if (records_path_to_label_.size() == num_records) - callback.Run(records_path_to_label_); + std::move(callback).Run(records_path_to_label_); } // static
diff --git a/services/device/fingerprint/fingerprint_chromeos.h b/services/device/fingerprint/fingerprint_chromeos.h index 45d7274e..943e03a 100644 --- a/services/device/fingerprint/fingerprint_chromeos.h +++ b/services/device/fingerprint/fingerprint_chromeos.h
@@ -33,23 +33,22 @@ // mojom::Fingerprint: void GetRecordsForUser(const std::string& user_id, - const GetRecordsForUserCallback& callback) override; + GetRecordsForUserCallback callback) override; void StartEnrollSession(const std::string& user_id, const std::string& label) override; void CancelCurrentEnrollSession( - const CancelCurrentEnrollSessionCallback& callback) override; + CancelCurrentEnrollSessionCallback callback) override; void RequestRecordLabel(const std::string& record_path, - const RequestRecordLabelCallback& callback) override; + RequestRecordLabelCallback callback) override; void SetRecordLabel(const std::string& record_path, const std::string& new_label, - const SetRecordLabelCallback& callback) override; + SetRecordLabelCallback callback) override; void RemoveRecord(const std::string& record_path, - const RemoveRecordCallback& callback) override; + RemoveRecordCallback callback) override; void StartAuthSession() override; - void EndCurrentAuthSession( - const EndCurrentAuthSessionCallback& callback) override; - void DestroyAllRecords(const DestroyAllRecordsCallback& callback) override; - void RequestType(const RequestTypeCallback& callback) override; + void EndCurrentAuthSession(EndCurrentAuthSessionCallback callback) override; + void DestroyAllRecords(DestroyAllRecordsCallback callback) override; + void RequestType(RequestTypeCallback callback) override; void AddFingerprintObserver(mojom::FingerprintObserverPtr observer) override; private: @@ -68,9 +67,9 @@ void OnFingerprintObserverDisconnected(mojom::FingerprintObserver* observer); void OnStartEnrollSession(const dbus::ObjectPath& enroll_path); void OnStartAuthSession(const dbus::ObjectPath& auth_path); - void OnGetRecordsForUser(const GetRecordsForUserCallback& callback, + void OnGetRecordsForUser(GetRecordsForUserCallback callback, const std::vector<dbus::ObjectPath>& record_paths); - void OnGetLabelFromRecordPath(const GetRecordsForUserCallback& callback, + void OnGetLabelFromRecordPath(GetRecordsForUserCallback callback, size_t num_records, const dbus::ObjectPath& record_path, const std::string& label);
diff --git a/services/device/public/interfaces/BUILD.gn b/services/device/public/interfaces/BUILD.gn index ae3247b..b368d19 100644 --- a/services/device/public/interfaces/BUILD.gn +++ b/services/device/public/interfaces/BUILD.gn
@@ -20,9 +20,6 @@ ":constants", ] - # TODO(crbug.com/714018): Convert the implementation to use OnceCallback. - use_once_callback = false - # TODO(crbug.com/699569): Convert to use the new JS bindings. use_new_js_bindings = false }
diff --git a/services/device/vibration/vibration_manager_impl_default.cc b/services/device/vibration/vibration_manager_impl_default.cc index 60bfd11..5141bc71 100644 --- a/services/device/vibration/vibration_manager_impl_default.cc +++ b/services/device/vibration/vibration_manager_impl_default.cc
@@ -22,14 +22,14 @@ VibrationManagerEmptyImpl() {} ~VibrationManagerEmptyImpl() override {} - void Vibrate(int64_t milliseconds, const VibrateCallback& callback) override { + void Vibrate(int64_t milliseconds, VibrateCallback callback) override { VibrationManagerImpl::milli_seconds_for_testing_ = milliseconds; - callback.Run(); + std::move(callback).Run(); } - void Cancel(const CancelCallback& callback) override { + void Cancel(CancelCallback callback) override { VibrationManagerImpl::cancelled_for_testing_ = true; - callback.Run(); + std::move(callback).Run(); } };
diff --git a/third_party/WebKit/LayoutTests/TestExpectations b/third_party/WebKit/LayoutTests/TestExpectations index 096bd59..2d0a933e 100644 --- a/third_party/WebKit/LayoutTests/TestExpectations +++ b/third_party/WebKit/LayoutTests/TestExpectations
@@ -435,9 +435,6 @@ crbug.com/635619 virtual/layout_ng/external/wpt/css/CSS2/floats-clear/margin-collapse-clear-014.xht [ Failure ] ### virtual/layout_ng/external/wpt/css/CSS2/linebox -crbug.com/636993 virtual/layout_ng/external/wpt/css/CSS2/linebox/border-padding-bleed-001.xht [ Failure ] -crbug.com/636993 virtual/layout_ng/external/wpt/css/CSS2/linebox/border-padding-bleed-002.xht [ Failure ] -crbug.com/636993 virtual/layout_ng/external/wpt/css/CSS2/linebox/border-padding-bleed-003.xht [ Failure ] crbug.com/636993 virtual/layout_ng/external/wpt/css/CSS2/linebox/empty-inline-002.xht [ Crash Failure ] crbug.com/636993 virtual/layout_ng/external/wpt/css/CSS2/linebox/inline-formatting-context-001.xht [ Failure ] crbug.com/636993 virtual/layout_ng/external/wpt/css/CSS2/linebox/inline-formatting-context-002.xht [ Failure ] @@ -449,7 +446,6 @@ crbug.com/636993 virtual/layout_ng/external/wpt/css/CSS2/linebox/inline-formatting-context-010b.xht [ Skip ] crbug.com/636993 virtual/layout_ng/external/wpt/css/CSS2/linebox/inline-formatting-context-012.xht [ Failure ] crbug.com/636993 virtual/layout_ng/external/wpt/css/CSS2/linebox/inline-formatting-context-013.xht [ Failure ] -crbug.com/636993 virtual/layout_ng/external/wpt/css/CSS2/linebox/inline-formatting-context-022.xht [ Failure ] crbug.com/636993 virtual/layout_ng/external/wpt/css/CSS2/linebox/inline-formatting-context-023.xht [ Failure ] crbug.com/636993 virtual/layout_ng/external/wpt/css/CSS2/linebox/line-height-002.xht [ Failure ] crbug.com/636993 virtual/layout_ng/external/wpt/css/CSS2/linebox/line-height-004.xht [ Failure ] @@ -481,12 +477,9 @@ crbug.com/636993 virtual/layout_ng/external/wpt/css/CSS2/linebox/line-height-101.xht [ Failure ] crbug.com/636993 virtual/layout_ng/external/wpt/css/CSS2/linebox/line-height-103.xht [ Failure ] crbug.com/636993 virtual/layout_ng/external/wpt/css/CSS2/linebox/line-height-104.xht [ Failure ] -crbug.com/636993 virtual/layout_ng/external/wpt/css/CSS2/linebox/line-height-125.xht [ Failure ] crbug.com/636993 virtual/layout_ng/external/wpt/css/CSS2/linebox/line-height-129.xht [ Failure ] crbug.com/636993 virtual/layout_ng/external/wpt/css/CSS2/linebox/line-height-bleed-001.xht [ Failure ] crbug.com/636993 virtual/layout_ng/external/wpt/css/CSS2/linebox/line-height-bleed-002.xht [ Failure ] -crbug.com/636993 virtual/layout_ng/external/wpt/css/CSS2/linebox/vertical-align-117a.xht [ Failure ] -crbug.com/636993 virtual/layout_ng/external/wpt/css/CSS2/linebox/vertical-align-118a.xht [ Failure ] crbug.com/636993 virtual/layout_ng/external/wpt/css/CSS2/linebox/vertical-align-121.xht [ Failure ] crbug.com/636993 virtual/layout_ng/external/wpt/css/CSS2/linebox/vertical-align-baseline-004a.xht [ Failure ] crbug.com/636993 virtual/layout_ng/external/wpt/css/CSS2/linebox/vertical-align-baseline-005a.xht [ Failure ] @@ -3022,11 +3015,6 @@ crbug.com/724392 [ Win ] fast/css/font-family-case-insensitive.html [ Failure Pass Timeout ] crbug.com/713094 [ Win ] fast/css/fontfaceset-check-platform-fonts.html [ Failure Pass ] -# Sheriff failures 2017-05-17 -# Crashes -crbug.com/723841 http/tests/dom/script-module-load-incomplete-no-crash.html [ Skip ] -crbug.com/723841 virtual/mojo-loading/http/tests/dom/script-module-load-incomplete-no-crash.html [ Skip ] - # Sheriff failures 2017-05-22 crbug.com/725542 [ Debug ] editing/selection/doubleclick-beside-cr-span.html [ Pass Timeout ] crbug.com/725545 [ Debug ] fast/forms/number/number-stepup-stepdown-from-renderer.html [ Pass Timeout ] @@ -3048,3 +3036,6 @@ # Sheriff failures 2017-05-30 # Failing on try (with patch) and Linux FYI too frequently. crbug.com/727505 [ Linux ] virtual/threaded/fast/compositorworker/basic-plumbing-main-to-worker.html [ Pass Crash ] + +# Sheriff failures 2017-05-31 +crbug.com/727991 [ Linux Mac ] virtual/threaded/fast/compositorworker/visual-update.html [ Pass Failure ]
diff --git a/third_party/WebKit/LayoutTests/external/wpt/css/css-scoping-1/css-scoping-shadow-host-namespace.html b/third_party/WebKit/LayoutTests/external/wpt/css/css-scoping-1/css-scoping-shadow-host-namespace.html new file mode 100644 index 0000000..46dffc8d --- /dev/null +++ b/third_party/WebKit/LayoutTests/external/wpt/css/css-scoping-1/css-scoping-shadow-host-namespace.html
@@ -0,0 +1,82 @@ +<!DOCTYPE html> +<html> +<head> + <title>CSS Scoping Module Level 1 - :host, :host-context, and default @namespace</title> + <link rel="author" title="Rune Lillesveen" href="mailto:rune@opera.com"/> + <link rel="help" href="https://www.w3.org/TR/css-scoping-1/#host-selector"> + <link rel="help" href="https://www.w3.org/TR/css3-selectors/#typenmsp"> + <link rel="help" href="https://www.w3.org/TR/css3-selectors/#univnmsp"> + <link rel="match" href="reference/green-box.html"/> +</head> +<body> + <style> + .host { + display: block; + width: 100px; + height: 10px; + background: red; + } + #host-3, #host-5, #host-10 { + background: green; + } + </style> + <p>Test passes if you see a single 100px by 100px green box below.</p> + <div id="host-1" class="host">FAIL</div> + <div id="host-2" class="host">FAIL</div> + <div id="host-3" class="host">FAIL</div> + <div id="host-4" class="host">FAIL</div> + <div id="host-5" class="host">FAIL</div> + <div id="host-6" class="host">FAIL</div> + <div id="host-7" class="host">FAIL</div> + <div id="host-8" class="host">FAIL</div> + <div id="host-9" class="host">FAIL</div> + <div id="host-10" class="host">FAIL</div> + <script> + try { + var shadowHost = document.querySelector('#host-1'); + shadowRoot = shadowHost.attachShadow({mode: 'open'}); + shadowRoot.innerHTML = '<style>@namespace url(http://www.w3.org/1999/xhtml); :host { background: green !important; } </style>'; + + shadowHost = document.querySelector('#host-2'); + shadowRoot = shadowHost.attachShadow({mode: 'open'}); + shadowRoot.innerHTML = '<style>@namespace url(http://dummy); :host { background: green !important; } </style>'; + + shadowHost = document.querySelector('#host-3'); + shadowRoot = shadowHost.attachShadow({mode: 'open'}); + shadowRoot.innerHTML = '<style>@namespace url(http://dummy); :host(*) { background: red !important; } </style>'; + + shadowHost = document.querySelector('#host-4'); + shadowRoot = shadowHost.attachShadow({mode: 'open'}); + shadowRoot.innerHTML = '<style>@namespace url(http://dummy); :host(*|*) { background: green !important; } </style>'; + + shadowHost = document.querySelector('#host-5'); + shadowRoot = shadowHost.attachShadow({mode: 'open'}); + shadowRoot.innerHTML = '<style>@namespace url(http://dummy); :host-context(*) { background: red !important; } </style>'; + + shadowHost = document.querySelector('#host-6'); + shadowRoot = shadowHost.attachShadow({mode: 'open'}); + shadowRoot.innerHTML = '<style>@namespace url(http://dummy); :host-context(*|*) { background: green !important; } </style>'; + + shadowHost = document.querySelector('#host-7'); + shadowRoot = shadowHost.attachShadow({mode: 'open'}); + shadowRoot.innerHTML = '<style>@namespace url(http://www.w3.org/1999/xhtml); :host(*) { background: green !important; } </style>'; + + shadowHost = document.querySelector('#host-8'); + shadowRoot = shadowHost.attachShadow({mode: 'open'}); + shadowRoot.innerHTML = '<style>@namespace url(http://www.w3.org/1999/xhtml); :host-context(*) { background: green !important; } </style>'; + + shadowHost = document.querySelector('#host-9'); + shadowRoot = shadowHost.attachShadow({mode: 'open'}); + shadowRoot.innerHTML = '<style>@namespace url(http://www.w3.org/1999/xhtml); :host(div) { background: green !important; } </style>'; + + shadowHost = document.querySelector('#host-10'); + shadowRoot = shadowHost.attachShadow({mode: 'open'}); + shadowRoot.innerHTML = '<style>@namespace url(http://dummy); :host-context(html) { background: red !important; } </style>'; + + } catch (exception) { + document.body.appendChild(document.createTextNode(exception)); + } + + </script> +</body> +</html>
diff --git a/third_party/WebKit/LayoutTests/http/tests/inspector-unit/view-location-expected.txt b/third_party/WebKit/LayoutTests/http/tests/inspector-unit/view-location-expected.txt new file mode 100644 index 0000000..0bbce416 --- /dev/null +++ b/third_party/WebKit/LayoutTests/http/tests/inspector-unit/view-location-expected.txt
@@ -0,0 +1,18 @@ +Creating new TabbedLocation +[] +Appending three views +["first","second","third"] +Creating new TabbedLocation +["first","second","third"] +Re-order tabs +["third","first","second"] +Creating new TabbedLocation +["third","first","second"] +["third","first","second","fourth"] +Creating new TabbedLocation +["third","first","second","fourth"] +Closing second tab +["third","first","fourth"] +Creating new TabbedLocation +["third","first","fourth"] +
diff --git a/third_party/WebKit/LayoutTests/http/tests/inspector-unit/view-location.js b/third_party/WebKit/LayoutTests/http/tests/inspector-unit/view-location.js new file mode 100644 index 0000000..ddce77fc --- /dev/null +++ b/third_party/WebKit/LayoutTests/http/tests/inspector-unit/view-location.js
@@ -0,0 +1,54 @@ +runtime._registerModule({ + name: "mock-module", + extensions: ['first', 'second', 'third', 'fourth'].map(title => { + return { + "type": "view", + "location": "mock-location", + "id": title, + "title": title, + "persistence": "closeable", + "factoryName": "UI.Widget" + } + }), + scripts: [] +}); + +var tabbedLocation; +var viewManager; +createTabbedLocation(); +dumpTabs(); +TestRunner.addResult('Appending three views') +viewManager.showView('first'); +viewManager.showView('second'); +viewManager.showView('third'); +dumpTabs(); +createTabbedLocation(); +dumpTabs(); +TestRunner.addResult('Re-order tabs'); +tabbedLocation.tabbedPane()._insertBefore(tabbedLocation.tabbedPane()._tabsById.get("third"), 0); +dumpTabs(); +createTabbedLocation(); +dumpTabs(); +viewManager.showView('fourth'); +dumpTabs(); +createTabbedLocation(); +dumpTabs(); +TestRunner.addResult('Closing second tab'); +tabbedLocation.tabbedPane().closeTab('second'); +dumpTabs(); +createTabbedLocation(); +dumpTabs(); +TestRunner.completeTest(); + +function createTabbedLocation() { + TestRunner.addResult('Creating new TabbedLocation'); + if (tabbedLocation) + tabbedLocation.tabbedPane().detach(true); + viewManager = new UI.ViewManager(); + tabbedLocation = viewManager.createTabbedLocation(undefined, 'mock-location', true, true); + tabbedLocation.widget().show(UI.inspectorView.element); +} + +function dumpTabs() { + TestRunner.addResult(JSON.stringify(tabbedLocation.tabbedPane().tabIds())); +} \ No newline at end of file
diff --git a/third_party/WebKit/LayoutTests/imagecapture/MediaStreamTrack-applyConstraints-getSettings.html b/third_party/WebKit/LayoutTests/imagecapture/MediaStreamTrack-applyConstraints-getSettings.html index 7a183e6..f8a79852 100644 --- a/third_party/WebKit/LayoutTests/imagecapture/MediaStreamTrack-applyConstraints-getSettings.html +++ b/third_party/WebKit/LayoutTests/imagecapture/MediaStreamTrack-applyConstraints-getSettings.html
@@ -2,6 +2,7 @@ <script src="../resources/testharness.js"></script> <script src="../resources/testharnessreport.js"></script> <script src="../resources/mojo-helpers.js"></script> +<script src="resources/imagecapture-helpers.js"></script> <script src="resources/mock-imagecapture.js"></script> <body> <canvas id='canvas' width=10 height=10/> @@ -59,10 +60,8 @@ // TODO(mcasas): this shouldn't be needed, https://crbug.com/711524. return new Promise(resolve => setTimeout(resolve, 100)); }) - .then(function() { - return videoTrack.applyConstraints(constraints); - }) - .then(function() { + .then(() => videoTrack.applyConstraints(constraints)) + .then(appliedConstraints => { settings = videoTrack.getSettings(); assert_equals(typeof settings, 'object'); @@ -74,16 +73,9 @@ assert_equals(constraints.advanced[0].focusMode, settings.focusMode, 'focusMode'); - assert_equals(constraints.advanced[0].pointsOfInterest.length, - settings.pointsOfInterest.length, 'pointsOfInterest'); - for (i = 0; i < constraints.advanced[0].pointsOfInterest.length; ++i) { - assert_approx_equals(constraints.advanced[0].pointsOfInterest[i].x, - settings.pointsOfInterest[i].x, 0.01, - 'pointsOfInterest x'); - assert_approx_equals(constraints.advanced[0].pointsOfInterest[i].y, - settings.pointsOfInterest[i].y, 0.01, - 'pointsOfInterest y'); - } + assert_point2d_array_approx_equals( + constraints.advanced[0].pointsOfInterest, settings.pointsOfInterest, + 0.01); assert_equals(constraints.advanced[0].exposureCompensation, settings.exposureCompensation, 'exposureCompensation');
diff --git a/third_party/WebKit/LayoutTests/imagecapture/MediaStreamTrack-applyConstraints.html b/third_party/WebKit/LayoutTests/imagecapture/MediaStreamTrack-applyConstraints.html index d16ef04f..25013a4 100644 --- a/third_party/WebKit/LayoutTests/imagecapture/MediaStreamTrack-applyConstraints.html +++ b/third_party/WebKit/LayoutTests/imagecapture/MediaStreamTrack-applyConstraints.html
@@ -2,6 +2,7 @@ <script src="../resources/testharness.js"></script> <script src="../resources/testharnessreport.js"></script> <script src="../resources/mojo-helpers.js"></script> +<script src="resources/imagecapture-helpers.js"></script> <script src="resources/mock-imagecapture.js"></script> <body> <canvas id='canvas' width=10 height=10/> @@ -58,50 +59,55 @@ error => { assert_unreached("Error creating MockImageCapture: " + error); }) - .then(function() { - return videoTrack.applyConstraints(constraints); - }) - .then(function() { - assert_equals(constraints.advanced[0].whiteBalanceMode, + .then(() => videoTrack.applyConstraints(constraints)) + .then(appliedConstraints => { + + const constraintsDict = constraints.advanced[0]; + const appliedConstraintsDict = appliedConstraints.advanced[0]; + + // Check that |appliedConstraints| and |constraints| are equal. + assert_equals(constraintsDict.length, appliedConstraintsDict.length); + Object.keys(appliedConstraintsDict).forEach((key, value) => { + assert_not_equals(constraintsDict[key], undefined, 'key ' + key); + if (key != "pointsOfInterest") { + assert_equals(constraintsDict[key], appliedConstraintsDict[key], key); + } else { + assert_point2d_array_approx_equals(constraintsDict[key], + appliedConstraintsDict[key], 0.01); + } + }); + + assert_equals(constraintsDict.whiteBalanceMode, meteringModeNames[theMock.options().white_balance_mode], 'whiteBalanceMode'); - assert_equals(constraints.advanced[0].exposureMode, + assert_equals(constraintsDict.exposureMode, meteringModeNames[theMock.options().exposure_mode], 'exposureMode'); - assert_equals(constraints.advanced[0].focusMode, + assert_equals(constraintsDict.focusMode, meteringModeNames[theMock.options().focus_mode], 'focusMode'); - assert_equals(constraints.advanced[0].pointsOfInterest.length, - theMock.options().points_of_interest.length, - 'pointsOfInterest'); - for (i = 0; i < constraints.advanced[0].pointsOfInterest.length; ++i) { - assert_approx_equals(constraints.advanced[0].pointsOfInterest[i].x, - theMock.options().points_of_interest[i].x, 0.01, - 'pointsOfInterest x'); - assert_approx_equals(constraints.advanced[0].pointsOfInterest[i].y, - theMock.options().points_of_interest[i].y, 0.01, - 'pointsOfInterest y'); - } + assert_point2d_array_approx_equals(constraintsDict.pointsOfInterest, + theMock.options().points_of_interest, + 0.01); - assert_equals(constraints.advanced[0].exposureCompensation, + assert_equals(constraintsDict.exposureCompensation, theMock.options().exposure_compensation, 'exposureCompensation'); - assert_equals(constraints.advanced[0].colorTemperature, + assert_equals(constraintsDict.colorTemperature, theMock.options().color_temperature, 'colorTemperature'); - assert_equals(constraints.advanced[0].iso, theMock.options().iso, 'iso'); + assert_equals(constraintsDict.iso, theMock.options().iso, 'iso'); - assert_equals(constraints.advanced[0].brightness, - theMock.options().brightness, 'brightness'); - assert_equals(constraints.advanced[0].contrast, - theMock.options().contrast, 'constrast'); - assert_equals(constraints.advanced[0].saturation, - theMock.options().saturation, 'saturation'); - assert_equals(constraints.advanced[0].sharpness, - theMock.options().sharpness, 'sharpness'); + assert_equals(constraintsDict.brightness, theMock.options().brightness, + 'brightness'); + assert_equals(constraintsDict.contrast, theMock.options().contrast, + 'constrast'); + assert_equals(constraintsDict.saturation, theMock.options().saturation, + 'saturation'); + assert_equals(constraintsDict.sharpness, theMock.options().sharpness, + 'sharpness'); - assert_equals(constraints.advanced[0].torch, theMock.options().torch, - 'torch'); + assert_equals(constraintsDict.torch, theMock.options().torch, 'torch'); t.done(); })
diff --git a/third_party/WebKit/LayoutTests/imagecapture/MediaStreamTrack-getSettings.html b/third_party/WebKit/LayoutTests/imagecapture/MediaStreamTrack-getSettings.html index ef25073e..822d22be 100644 --- a/third_party/WebKit/LayoutTests/imagecapture/MediaStreamTrack-getSettings.html +++ b/third_party/WebKit/LayoutTests/imagecapture/MediaStreamTrack-getSettings.html
@@ -2,6 +2,7 @@ <script src="../resources/testharness.js"></script> <script src="../resources/testharnessreport.js"></script> <script src="../resources/mojo-helpers.js"></script> +<script src="resources/imagecapture-helpers.js"></script> <script src="resources/mock-imagecapture.js"></script> <body> <canvas id='canvas' width=10 height=10/> @@ -48,17 +49,8 @@ meteringModeNames[mock_settings.current_focus_mode], 'focusMode'); - assert_equals(settings.pointsOfInterest.length, - mock_settings.points_of_interest.length, - 'pointsOfInterest'); - for (i = 0; i < settings.pointsOfInterest.length; ++i) { - assert_approx_equals(settings.pointsOfInterest[i].x, - mock_settings.points_of_interest[i].x, 0.01, - 'pointsOfInterest x'); - assert_approx_equals(settings.pointsOfInterest[i].y, - mock_settings.points_of_interest[i].y, 0.01, - 'pointsOfInterest y'); - } + assert_point2d_array_approx_equals( + settings.pointsOfInterest, mock_settings.points_of_interest, 0.01); assert_equals(settings.exposureCompensation, mock_settings.exposure_compensation.current);
diff --git a/third_party/WebKit/LayoutTests/imagecapture/resources/imagecapture-helpers.js b/third_party/WebKit/LayoutTests/imagecapture/resources/imagecapture-helpers.js new file mode 100644 index 0000000..3eb14ca --- /dev/null +++ b/third_party/WebKit/LayoutTests/imagecapture/resources/imagecapture-helpers.js
@@ -0,0 +1,9 @@ +'use strict'; + +function assert_point2d_array_approx_equals(actual, expected, epsilon) { + assert_equals(actual.length, expected.length, 'length'); + for (var i = 0; i < actual.length; ++i) { + assert_approx_equals(actual[i].x, expected[i].x, epsilon, 'x'); + assert_approx_equals(actual[i].y, expected[i].y, epsilon, 'y'); + } +}
diff --git a/third_party/WebKit/LayoutTests/webaudio/BiquadFilter/tail-time-allpass.html b/third_party/WebKit/LayoutTests/webaudio/BiquadFilter/tail-time-allpass.html deleted file mode 100644 index b902f42..0000000 --- a/third_party/WebKit/LayoutTests/webaudio/BiquadFilter/tail-time-allpass.html +++ /dev/null
@@ -1,103 +0,0 @@ -<!doctype html> -<html> - <head> - <title>Test Biquad Tail-Time</title> - <script src="../../resources/testharness.js"></script> - <script src="../../resources/testharnessreport.js"></script> - <script src="../resources/audit-util.js"></script> - <script src="../resources/audit.js"></script> - <script src="../resources/biquad-filters.js"></script> - <script src="test-tail-time.js"></script> - </head> - - <body> - <script> - let audit = Audit.createTaskRunner(); - - let sampleRate = 16384; - let renderSeconds = 1; - let renderFrames = renderSeconds * sampleRate; - - // For an allpass filter: - // b0 = 1 - alpha - // b1 = -2*cos(w0) - // b2 = 1 + alpha - // a0 = 1 + alpha - // a1 = -2*cos(w0) - // a2 = 1 - alpha - // - // where alpha = sin(w0)/(2*Q) and w0 = 2*%pi*f0/Fs. - // - // Equivalently a1 = -2*cos(w0)/(1+alpha), a2 = (1-alpha)/(1+alpha). The - // poles of this filter are at - // - // (2*Q*cos(w0) +/- sqrt(1-4*Q^2)*sin(w0))/(2*Q + sin(w0)) - // - // Thus, if 1-4*Q^2 < 0, the poles are complex. For 1-4*Q^2 > 0, the - // poles are real and distinct. For 1-4*Q^2 = 0, there are two identical - // real poles. - - // Array of tests to run. |descripton| is the task description for - // audit.define. |parameters| is option for |testTailTime|. - let tests = [ - { - description: - {label: 'allpass-complex-roots', description: 'complex roots'}, - parameters: { - prefix: 'Complex roots', - // Choose a fairly large Q to make the tail long. Frequency is - // fairly arbitrary. - filterOptions: {type: 'allpass', Q: 200, frequency: sampleRate / 4}, - // This filter the actual real tail frame is 2317, and the node - // computed frame is 2316.81. Thus, the tail output should be - // exactly 0. - threshold: 0 - } - }, - { - description: { - label: 'allpass-real-distinct-roots', - description: 'real distinct roots' - }, - parameters: { - prefix: 'Distinct roots', - filterOptions: { - type: 'allpass', - Q: 0.001, - frequency: sampleRate / 8, - }, - // With this particular filter, the real tail frame is 4822, but - // the node way overestimates it to be 7136. Thus, the actual - // tail frames won't be exactly zero. - threshold: 1 / 32768 - } - }, - { - description: { - label: 'allpass-repeated-roots', - description: 'repeated real root' - }, - parameters: { - prefix: 'Repeated roots', - // 1-4*Q^2 = 0 iff Q = 1/2. - filterOptions: {type: 'allpass', Q: 0.5, frequency: sampleRate / 8}, - // The node estimated tail time is 16.8 frames, but the actual is - // 106. Thus, the outupt should be exactly 0. - threshold: 0 - } - } - ]; - - // Define an appropriate task for each test. - tests.forEach(entry => { - audit.define(entry.description, (task, should) => { - let context = new OfflineAudioContext(1, renderFrames, sampleRate); - testTailTime(should, context, entry.parameters) - .then(() => task.done()); - }); - }); - - audit.run(); - </script> - </body> -</html>
diff --git a/third_party/WebKit/LayoutTests/webaudio/BiquadFilter/tail-time-bandpass.html b/third_party/WebKit/LayoutTests/webaudio/BiquadFilter/tail-time-bandpass.html deleted file mode 100644 index 6eb0ad1..0000000 --- a/third_party/WebKit/LayoutTests/webaudio/BiquadFilter/tail-time-bandpass.html +++ /dev/null
@@ -1,95 +0,0 @@ -<!doctype html> -<html> - <head> - <title>Test Biquad Tail-Time</title> - <script src="../../resources/testharness.js"></script> - <script src="../../resources/testharnessreport.js"></script> - <script src="../resources/audit-util.js"></script> - <script src="../resources/audit.js"></script> - <script src="../resources/biquad-filters.js"></script> - <script src="test-tail-time.js"></script> - </head> - - <body> - <script> - let audit = Audit.createTaskRunner(); - - let sampleRate = 16384; - let renderSeconds = 1; - let renderFrames = renderSeconds * sampleRate; - - // For a bandpass filter: - // b0 = alpha - // b1 = 0 - // b2 = -alpha - // a0 = 1 + alpha - // a1 = -2*cos(w0) - // a2 = 1 - alpha - // - // where alpha = sin(w0)/(2*Q) and w0 = 2*%pi*f0/Fs. - // - // Equivalently a1 = -2*cos(w0)/(1+alpha), a2 = (1-alpha)/(1+alpha). The - // poles of this filter are at - // - // (2*Q*cos(w0) +/- sqrt(1-4*Q^2)*sin(w0))/(2*Q + sin(w0)) - // - // Thus, if 1-4*Q^2 < 0, the poles are complex. For 1-4*Q^2 > 0, the - // poles are real and distinct. For 1-4*Q^2 = 0, there are two identical - // real poles. - - // Array of tests to run. |descripton| is the task description for - // audit.define. |parameters| is option for |testTailTime|. - let tests = [ - { - descripton: - {label: 'bpf-complex-roots', description: 'complex roots'}, - parameters: { - prefix: 'BPF complex roots', - filterOptions: - {type: 'bandpass', Q: 200, frequency: sampleRate / 4}, - // The node estimated tail frame is 2039.55, which matches the - // true tail frame so output should be exactly 0 - threshold: 0 - } - }, - { - descripton: { - label: 'bpf-real-distinct-roots', - description: 'real distinct roots' - }, - parameters: { - prefix: 'BPF real distinct roots', - filterOptions: - {type: 'bandpass', Q: 0.001, frequency: sampleRate / 4}, - // The node estimated tail frame is 2437, which matches the - // true tail frame so output should be exactly 0 - threshold: 0 - } - }, - { - descripton: - {label: 'bpf-repeated-roots', description: 'repeated real root'}, - parameters: { - prefix: 'BPF repeated roots', - filterOptions: - {type: 'bandpass', Q: 0.5, frequency: sampleRate / 8}, - // The node estimated tail frame is 15.9, which matches the true - // tail frame so output should be exactly 0 - threshold: 0 - } - } - ]; - - // Define an appropriate task for each test. - tests.forEach(entry => { - audit.define(entry.descripton, (task, should) => { - let context = new OfflineAudioContext(1, renderFrames, sampleRate); - testTailTime(should, context, entry.parameters) - .then(() => task.done()); - }); - }); - - audit.run(); - </script> - </body> -</html>
diff --git a/third_party/WebKit/LayoutTests/webaudio/BiquadFilter/tail-time-highpass.html b/third_party/WebKit/LayoutTests/webaudio/BiquadFilter/tail-time-highpass.html deleted file mode 100644 index 90bd15a3..0000000 --- a/third_party/WebKit/LayoutTests/webaudio/BiquadFilter/tail-time-highpass.html +++ /dev/null
@@ -1,116 +0,0 @@ -<!doctype html> -<html> - <head> - <title>Test Biquad Tail-Time</title> - <script src="../../resources/testharness.js"></script> - <script src="../../resources/testharnessreport.js"></script> - <script src="../resources/audit-util.js"></script> - <script src="../resources/audit.js"></script> - <script src="../resources/biquad-filters.js"></script> - <script src="test-tail-time.js"></script> - </head> - - <body> - <script> - let audit = Audit.createTaskRunner(); - - let sampleRate = 16384; - let renderSeconds = 1; - let renderFrames = renderSeconds * sampleRate; - - // For a highpass filter: - // b0 = (1+cos(w0))/2 - // b1 = -(1+cos(w0)) - // b2 = (1+cos(w0))/2 - // a0 = 1 + alpha - // a1 = -2*cos(w0) - // a2 = 1 - alpha - // - // where alpha = sin(w0)/(2*10^(Q/20)) and w0 = 2*%pi*f0/Fs. - // - // Equivalently a1 = -2*cos(w0)/(1+alpha), a2 = (1-alpha)/(1+alpha). The - // poles of this filter are at - // - // cos(w0)/(1+alpha) +/- sqrt(alpha^2-sin(w0)^2)/(1+alpha) - // - // But alpha^2-sin(w0)^2 = sin(w0)^2*(1/4/10^(Q/10) - 1). Thus the poles - // are complex if 1/4/10^(Q/10) < 1; real distinct if 1/4/10^(Q/10) > 1; - // and repeated if 1/4/10^(Q/10) = 1. - - // Array of tests to run. |descripton| is the task description for - // audit.define. |parameters| is option for |testTailTime|. - let tests = [ - { - descripton: - {label: 'hpf-complex-roots', description: 'complex roots'}, - parameters: { - prefix: 'HPF complex roots', - filterOptions: {type: 'highpass', Q: 40, frequency: sampleRate / 4}, - // Node computed tail frame is 2079.4, which matches the actual tail - // frome so output should be exactly 0. - threshold: 0 - } - }, - { - descripton: { - label: 'hpf-real-distinct-roots', - description: 'real distinct roots' - }, - parameters: { - prefix: 'HPF real distinct roots', - filterOptions: - {type: 'highpass', Q: -50, frequency: sampleRate / 8}, - // With these filter parameters, the real tail time is 408, but - // the node overestimates it to be 2367. Thus, the actual tail - // frames won't be exactly zero. - threshold: 1 / 32768 - } - }, - { - descripton: - {label: 'hpf-repeated-root', description: 'repeated real root'}, - parameters: { - prefix: 'HPF repeated roots (approximately)', - // For a repeated root, we need 1/4/10^(Q/10) = 1, or Q = - // -10*log(4)/log(10). This isn't exactly representable as a float, - // so the roots might not actually be repeated. In fact the roots - // are complex at 6.40239e-5*exp(i*1.570596). - filterOptions: { - type: 'highpass', - Q: -10 * Math.log10(4), - frequency: sampleRate / 4 - }, - // Node computed tail frame is 2.9, which matches the actual tail - // frome so output should be exactly 0. - threshold: 0 - } - }, - { - descripton: {label: 'hpf-real-roots-2', description: 'complex roots'}, - parameters: { - prefix: 'HPF repeated roots 2', - // This tests an extreme case where approximate impulse response is - // h(n) = C*r^(n-1) and C < 1/32768. Thus, the impulse response is - // always less than the response threshold of 1/32768. - filterOptions: - {type: 'highpass', Q: -100, frequency: sampleRate / 4}, - // Node computed tail frame is 0, which matches the actual tail - // frame so output should be exactly 0. - threshold: 0 - } - } - ]; - - // Define an appropriate task for each test. - tests.forEach(entry => { - audit.define(entry.descripton, (task, should) => { - let context = new OfflineAudioContext(1, renderFrames, sampleRate); - testTailTime(should, context, entry.parameters) - .then(() => task.done()); - }); - }); - - audit.run(); - </script> - </body> -</html>
diff --git a/third_party/WebKit/LayoutTests/webaudio/BiquadFilter/tail-time-highshelf.html b/third_party/WebKit/LayoutTests/webaudio/BiquadFilter/tail-time-highshelf.html deleted file mode 100644 index 0b3a2ae..0000000 --- a/third_party/WebKit/LayoutTests/webaudio/BiquadFilter/tail-time-highshelf.html +++ /dev/null
@@ -1,81 +0,0 @@ -<!doctype html> -<html> - <head> - <title>Test Biquad Tail-Time</title> - <script src="../../resources/testharness.js"></script> - <script src="../../resources/testharnessreport.js"></script> - <script src="../resources/audit-util.js"></script> - <script src="../resources/audit.js"></script> - <script src="../resources/biquad-filters.js"></script> - <script src="test-tail-time.js"></script> - </head> - - <body> - <script> - let audit = Audit.createTaskRunner(); - - let sampleRate = 16384; - let renderSeconds = 1; - let renderFrames = renderSeconds * sampleRate; - - // For a highshelf filter: - // b0 = A*[(A+1)+(A−1)*cos(w0)+2*as*sqrt(A)] - // b1 = 2*A*[(A-1)+(A+1)*cos(w0)] - // b2 = A*[(A+1)+(A−1)*cos(w0)-2*as*sqrt(A)] - // a0 = (A+1)-(A-1)*cos(w0)+2*as*sqrt(A) - // a1 = -2*[(A-1)-(A+1)*cos(w0)] - // a2 = (A+1)-(A-1)*cos(w0)-2*as*sqrt(A) - // - // where as = sin(w0)/sqrt(2), w0 = 2*%pi*f0/Fs, and A = 10^(G/40) - // - // The poles of this filter are - // - // -a2/(2*a0) +/- sqrt(a1^2-4*a0*a2)/(2*a0). - // - // Thus, the poles depend on the sign of d = a1^2-4*a0*a2 = - // 16*A*(as^2-sin(w0)^2) = -8*A*sin(w0)^2. Thus, the poles are always - // complex except if w0 = 0, in which case there is a repeated pole at 0. - - // Array of tests to run. |descripton| is the task description for - // audit.define. |parameters| is option for |testTailTime|. - let tests = [ - { - descripton: - {label: 'highshelf-complex-roots', description: 'complex roots'}, - parameters: { - prefix: 'Highshelf complex roots', - filterOptions: - {type: 'highshelf', gain: 40, frequency: sampleRate / 8}, - // Node computed tail frame is 18.6 frames, which matches the actual - // tail, so tail output should be exactly zero. - threshold: 0 - } - }, - { - descripton: { - label: 'highshelf-repeated-roots', - description: 'repeated real root' - }, - parameters: { - prefix: 'Highshelf repeated roots', - filterOptions: {type: 'highshelf', gain: 40, frequency: 0}, - // Node computed tail frame is 2 frames, which matches the actual - // tail, so tail output should be exactly zero. - threshold: 0 - } - }, - ]; - - // Define an appropriate task for each test. - tests.forEach(entry => { - audit.define(entry.descripton, (task, should) => { - let context = new OfflineAudioContext(1, renderFrames, sampleRate); - testTailTime(should, context, entry.parameters) - .then(() => task.done()); - }); - }); - - audit.run(); - </script> - </body> -</html>
diff --git a/third_party/WebKit/LayoutTests/webaudio/BiquadFilter/tail-time-lowpass.html b/third_party/WebKit/LayoutTests/webaudio/BiquadFilter/tail-time-lowpass.html deleted file mode 100644 index dc042637..0000000 --- a/third_party/WebKit/LayoutTests/webaudio/BiquadFilter/tail-time-lowpass.html +++ /dev/null
@@ -1,168 +0,0 @@ -<!doctype html> -<html> - <head> - <title>Test Biquad Tail-Time</title> - <script src="../../resources/testharness.js"></script> - <script src="../../resources/testharnessreport.js"></script> - <script src="../resources/audit-util.js"></script> - <script src="../resources/audit.js"></script> - <script src="../resources/biquad-filters.js"></script> - <script src="test-tail-time.js"></script> - </head> - - <body> - <script> - let audit = Audit.createTaskRunner(); - - let sampleRate = 16384; - let renderSeconds = 1; - - // For a lowpass filter: - // b0 = (1-cos(w0))/2 - // b1 = 1-cos(w0) - // b2 = (1-cos(w0))/2 - // a0 = 1 + alpha - // a1 = -2*cos(w0) - // a2 = 1 - alpha - // - // where alpha = sin(w0)/(2*10^(Q/20)) and w0 = 2*%pi*f0/Fs. - // - // Equivalently a1 = -2*cos(w0)/(1+alpha), a2 = (1-alpha)/(1+alpha). The - // poles of this filter are at - // - // cos(w0)/(1+alpha) +/- sqrt(alpha^2-sin(w0)^2)/(1+alpha) - // - // But alpha^2-sin(w0)^2 = sin(w0)^2*(1/4/10^(Q/10) - 1). Thus the poles - // are complex if 1/4/10^(Q/10) < 1; real distinct if 1/4/10^(Q/10) > 1; - // and repeated if 1/4/10^(Q/10) = 1. - - // Array of tests to run. |descripton| is the task description for - // audit.define. |parameters| is option for |testTailTime|. - let tests = [ - { - descripton: - {label: 'lpf-complex-roots', description: 'complex roots'}, - sampleRate: sampleRate, - renderDuration: renderSeconds, - parameters: { - prefix: 'LPF complex roots', - filterOptions: {type: 'lowpass', Q: 40, frequency: sampleRate / 4} - }, - // Node computed tail frame is 2079.4 which matches the real tail, so - // tail output should be exactly 0. - threshold: 0, - }, - { - descripton: { - label: 'lpf-real-distinct-roots', - description: 'real distinct roots' - }, - sampleRate: sampleRate, - renderDuration: renderSeconds, - parameters: { - prefix: 'LPF real distinct roots', - filterOptions: - {type: 'lowpass', Q: -50, frequency: sampleRate / 8} - }, - // Node computed tail frame is 1699 which matches the real tail, so - // tail output should be exactly 0. - threshold: 0, - }, - { - descripton: - {label: 'lpf-repeated-root', description: 'repeated real root'}, - sampleRate: sampleRate, - renderDuration: renderSeconds, - parameters: { - prefix: 'LPF repeated roots (approximately)', - // For a repeated root, we need 1/4/10^(Q/10) = 1, or Q = - // -10*log(4)/log(10). This isn't exactly representable as a float, - // we the roots might not actually be repeated. In fact the roots - // are actually complex at 6.402396e-5*exp(i*1.570796). - filterOptions: { - type: 'lowpass', - Q: -10 * Math.log10(4), - frequency: sampleRate / 4 - } - }, - // Node computed tail frame is 2.9 which matches the real tail, so - // tail output should be exactly 0. - threshold: 0, - }, - { - descripton: {label: 'lpf-real-roots-2', description: 'complex roots'}, - sampleRate: sampleRate, - renderDuration: renderSeconds, - parameters: { - prefix: 'LPF repeated roots 2', - // This tests an extreme case where approximate impulse response is - // h(n) = C*r^(n-1) and C < 1/32768. Thus, the impulse response is - // always less than the response threshold of 1/32768. - filterOptions: - {type: 'lowpass', Q: -100, frequency: sampleRate / 4} - }, - // Node computed tail frame is 0 which matches the real tail, so - // tail output should be exactly 0. - threshold: 0, - }, - { - descripton: 'huge tail', - // The BiquadFilter has an internal maximum tail of 30 sec so we want - // to render for at least 30 sec to test this. Use the smallest - // sample rate we can to limit memory and CPU usage! - sampleRate: 3000, - renderDuration: 31, - parameters: { - prefix: 'LPF repeated roots (approximately)', - hugeTaileTime: true, - // For the record, for this lowpass filter, the computed tail time - // is approximately 2830.23 sec, with poles at - // 0.999998960442086*exp(i*0.209439510236777). This is very close to - // being marginally stable. - filterOptions: { - type: 'lowpass', - Q: 100, - frequency: 100, - }, - // Node computed tail frame is 8.49069e6 which is clamped to 30 sec - // so tail output should be exactly 0 after 30 sec. - threshold: 0, - }, - }, - { - descripton: 'ginormous tail', - // Or this lowpass filter, the complex poles are actually computed to - // be on the unit circle so the tail infinite. This just tests that - // nothing bad happens in computing the tail time. Thus, any small - // sample rate and short duration for the test; the results aren't - // really interesting. (But they must pass, of course!) - sampleRate: 3000, - renderDuration: 0.25, - parameters: { - prefix: 'LPF repeated roots (approximately)', - filterOptions: { - type: 'lowpass', - Q: 500, - frequency: 100, - }, - }, - // Node computed tail frame is 90000 which matches the real tail, so - // tail output should be exactly 0. - threshold: 0, - } - ] - - // Define an appropriate task for each test. - tests.forEach(entry => { - audit.define(entry.descripton, (task, should) => { - let context = new OfflineAudioContext( - 1, entry.renderDuration * entry.sampleRate, entry.sampleRate); - testTailTime(should, context, entry.parameters) - .then(() => task.done()); - }); - }); - - audit.run(); - </script> - </body> -</html>
diff --git a/third_party/WebKit/LayoutTests/webaudio/BiquadFilter/tail-time-lowshelf.html b/third_party/WebKit/LayoutTests/webaudio/BiquadFilter/tail-time-lowshelf.html deleted file mode 100644 index b8af1f3..0000000 --- a/third_party/WebKit/LayoutTests/webaudio/BiquadFilter/tail-time-lowshelf.html +++ /dev/null
@@ -1,83 +0,0 @@ -<!doctype html> -<html> - <head> - <title>Test Biquad Tail-Time</title> - <script src="../../resources/testharness.js"></script> - <script src="../../resources/testharnessreport.js"></script> - <script src="../resources/audit-util.js"></script> - <script src="../resources/audit.js"></script> - <script src="../resources/biquad-filters.js"></script> - <script src="test-tail-time.js"></script> - </head> - - <body> - <script> - let audit = Audit.createTaskRunner(); - - let sampleRate = 16384; - let renderSeconds = 1; - let renderFrames = renderSeconds * sampleRate; - let renderQuantumFrames = 128; - - // For a lowshelf filter: - // b0 = A*[(A+1)−(A−1)*cos(w0)+2*as*sqrt(A)] - // b1 = 2*A*[(A+1)-(A-1)*cos(w0)] - // b2 = A*[(A+1)−(A−1)*cos(w0)-2*as*sqrt(A)] - // a0 = (A+1)+(A-1)*cos(w0)+2*as*sqrt(A) - // a1 = -2*[(A-1)+(A+1)*cos(w0)] - // a2 = (A+1)+(A-1)*cos(w0)-2*as*sqrt(A) - // - // where as = sin(w0)/sqrt(2), w0 = 2*%pi*f0/Fs, and A = 10^(G/40) - // - // The poles of this filter are - // - // -a2/(2*a0) +/- sqrt(a1^2-4*a0*a2)/(2*a0). - // - // Thus, the poles depend on the sign of d = a1^2-4*a0*a2 = - // 16*A*(as^2-sin(w0)^2) = -8*A*sin(w0)^2. Thus, the poles are always - // complex except if w0 = 0, in which case there is a repeated pole at 0. - - // Array of tests to run. |descripton| is the task description for - // audit.define. |parameters| is option for |testTailTime|. - let tests = [ - { - descripton: - {label: 'lowshelf-complex-roots', description: 'complex roots'}, - parameters: { - prefix: 'Lowshelf complex roots', - filterOptions: - {type: 'lowshelf', gain: 40, frequency: sampleRate / 8}, - // Node computed tail frame is 75.5 frames, which matches the actual - // tail, so tail output should be exactly zero. - threshold: 0 - }, - }, - { - descripton: { - label: 'lowshelf-repeated-roots', - description: 'repeated real root' - }, - parameters: { - prefix: 'Lowshelf repeated roots', - filterOptions: - {type: 'lowshelf', Q: 1 / 20, gain: 40, frequency: 0}, - // Node computed tail frame is 2 frames, which matches the actual - // tail, so tail output should be exactly zero. - threshold: 0 - }, - }, - ]; - - // Define an appropriate task for each test. - tests.forEach(entry => { - audit.define(entry.descripton, (task, should) => { - let context = new OfflineAudioContext(1, renderFrames, sampleRate); - testTailTime(should, context, entry.parameters) - .then(() => task.done()); - }); - }); - - audit.run(); - </script> - </body> -</html>
diff --git a/third_party/WebKit/LayoutTests/webaudio/BiquadFilter/tail-time-notch.html b/third_party/WebKit/LayoutTests/webaudio/BiquadFilter/tail-time-notch.html deleted file mode 100644 index 0b38d4bc..0000000 --- a/third_party/WebKit/LayoutTests/webaudio/BiquadFilter/tail-time-notch.html +++ /dev/null
@@ -1,98 +0,0 @@ -<!doctype html> -<html> - <head> - <title>Test Biquad Tail-Time</title> - <script src="../../resources/testharness.js"></script> - <script src="../../resources/testharnessreport.js"></script> - <script src="../resources/audit-util.js"></script> - <script src="../resources/audit.js"></script> - <script src="../resources/biquad-filters.js"></script> - <script src="test-tail-time.js"></script> - </head> - - <body> - <script> - let audit = Audit.createTaskRunner(); - - let sampleRate = 16384; - let renderSeconds = 1; - let renderFrames = renderSeconds * sampleRate; - let renderQuantumFrames = 128; - - // For a notch filter: - // b0 = 1 - // b1 = -2*cos(w0) - // b2 = 1 - // a0 = 1 + alpha - // a1 = -2*cos(w0) - // a2 = 1 - alpha - // - // where alpha = sin(w0)/(2*Q) and w0 = 2*%pi*f0/Fs. - // - // Equivalently a1 = -2*cos(w0)/(1+alpha), a2 = (1-alpha)/(1+alpha). The - // poles of this filter are at - // - // (2*Q*cos(w0) +/- sqrt(1-4*Q^2)*sin(w0))/(2*Q + sin(w0)) - // - // Thus, if 1-4*Q^2 < 0, the poles are complex. For 1-4*Q^2 > 0, the - // poles are real and distinct. For 1-4*Q^2 = 0, there are two identical - // real poles. - - // Array of tests to run. |descripton| is the task description for - // audit.define. |parameters| is option for |testTailTime|. - let tests = [ - { - descripton: - {label: 'notch-complex-roots', description: 'complex roots'}, - parameters: { - prefix: 'Notch complex roots', - filterOptions: {type: 'notch', Q: 200, frequency: sampleRate / 4}, - // Node computed tail frame is 2039.5 frames, which matches the - // actual tail, so tail output should be exactly zero. - threshold: 0 - }, - }, - { - descripton: { - label: 'notch-real-distinct-roots', - description: 'real distinct roots' - }, - parameters: { - prefix: 'Notch real distinct roots', - filterOptions: {type: 'notch', Q: 0.001, frequency: sampleRate / 4}, - // Node computed tail frame is 2437 frames, which matches the actual - // tail, so tail output should be exactly zero. - threshold: 0 - }, - }, - { - descripton: { - label: 'notch-repeated-roots', - description: 'repeated real root' - }, - parameters: { - prefix: 'Notch repeated roots', - // Note that while the roots are mathematically repeated, - // numerical roundoff in compute the filter coefficients causes - // the resulting filter to have roots at 0 and 6.123e-17. - filterOptions: {type: 'notch', Q: 0.5, frequency: sampleRate / 4}, - // Node computed tail frame is 2 frames, which matches the actual - // tail, so tail output should be exactly zero. - threshold: 0 - } - }, - ]; - - // Define an appropriate task for each test. - tests.forEach(entry => { - audit.define(entry.descripton, (task, should) => { - let context = new OfflineAudioContext(1, renderFrames, sampleRate); - testTailTime(should, context, entry.parameters) - .then(() => task.done()); - }); - }); - - audit.run(); - </script> - </body> -</html>
diff --git a/third_party/WebKit/LayoutTests/webaudio/BiquadFilter/tail-time-peaking.html b/third_party/WebKit/LayoutTests/webaudio/BiquadFilter/tail-time-peaking.html deleted file mode 100644 index 973d8361..0000000 --- a/third_party/WebKit/LayoutTests/webaudio/BiquadFilter/tail-time-peaking.html +++ /dev/null
@@ -1,115 +0,0 @@ -<!doctype html> -<html> - <head> - <title>Test Biquad Tail-Time</title> - <script src="../../resources/testharness.js"></script> - <script src="../../resources/testharnessreport.js"></script> - <script src="../resources/audit-util.js"></script> - <script src="../resources/audit.js"></script> - <script src="../resources/biquad-filters.js"></script> - <script src="test-tail-time.js"></script> - </head> - - <body> - <script> - let audit = Audit.createTaskRunner(); - - let sampleRate = 16384; - let renderSeconds = 1; - let renderFrames = renderSeconds * sampleRate; - - // For a peaking filter: - // b0 = 1 - alpha*A - // b1 = -2*cos(w0) - // b2 = 1 + alpha*A - // a0 = 1 + alpha/A - // a1 = -2*cos(w0) - // a2 = 1 - alpha/A - // - // where alpha = sin(w0)/(2*Q), w0 = 2*%pi*f0/Fs, and A = 10^(G/40) - // - // Equivalently a1 = -2*cos(w0)/(1+alpha/A), a2 = (1-alpha/A)/(1+alpha/A). - // The poles of this filter are at - // - // A*cos(w0)/(A + alpha) +/- sqrt(alpha^2-A^2*sin(w0)^2)/(A + alpha) - // - // But alpha^2-A^2*sin(w0)^2 = sin(w0)^2*(1/4/Q^2-1). - // Thus, the poles are complex if 1/(4*Q^2) < A^2; real and distinct if - // 1/(4*Q^2)>A^2; and repeated if 1/(4*Q^2) = A^2 or w0 = 0. - - // Array of tests to run. |descripton| is the task description for - // audit.define. |parameters| is option for |testTailTime|. - let tests = [ - { - descripton: - {label: 'peaking-complex-roots', description: 'complex roots'}, - parameters: { - prefix: 'Peaking complex roots', - // A gain of 40 gives A = 10. - filterOptions: - {type: 'peaking', Q: 10, gain: 40, frequency: sampleRate / 4}, - // Node computed tail frame is 2077.4 frames, which matches the - // actual tail, so tail output should be exactly zero. - threshold: 0 - } - }, - { - descripton: { - label: 'peaking-real-distinct-roots', - description: 'real distinct roots' - }, - parameters: { - prefix: 'Peaking real distinct roots', - filterOptions: { - type: 'peaking', - Q: 0.001, - gain: 40, - frequency: sampleRate / 4 - }, - // Node computed tail frame is 588 frames, which matches the actual - // tail, so tail output should be exactly zero. - threshold: 0 - } - }, - { - descripton: { - label: 'peaking-repeated-roots', - description: 'repeated real root' - }, - parameters: { - prefix: 'Peaking repeated roots', - filterOptions: - {type: 'peaking', Q: 1 / 2, gain: 0, frequency: sampleRate / 8}, - // Node computed tail frame is 0 frames, which matches the actual - // tail, so tail output should be exactly zero. - threshold: 0 - - } - }, - { - descripton: { - label: 'peaking-repeated-roots 2', - description: 'repeated real root' - }, - parameters: { - prefix: 'Peaking repeated roots 2', - filterOptions: {type: 'peaking', Q: 1, gain: 40, frequency: 0}, - // Node computed tail frame is 1 frames, which matches the actual - // tail, so tail output should be exactly zero. - threshold: 0 - } - } - ]; - - // Define an appropriate task for each test. - tests.forEach(entry => { - audit.define(entry.descripton, (task, should) => { - let context = new OfflineAudioContext(1, renderFrames, sampleRate); - testTailTime(should, context, entry.parameters).then(() => task.done()); - }); - }); - - audit.run(); - </script> - </body> -</html>
diff --git a/third_party/WebKit/LayoutTests/webaudio/BiquadFilter/test-tail-time.js b/third_party/WebKit/LayoutTests/webaudio/BiquadFilter/test-tail-time.js deleted file mode 100644 index 94f1449..0000000 --- a/third_party/WebKit/LayoutTests/webaudio/BiquadFilter/test-tail-time.js +++ /dev/null
@@ -1,90 +0,0 @@ -function testTailTime(should, context, options) { - let src = new ConstantSourceNode(context, {offset: 1}); - let f = new BiquadFilterNode(context, options.filterOptions); - - src.connect(f).connect(context.destination); - src.start(); - src.stop(1 / context.sampleRate); - - let expectedTailFrame = computeTailFrame(f); - - // The internal Biquad time computation limits he tail time to a - // maximum of 30 sec. We need to limit the computed tail frame to - // that limit as well. - expectedTailFrame = Math.min(expectedTailFrame, 30 * context.sampleRate); - - return context.startRendering().then(renderedBuffer => { - let s = renderedBuffer.getChannelData(0); - let prefix = options.prefix + ': Biquad(' + - JSON.stringify(options.filterOptions) + ')'; - - // Round actual tail frame to a render boundary - let quantumIndex = Math.floor(expectedTailFrame / RENDER_QUANTUM_FRAMES); - let expectedTailBoundary = RENDER_QUANTUM_FRAMES * quantumIndex; - - // Find the actual tail frame. That is, the last point where the - // output is not zero. - let actualTailFrame; - - for (actualTailFrame = s.length; actualTailFrame > 0; --actualTailFrame) { - if (Math.abs(s[actualTailFrame - 1]) > 0) - break; - } - - should(actualTailFrame, `${prefix}: Actual Tail Frame ${actualTailFrame}`) - .beGreaterThanOrEqualTo(expectedTailFrame); - - // Verify each render quanta is not identically zero up to the - // boundary. - for (let k = 0; k <= quantumIndex; ++k) { - let firstFrame = RENDER_QUANTUM_FRAMES * k; - let lastFrame = firstFrame + RENDER_QUANTUM_FRAMES - 1; - should( - s.slice(firstFrame, lastFrame + 1), - `${prefix}: output[${firstFrame}:${lastFrame}]`) - .notBeConstantValueOf(0); - } - // The frames after the tail should be zero. Because the - // implementation uses approximations to simplify the - // computations, the nodes tail time may be greater than the real - // impulse response tail. Thus, we just verify that the output - // over the tail is less than the tail threshold value. - let zero = new Float32Array(s.length); - should( - s.slice(expectedTailBoundary + RENDER_QUANTUM_FRAMES + 256), - prefix + ': output[' + - (expectedTailBoundary + RENDER_QUANTUM_FRAMES + 256) + ':]') - .beCloseToArray( - zero.slice(expectedTailBoundary + RENDER_QUANTUM_FRAMES + 256), - {absoluteThreshold: options.threshold || 0}); - }) -} - -function computeTailFrame(filterNode) { - // Compute the impuluse response for the filter |filterNode| by - // filtering the impulse directly ourself. - let coef = createFilter( - filterNode.type, - filterNode.frequency.value / filterNode.context.sampleRate * 2, - filterNode.Q.value, filterNode.gain.value); - - let impulse = new Float32Array(filterNode.context.length); - impulse[0] = 1; - - let filtered = filterData(coef, impulse, impulse.length); - - // Compute the magnitude and find out where the imuplse is small enough. - let tailFrame = 0; - if (Math.abs(filtered[filtered.length - 1]) >= 1 / 32768) { - tailFrame = filtered.length - 1; - } else { - for (let k = filtered.length - 1; k >= 0; --k) { - if (Math.abs(filtered[k]) >= 1 / 32768) { - tailFrame = k + 1; - break; - } - } - } - - return tailFrame; -}
diff --git a/third_party/WebKit/LayoutTests/webaudio/resources/audit-util.js b/third_party/WebKit/LayoutTests/webaudio/resources/audit-util.js index 1e39c87..671bcf1 100644 --- a/third_party/WebKit/LayoutTests/webaudio/resources/audit-util.js +++ b/third_party/WebKit/LayoutTests/webaudio/resources/audit-util.js
@@ -8,8 +8,6 @@ * test. */ -// How many frames in a WebAudio render quantum. -let RENDER_QUANTUM_FRAMES = 128; function writeString(s, a, offset) { for (let i = 0; i < s.length; ++i) {
diff --git a/third_party/WebKit/Source/build/scripts/templates/fields/group.tmpl b/third_party/WebKit/Source/build/scripts/templates/fields/group.tmpl index 56d3f9d..956b974 100644 --- a/third_party/WebKit/Source/build/scripts/templates/fields/group.tmpl +++ b/third_party/WebKit/Source/build/scripts/templates/fields/group.tmpl
@@ -1,7 +1,7 @@ {% from 'fields/field.tmpl' import encode, declare_storage, compare %} {% from 'macros.tmpl' import print_if %} {% macro define_field_group_class(group): -%} -class {{group.type_name}} : public RefCountedCopyable<{{group.type_name}}> { +class {{group.type_name}} : public RefCounted<{{group.type_name}}> { public: static PassRefPtr<{{group.type_name}}> Create() { return AdoptRef(new {{group.type_name}}); @@ -30,6 +30,10 @@ {% endfor %} {} - {{group.type_name}}(const {{group.type_name}}&) = default; + {{group.type_name}}(const {{group.type_name}}& other) : + {% for field in group.fields %} + {{field.name}}(other.{{field.name}}){{print_if(not loop.last, ',')}} + {% endfor %} + {} }; {%- endmacro %}
diff --git a/third_party/WebKit/Source/core/BUILD.gn b/third_party/WebKit/Source/core/BUILD.gn index 2b586df..b4610d2f 100644 --- a/third_party/WebKit/Source/core/BUILD.gn +++ b/third_party/WebKit/Source/core/BUILD.gn
@@ -1338,6 +1338,7 @@ "layout/ng/inline/ng_inline_items_builder_test.cc", "layout/ng/inline/ng_inline_layout_algorithm_test.cc", "layout/ng/inline/ng_inline_node_test.cc", + "layout/ng/inline/ng_line_breaker_test.cc", "layout/ng/ng_absolute_utils_test.cc", "layout/ng/ng_base_layout_algorithm_test.cc", "layout/ng/ng_base_layout_algorithm_test.h",
diff --git a/third_party/WebKit/Source/core/css/CSSPrimitiveValueMappings.h b/third_party/WebKit/Source/core/css/CSSPrimitiveValueMappings.h index 2b0f5a4..f2ca431 100644 --- a/third_party/WebKit/Source/core/css/CSSPrimitiveValueMappings.h +++ b/third_party/WebKit/Source/core/css/CSSPrimitiveValueMappings.h
@@ -874,49 +874,6 @@ } template <> -inline CSSIdentifierValue::CSSIdentifierValue(LineBreak e) - : CSSValue(kIdentifierClass) { - switch (e) { - case LineBreak::kAuto: - value_id_ = CSSValueAuto; - break; - case LineBreak::kLoose: - value_id_ = CSSValueLoose; - break; - case LineBreak::kNormal: - value_id_ = CSSValueNormal; - break; - case LineBreak::kStrict: - value_id_ = CSSValueStrict; - break; - case LineBreak::kAfterWhiteSpace: - value_id_ = CSSValueAfterWhiteSpace; - break; - } -} - -template <> -inline LineBreak CSSIdentifierValue::ConvertTo() const { - switch (value_id_) { - case CSSValueAuto: - return LineBreak::kAuto; - case CSSValueLoose: - return LineBreak::kLoose; - case CSSValueNormal: - return LineBreak::kNormal; - case CSSValueStrict: - return LineBreak::kStrict; - case CSSValueAfterWhiteSpace: - return LineBreak::kAfterWhiteSpace; - default: - break; - } - - NOTREACHED(); - return LineBreak::kAuto; -} - -template <> inline CSSIdentifierValue::CSSIdentifierValue(EMarginCollapse e) : CSSValue(kIdentifierClass) { switch (e) { @@ -1146,10 +1103,10 @@ inline CSSIdentifierValue::CSSIdentifierValue(TextUnderlinePosition e) : CSSValue(kIdentifierClass) { switch (e) { - case kTextUnderlinePositionAuto: + case TextUnderlinePosition::kAuto: value_id_ = CSSValueAuto; break; - case kTextUnderlinePositionUnder: + case TextUnderlinePosition::kUnder: value_id_ = CSSValueUnder; break; } @@ -1161,9 +1118,9 @@ inline TextUnderlinePosition CSSIdentifierValue::ConvertTo() const { switch (value_id_) { case CSSValueAuto: - return kTextUnderlinePositionAuto; + return TextUnderlinePosition::kAuto; case CSSValueUnder: - return kTextUnderlinePositionUnder; + return TextUnderlinePosition::kUnder; default: break; } @@ -1171,7 +1128,7 @@ // FIXME: Implement support for 'under left' and 'under right' values. NOTREACHED(); - return kTextUnderlinePositionAuto; + return TextUnderlinePosition::kAuto; } template <> @@ -1387,34 +1344,6 @@ } template <> -inline CSSIdentifierValue::CSSIdentifierValue(RubyPosition position) - : CSSValue(kIdentifierClass) { - switch (position) { - case RubyPosition::kBefore: - value_id_ = CSSValueBefore; - break; - case RubyPosition::kAfter: - value_id_ = CSSValueAfter; - break; - } -} - -template <> -inline RubyPosition CSSIdentifierValue::ConvertTo() const { - switch (value_id_) { - case CSSValueBefore: - return RubyPosition::kBefore; - case CSSValueAfter: - return RubyPosition::kAfter; - default: - break; - } - - NOTREACHED(); - return RubyPosition::kBefore; -} - -template <> inline CSSIdentifierValue::CSSIdentifierValue(TextEmphasisPosition position) : CSSValue(kIdentifierClass) { switch (position) {
diff --git a/third_party/WebKit/Source/core/css/CSSProperties.json5 b/third_party/WebKit/Source/core/css/CSSProperties.json5 index 55217e2..dbc65d5 100644 --- a/third_party/WebKit/Source/core/css/CSSProperties.json5 +++ b/third_party/WebKit/Source/core/css/CSSProperties.json5
@@ -2148,7 +2148,7 @@ runtime_flag: "CSS3TextDecorations", field_template: "storage_only", type_name: "TextUnderlinePosition", - default_value: "kTextUnderlinePositionAuto", + default_value: "TextUnderlinePosition::kAuto", field_size: 1, field_group: "rare-inherited", }, @@ -2435,10 +2435,10 @@ { name: "-webkit-line-break", inherited: true, - field_template: "storage_only", + field_template: "keyword", type_name: "LineBreak", - default_value: "LineBreak::kAuto", - field_size: 3, + keywords: ["auto", "loose", "normal", "strict", "after-white-space"], + default_value: "auto", field_group: "rare-inherited", }, { @@ -2571,10 +2571,10 @@ { name: "-webkit-ruby-position", inherited: true, - field_template: "storage_only", + field_template: "keyword", type_name: "RubyPosition", - default_value: "RubyPosition::kBefore", - field_size: 1, + keywords: ["before", "after"], + default_value: "before", field_group: "rare-inherited", }, { @@ -2610,7 +2610,11 @@ { name: "-webkit-text-emphasis-position", inherited: true, + field_template: "keyword", type_name: "TextEmphasisPosition", + default_value: "over", + keywords: ["over", "under"], + field_group: "rare-inherited", }, { name: "-webkit-text-emphasis-style",
diff --git a/third_party/WebKit/Source/core/css/ComputedStyleExtraFields.json5 b/third_party/WebKit/Source/core/css/ComputedStyleExtraFields.json5 index 8038a50..a68b70f 100644 --- a/third_party/WebKit/Source/core/css/ComputedStyleExtraFields.json5 +++ b/third_party/WebKit/Source/core/css/ComputedStyleExtraFields.json5
@@ -429,10 +429,10 @@ { name: "TextEmphasisFill", inherited: true, - field_template: "storage_only", + field_template: "keyword", type_name: "TextEmphasisFill", - default_value: "TextEmphasisFill::kFilled", - field_size: 1, + default_value: "filled", + keywords: ["filled", "open"], field_group: "rare-inherited", }, { @@ -445,15 +445,6 @@ field_group: "rare-inherited", }, { - name: "TextEmphasisPosition", - inherited: true, - field_template: "storage_only", - type_name: "TextEmphasisPosition", - default_value: "TextEmphasisPosition::kOver", - field_size: 1, - field_group: "rare-inherited", - }, - { name: "TextIndentLine", inherited: true, field_template: "keyword", @@ -538,7 +529,7 @@ { name: "TextEmphasisCustomMark", inherited: true, - field_template: "storage_only", + field_template: "external", type_name: "AtomicString", include_paths: ["platform/wtf/text/AtomicString.h"], default_value: "AtomicString()",
diff --git a/third_party/WebKit/Source/core/css/parser/CSSSelectorParser.cpp b/third_party/WebKit/Source/core/css/parser/CSSSelectorParser.cpp index 67295cfce..a5caa674 100644 --- a/third_party/WebKit/Source/core/css/parser/CSSSelectorParser.cpp +++ b/third_party/WebKit/Source/core/css/parser/CSSSelectorParser.cpp
@@ -762,13 +762,15 @@ // ::cue, ::shadow), we need a universal selector to set the combinator // (relation) on in the cases where there are no simple selectors preceding // the pseudo element. - bool explicit_for_host = - compound_selector->IsHostPseudoSelector() && !element_name.IsNull(); - if (tag != AnyQName() || explicit_for_host || - compound_selector->NeedsImplicitShadowCombinatorForMatching()) + bool is_host_pseudo = compound_selector->IsHostPseudoSelector(); + if (is_host_pseudo && element_name.IsNull() && namespace_prefix.IsNull()) + return; + if (tag != AnyQName() || is_host_pseudo || + compound_selector->NeedsImplicitShadowCombinatorForMatching()) { compound_selector->PrependTagSelector( tag, determined_prefix == g_null_atom && - determined_element_name == g_star_atom && !explicit_for_host); + determined_element_name == g_star_atom && !is_host_pseudo); + } } std::unique_ptr<CSSParserSelector>
diff --git a/third_party/WebKit/Source/core/editing/Editor.cpp b/third_party/WebKit/Source/core/editing/Editor.cpp index ca84712..4590346 100644 --- a/third_party/WebKit/Source/core/editing/Editor.cpp +++ b/third_party/WebKit/Source/core/editing/Editor.cpp
@@ -1089,10 +1089,8 @@ if (!CanEdit()) return false; - VisiblePosition caret = GetFrame() - .Selection() - .ComputeVisibleSelectionInDOMTreeDeprecated() - .VisibleStart(); + VisiblePosition caret = + GetFrame().Selection().ComputeVisibleSelectionInDOMTree().VisibleStart(); bool align_to_edge = IsEndOfEditableOrNonEditableContent(caret); DCHECK(GetFrame().GetDocument()); if (!TypingCommand::InsertLineBreak(*GetFrame().GetDocument())) @@ -1111,10 +1109,8 @@ if (!CanEditRichly()) return InsertLineBreak(); - VisiblePosition caret = GetFrame() - .Selection() - .ComputeVisibleSelectionInDOMTreeDeprecated() - .VisibleStart(); + VisiblePosition caret = + GetFrame().Selection().ComputeVisibleSelectionInDOMTree().VisibleStart(); bool align_to_edge = IsEndOfEditableOrNonEditableContent(caret); DCHECK(GetFrame().GetDocument()); EditingState editing_state;
diff --git a/third_party/WebKit/Source/core/editing/iterators/TextIterator.cpp b/third_party/WebKit/Source/core/editing/iterators/TextIterator.cpp index 91854ed..51ca7fe 100644 --- a/third_party/WebKit/Source/core/editing/iterators/TextIterator.cpp +++ b/third_party/WebKit/Source/core/editing/iterators/TextIterator.cpp
@@ -170,13 +170,9 @@ DCHECK(!start.GetDocument()->View() || !start.GetDocument()->View()->NeedsLayout()); DCHECK(!start.GetDocument()->NeedsLayoutTreeUpdate()); - - if (start.CompareTo(end) > 0) { - Initialize(end.ComputeContainerNode(), end.ComputeOffsetInContainerNode(), - start.ComputeContainerNode(), - start.ComputeOffsetInContainerNode()); - return; - } + // To avoid renderer hang, we use |CHECK_LE()| to catch the bad callers + // in release build. + CHECK_LE(start, end); Initialize(start.ComputeContainerNode(), start.ComputeOffsetInContainerNode(), end.ComputeContainerNode(), end.ComputeOffsetInContainerNode()); }
diff --git a/third_party/WebKit/Source/core/editing/markers/DocumentMarker.cpp b/third_party/WebKit/Source/core/editing/markers/DocumentMarker.cpp index b3b2c60..1accf9a 100644 --- a/third_party/WebKit/Source/core/editing/markers/DocumentMarker.cpp +++ b/third_party/WebKit/Source/core/editing/markers/DocumentMarker.cpp
@@ -169,12 +169,6 @@ thick, background_color)) {} -DocumentMarker::DocumentMarker(const DocumentMarker& marker) - : type_(marker.GetType()), - start_offset_(marker.StartOffset()), - end_offset_(marker.EndOffset()), - details_(marker.Details()) {} - Optional<DocumentMarker::MarkerOffsets> DocumentMarker::ComputeOffsetsAfterShift(unsigned offset, unsigned old_length,
diff --git a/third_party/WebKit/Source/core/editing/markers/DocumentMarker.h b/third_party/WebKit/Source/core/editing/markers/DocumentMarker.h index 6d76290..2a1dd7d 100644 --- a/third_party/WebKit/Source/core/editing/markers/DocumentMarker.h +++ b/third_party/WebKit/Source/core/editing/markers/DocumentMarker.h
@@ -142,8 +142,6 @@ bool thick, Color background_color); - DocumentMarker(const DocumentMarker&); - MarkerType GetType() const { return type_; } unsigned StartOffset() const { return start_offset_; } unsigned EndOffset() const { return end_offset_; } @@ -183,6 +181,8 @@ unsigned start_offset_; unsigned end_offset_; Member<DocumentMarkerDetails> details_; + + DISALLOW_COPY_AND_ASSIGN(DocumentMarker); }; using DocumentMarkerVector = HeapVector<Member<DocumentMarker>>;
diff --git a/third_party/WebKit/Source/core/editing/markers/DocumentMarkerListEditor.cpp b/third_party/WebKit/Source/core/editing/markers/DocumentMarkerListEditor.cpp index c37a4fe5..b629b20b 100644 --- a/third_party/WebKit/Source/core/editing/markers/DocumentMarkerListEditor.cpp +++ b/third_party/WebKit/Source/core/editing/markers/DocumentMarkerListEditor.cpp
@@ -66,7 +66,7 @@ return start_offset < marker->EndOffset(); }); for (MarkerList::iterator i = start_pos; i != list->end();) { - DocumentMarker marker(*i->Get()); + const DocumentMarker& marker = *i->Get(); // markers are returned in order, so stop if we are now past the specified // range
diff --git a/third_party/WebKit/Source/core/editing/markers/TextMatchMarker.h b/third_party/WebKit/Source/core/editing/markers/TextMatchMarker.h index c6c87b1..559f1a79 100644 --- a/third_party/WebKit/Source/core/editing/markers/TextMatchMarker.h +++ b/third_party/WebKit/Source/core/editing/markers/TextMatchMarker.h
@@ -70,9 +70,6 @@ bool IsValid() const { return state_ != State::kInvalid; } private: - explicit TextMatchMarker(const DocumentMarker& marker) - : DocumentMarker(marker), state_(State::kInvalid) {} - LayoutRect rendered_rect_; State state_; };
diff --git a/third_party/WebKit/Source/core/layout/ng/inline/ng_inline_item.cc b/third_party/WebKit/Source/core/layout/ng/inline/ng_inline_item.cc index 61a5e31..6db6e70 100644 --- a/third_party/WebKit/Source/core/layout/ng/inline/ng_inline_item.cc +++ b/third_party/WebKit/Source/core/layout/ng/inline/ng_inline_item.cc
@@ -4,6 +4,7 @@ #include "core/layout/ng/inline/ng_inline_item.h" +#include "core/layout/LayoutInline.h" #include "core/layout/LayoutObject.h" #include "platform/fonts/CharacterRange.h" #include "platform/fonts/shaping/ShapeResultBuffer.h" @@ -123,6 +124,18 @@ shape_result_->FallbackFonts(fallback_fonts); } +bool NGInlineItem::HasStartEdge() const { + DCHECK(Type() == kOpenTag || Type() == kCloseTag); + // TODO(kojii): Should use break token when NG has its own tree building. + return !GetLayoutObject()->IsInlineElementContinuation(); +} + +bool NGInlineItem::HasEndEdge() const { + DCHECK(Type() == kOpenTag || Type() == kCloseTag); + // TODO(kojii): Should use break token when NG has its own tree building. + return !ToLayoutInline(GetLayoutObject())->Continuation(); +} + NGInlineItemRange::NGInlineItemRange(Vector<NGInlineItem>* items, unsigned start_index, unsigned end_index)
diff --git a/third_party/WebKit/Source/core/layout/ng/inline/ng_inline_item.h b/third_party/WebKit/Source/core/layout/ng/inline/ng_inline_item.h index 1d14bbfc..49bcd73 100644 --- a/third_party/WebKit/Source/core/layout/ng/inline/ng_inline_item.h +++ b/third_party/WebKit/Source/core/layout/ng/inline/ng_inline_item.h
@@ -93,6 +93,9 @@ unsigned start, unsigned end) const; + bool HasStartEdge() const; + bool HasEndEdge() const; + static void Split(Vector<NGInlineItem>&, unsigned index, unsigned offset); static unsigned SetBidiLevel(Vector<NGInlineItem>&, unsigned index,
diff --git a/third_party/WebKit/Source/core/layout/ng/inline/ng_inline_item_result.cc b/third_party/WebKit/Source/core/layout/ng/inline/ng_inline_item_result.cc index a4a467e..3c315cb33 100644 --- a/third_party/WebKit/Source/core/layout/ng/inline/ng_inline_item_result.cc +++ b/third_party/WebKit/Source/core/layout/ng/inline/ng_inline_item_result.cc
@@ -11,6 +11,10 @@ NGInlineItemResult::NGInlineItemResult(unsigned index, unsigned start, unsigned end) - : item_index(index), start_offset(start), end_offset(end) {} + : item_index(index), + start_offset(start), + end_offset(end), + no_break_opportunities_inside(false), + prohibit_break_after(false) {} } // namespace blink
diff --git a/third_party/WebKit/Source/core/layout/ng/inline/ng_inline_item_result.h b/third_party/WebKit/Source/core/layout/ng/inline/ng_inline_item_result.h index 0e52770..12dd1b6 100644 --- a/third_party/WebKit/Source/core/layout/ng/inline/ng_inline_item_result.h +++ b/third_party/WebKit/Source/core/layout/ng/inline/ng_inline_item_result.h
@@ -42,6 +42,14 @@ // NGBoxStrut for atomic inline items. NGBoxStrut margins; + // Inside of this is not breakable. + // Used only during line breaking. + unsigned no_break_opportunities_inside : 1; + + // Lines must not break after this. + // Used only during line breaking. + unsigned prohibit_break_after : 1; + NGInlineItemResult(); NGInlineItemResult(unsigned index, unsigned start, unsigned end); };
diff --git a/third_party/WebKit/Source/core/layout/ng/inline/ng_inline_layout_algorithm.cc b/third_party/WebKit/Source/core/layout/ng/inline/ng_inline_layout_algorithm.cc index fb30678..1a426a9 100644 --- a/third_party/WebKit/Source/core/layout/ng/inline/ng_inline_layout_algorithm.cc +++ b/third_party/WebKit/Source/core/layout/ng/inline/ng_inline_layout_algorithm.cc
@@ -271,8 +271,10 @@ borders.BlockSum() + paddings.BlockSum()); } } else if (item.Type() == NGInlineItem::kCloseTag) { + position += item_result.inline_size; box = box_states_.OnCloseTag(item, &line_box, box, baseline_type_, position); + continue; } else if (item.Type() == NGInlineItem::kAtomicInline) { box = PlaceAtomicInline(item, &item_result, position, &line_box, &text_builder); @@ -303,8 +305,7 @@ // The baselines are always placed at pixel boundaries. Not doing so results // in incorrect layout of text decorations, most notably underlines. - LayoutUnit baseline = content_size_ + line_box.Metrics().ascent + - border_and_padding_.block_start; + LayoutUnit baseline = content_size_ + line_box.Metrics().ascent; baseline = LayoutUnit(baseline.Round()); // Check if the line fits into the constraint space in block direction. @@ -429,6 +430,10 @@ } RefPtr<NGLayoutResult> NGInlineLayoutAlgorithm::Layout() { + // If we are resuming from a break token our start border and padding is + // within a previous fragment. + content_size_ = BreakToken() ? LayoutUnit() : border_and_padding_.block_start; + NGLineBreaker line_breaker(Node(), constraint_space_, BreakToken()); NGInlineItemResults item_results; while (true) { @@ -440,7 +445,7 @@ } // TODO(crbug.com/716930): Avoid calculating border/padding twice. - if (!Node()->Items().IsEmpty()) + if (!BreakToken()) content_size_ -= border_and_padding_.block_start; // TODO(kojii): Check if the line box width should be content or available.
diff --git a/third_party/WebKit/Source/core/layout/ng/inline/ng_inline_node.h b/third_party/WebKit/Source/core/layout/ng/inline/ng_inline_node.h index 4524c5d8..6cb3e091 100644 --- a/third_party/WebKit/Source/core/layout/ng/inline/ng_inline_node.h +++ b/third_party/WebKit/Source/core/layout/ng/inline/ng_inline_node.h
@@ -93,6 +93,8 @@ LayoutObject* start_inline_; LayoutNGBlockFlow* block_; Member<NGLayoutInputNode> next_sibling_; + + friend class NGLineBreakerTest; }; inline void NGInlineNode::AssertOffset(unsigned index, unsigned offset) const {
diff --git a/third_party/WebKit/Source/core/layout/ng/inline/ng_line_breaker.cc b/third_party/WebKit/Source/core/layout/ng/inline/ng_line_breaker.cc index d464a03..4656455 100644 --- a/third_party/WebKit/Source/core/layout/ng/inline/ng_line_breaker.cc +++ b/third_party/WebKit/Source/core/layout/ng/inline/ng_line_breaker.cc
@@ -17,7 +17,6 @@ #include "core/style/ComputedStyle.h" #include "platform/fonts/shaping/HarfBuzzShaper.h" #include "platform/fonts/shaping/ShapingLineBreaker.h" -#include "platform/text/TextBreakIterator.h" namespace blink { @@ -28,19 +27,7 @@ #if defined(MOCK_SHAPE_LINE) // The mock for ShapingLineBreaker::ShapeLine(). -// Given the design of ShapingLineBreaker, expected semantics are: -// - The returned offset is always > item.StartOffset(). -// - offset < item.EndOffset(): -// - width <= available_width: the break opportunity to fit is found. -// - width > available_width: the first break opportunity did not fit. -// - offset == item.EndOffset(): -// - width <= available_width: the break opportunity at the end of the item -// fits. -// - width > available_width: the first break opportunity is at the end of -// the item and it does not fit. -// - offset > item.EndOffset():, the first break opportunity is beyond the -// end of item and thus cannot measure. In this case, inline_size shows the -// width until the end of the item. It may fit or may not. +// See BreakText() for the expected semantics. std::pair<unsigned, LayoutUnit> ShapeLineMock( const NGInlineItem& item, unsigned offset, @@ -67,23 +54,16 @@ } #endif -LineBreakType GetLineBreakType(const ComputedStyle& style) { - if (style.AutoWrap()) { - if (style.WordBreak() == EWordBreak::kBreakAll || - style.WordBreak() == EWordBreak::kBreakWord) - return LineBreakType::kBreakAll; - if (style.WordBreak() == EWordBreak::kKeepAll) - return LineBreakType::kKeepAll; - } - return LineBreakType::kNormal; -} - } // namespace NGLineBreaker::NGLineBreaker(NGInlineNode* node, const NGConstraintSpace* space, NGInlineBreakToken* break_token) - : node_(node), constraint_space_(space), item_index_(0), offset_(0) { + : node_(node), + constraint_space_(space), + item_index_(0), + offset_(0), + break_iterator_(node->Text()) { if (break_token) { item_index_ = break_token->ItemIndex(); offset_ = break_token->TextOffset(); @@ -106,130 +86,169 @@ NGInlineLayoutAlgorithm* algorithm) { DCHECK(item_results->IsEmpty()); const Vector<NGInlineItem>& items = node_->Items(); - const String& text = node_->Text(); const ComputedStyle& style = node_->Style(); - LazyLineBreakIterator break_iterator(text, style.LocaleForLineBreakIterator(), - GetLineBreakType(style)); + UpdateBreakIterator(style); #if !defined(MOCK_SHAPE_LINE) + // TODO(kojii): Instantiate in the constructor. HarfBuzzShaper shaper(text.Characters16(), text.length()); #endif - LayoutUnit available_width = algorithm->AvailableWidth(); - LayoutUnit position; + available_width_ = algorithm->AvailableWidth(); + position_ = LayoutUnit(0); + LineBreakState state = LineBreakState::kNotBreakable; + while (item_index_ < items.size()) { + // CloseTag prohibits to break before. const NGInlineItem& item = items[item_index_]; + if (item.Type() == NGInlineItem::kCloseTag) { + item_results->push_back( + NGInlineItemResult(item_index_, offset_, item.EndOffset())); + HandleCloseTag(item, &item_results->back()); + continue; + } + + if (state == LineBreakState::kBreakAfterTrailings) + return; + if (state == LineBreakState::kIsBreakable && position_ > available_width_) + return HandleOverflow(item_results); + item_results->push_back( NGInlineItemResult(item_index_, offset_, item.EndOffset())); NGInlineItemResult* item_result = &item_results->back(); - - // If the start offset is at the item boundary, try to add the entire item. - if (offset_ == item.StartOffset()) { - if (item.Type() == NGInlineItem::kText) { - item_result->inline_size = item.InlineSize(); - } else if (item.Type() == NGInlineItem::kAtomicInline) { - LayoutAtomicInline(item, item_result); - } else if (item.Type() == NGInlineItem::kControl) { - if (HandleControlItem(item, text, item_result, position)) { - MoveToNextOf(item); - break; - } - } else if (item.Type() == NGInlineItem::kFloating) { - algorithm->LayoutAndPositionFloat(position, item.GetLayoutObject()); - // Floats may change the available width if they fit. - available_width = algorithm->AvailableWidth(); - // Floats are already positioned in the container_builder. - item_results->pop_back(); - MoveToNextOf(item); - continue; - } else { - MoveToNextOf(item); - continue; - } - LayoutUnit next_position = position + item_result->inline_size; - if (next_position <= available_width) { - MoveToNextOf(item); - position = next_position; - continue; - } - - // The entire item does not fit. Handle non-text items as overflow, - // since only text item is breakable. - if (item.Type() != NGInlineItem::kText) { - MoveToNextOf(item); - return HandleOverflow(item_results, break_iterator); - } - } - - // Either the start or the break is in the mid of a text item. - DCHECK_EQ(item.Type(), NGInlineItem::kText); - DCHECK_LT(offset_, item.EndOffset()); - break_iterator.SetLocale(item.Style()->LocaleForLineBreakIterator()); - break_iterator.SetBreakType(GetLineBreakType(*item.Style())); -#if defined(MOCK_SHAPE_LINE) - unsigned break_offset; - std::tie(break_offset, item_result->inline_size) = ShapeLineMock( - item, offset_, available_width - position, break_iterator); -#else - // TODO(kojii): We need to instantiate ShapingLineBreaker here because it - // has item-specific info as context. Should they be part of ShapeLine() to - // instantiate once, or is this just fine since instatiation is not - // expensive? - DCHECK_EQ(item.TextShapeResult()->StartIndexForResult(), - item.StartOffset()); - DCHECK_EQ(item.TextShapeResult()->EndIndexForResult(), item.EndOffset()); - ShapingLineBreaker breaker(&shaper, &item.Style()->GetFont(), - item.TextShapeResult(), &break_iterator); - unsigned break_offset; - item_result->shape_result = - breaker.ShapeLine(offset_, available_width - position, &break_offset); - item_result->inline_size = item_result->shape_result->SnappedWidth(); -#endif - DCHECK_GT(break_offset, offset_); - position += item_result->inline_size; - - // If the break found within the item, break here. - if (break_offset < item.EndOffset()) { - offset_ = item_result->end_offset = break_offset; - if (position <= available_width) - break; - // The first break opportunity of the item does not fit. + if (item.Type() == NGInlineItem::kText) { + state = HandleText(item, item_result); + } else if (item.Type() == NGInlineItem::kAtomicInline) { + state = HandleAtomicInline(item, item_result); + } else if (item.Type() == NGInlineItem::kControl) { + state = HandleControlItem(item, item_result); + if (state == LineBreakState::kForcedBreak) + return; + } else if (item.Type() == NGInlineItem::kOpenTag) { + HandleOpenTag(item, item_result); + state = LineBreakState::kNotBreakable; + } else if (item.Type() == NGInlineItem::kFloating) { + HandleFloat(item, item_results, algorithm); } else { - // No break opporunity in the item, or the first break opportunity is at - // the end of the item. If it fits, continue to the next item. - item_result->end_offset = item.EndOffset(); MoveToNextOf(item); - if (position <= available_width) - continue; } + } + if (state == LineBreakState::kIsBreakable && position_ > available_width_) + return HandleOverflow(item_results); +} - // We need to look at next item if we're overflowing, and the break - // opportunity is beyond this item. - if (break_offset > item.EndOffset()) - continue; - return HandleOverflow(item_results, break_iterator); +NGLineBreaker::LineBreakState NGLineBreaker::HandleText( + const NGInlineItem& item, + NGInlineItemResult* item_result) { + DCHECK_EQ(item.Type(), NGInlineItem::kText); + + // If the start offset is at the item boundary, try to add the entire item. + if (offset_ == item.StartOffset()) { + item_result->inline_size = item.InlineSize(); + LayoutUnit next_position = position_ + item_result->inline_size; + if (!auto_wrap_ || next_position <= available_width_) { + position_ = next_position; + MoveToNextOf(item); + if (auto_wrap_ && break_iterator_.IsBreakable(item.EndOffset())) + return LineBreakState::kIsBreakable; + item_result->prohibit_break_after = true; + return LineBreakState::kNotBreakable; + } + } + + if (auto_wrap_) { + // Try to break inside of this text item. + BreakText(item_result, item, available_width_ - position_); + position_ += item_result->inline_size; + + bool is_overflow = position_ > available_width_; + item_result->no_break_opportunities_inside = is_overflow; + if (item_result->end_offset < item.EndOffset()) { + offset_ = item_result->end_offset; + return is_overflow ? LineBreakState::kIsBreakable + : LineBreakState::kBreakAfterTrailings; + } + MoveToNextOf(item); + return item_result->prohibit_break_after ? LineBreakState::kNotBreakable + : LineBreakState::kIsBreakable; + } + + // Add the rest of the item if !auto_wrap. + // Because the start position may need to reshape, run ShapingLineBreaker + // with max available width. + DCHECK_NE(offset_, item.StartOffset()); + BreakText(item_result, item, LayoutUnit::Max()); + DCHECK_EQ(item_result->end_offset, item.EndOffset()); + item_result->no_break_opportunities_inside = true; + item_result->prohibit_break_after = true; + position_ += item_result->inline_size; + MoveToNextOf(item); + return LineBreakState::kNotBreakable; +} + +void NGLineBreaker::BreakText(NGInlineItemResult* item_result, + const NGInlineItem& item, + LayoutUnit available_width) { + DCHECK_EQ(item.Type(), NGInlineItem::kText); + item.AssertOffset(item_result->start_offset); + +#if defined(MOCK_SHAPE_LINE) + std::tie(item_result->end_offset, item_result->inline_size) = ShapeLineMock( + item, item_result->start_offset, available_width, break_iterator_); +#else + // TODO(kojii): We need to instantiate ShapingLineBreaker here because it + // has item-specific info as context. Should they be part of ShapeLine() to + // instantiate once, or is this just fine since instatiation is not + // expensive? + DCHECK_EQ(item.TextShapeResult()->StartIndexForResult(), item.StartOffset()); + DCHECK_EQ(item.TextShapeResult()->EndIndexForResult(), item.EndOffset()); + ShapingLineBreaker breaker(&shaper, &item.Style()->GetFont(), + item.TextShapeResult(), break_iterator_); + item_result->shape_result = breaker.ShapeLine( + item_result->start_offset, available_width, &item_result->end_offset); + item_result->inline_size = item_result->shape_result->SnappedWidth(); +#endif + DCHECK_GT(item_result->end_offset, item_result->start_offset); + // * If width <= available_width: + // * If offset < item.EndOffset(): the break opportunity to fit is found. + // * If offset == item.EndOffset(): the break opportunity at the end fits. + // There may be room for more characters. + // * If offset > item.EndOffset(): the first break opportunity is beyond + // the end. There may be room for more characters. + // * If width > available_width: The first break opporunity does not fit. + // offset is the first break opportunity, either inside, at the end, or + // beyond the end. + if (item_result->end_offset <= item.EndOffset()) { + item_result->prohibit_break_after = false; + } else { + item_result->prohibit_break_after = true; + item_result->end_offset = item.EndOffset(); } } // Measure control items; new lines and tab, that are similar to text, affect // layout, but do not need shaping/painting. -bool NGLineBreaker::HandleControlItem(const NGInlineItem& item, - const String& text, - NGInlineItemResult* item_result, - LayoutUnit position) { +NGLineBreaker::LineBreakState NGLineBreaker::HandleControlItem( + const NGInlineItem& item, + NGInlineItemResult* item_result) { DCHECK_EQ(item.Length(), 1u); - UChar character = text[item.StartOffset()]; - if (character == kNewlineCharacter) - return true; - + UChar character = node_->Text()[item.StartOffset()]; + if (character == kNewlineCharacter) { + MoveToNextOf(item); + return LineBreakState::kForcedBreak; + } DCHECK_EQ(character, kTabulationCharacter); DCHECK(item.Style()); const ComputedStyle& style = *item.Style(); const Font& font = style.GetFont(); - item_result->inline_size = font.TabWidth(style.GetTabSize(), position); - return false; + item_result->inline_size = font.TabWidth(style.GetTabSize(), position_); + position_ += item_result->inline_size; + MoveToNextOf(item); + // TODO(kojii): Implement break around the tab character. + return LineBreakState::kIsBreakable; } -void NGLineBreaker::LayoutAtomicInline(const NGInlineItem& item, - NGInlineItemResult* item_result) { +NGLineBreaker::LineBreakState NGLineBreaker::HandleAtomicInline( + const NGInlineItem& item, + NGInlineItemResult* item_result) { DCHECK_EQ(item.Type(), NGInlineItem::kAtomicInline); NGBlockNode* node = new NGBlockNode(item.GetLayoutObject()); const ComputedStyle& style = node->Style(); @@ -251,65 +270,158 @@ ComputeMargins(*constraint_space_, style, constraint_space_->WritingMode(), style.Direction()); item_result->inline_size += item_result->margins.InlineSum(); + + position_ += item_result->inline_size; + MoveToNextOf(item); + if (auto_wrap_) + return LineBreakState::kIsBreakable; + item_result->prohibit_break_after = true; + return LineBreakState::kNotBreakable; +} + +void NGLineBreaker::HandleFloat(const NGInlineItem& item, + NGInlineItemResults* item_results, + NGInlineLayoutAlgorithm* algorithm) { + algorithm->LayoutAndPositionFloat(position_, item.GetLayoutObject()); + // Floats may change the available width if they fit. + available_width_ = algorithm->AvailableWidth(); + // Floats are already positioned in the container_builder. + item_results->pop_back(); + MoveToNextOf(item); +} + +void NGLineBreaker::HandleOpenTag(const NGInlineItem& item, + NGInlineItemResult* item_result) { + if (item.HasStartEdge()) { + DCHECK(item.Style()); + // TODO(kojii): We compute 16 values and discard 12 out of that, and do it 3 + // times per element. We may want to cache this. crrev.com/2865903002/#msg14 + NGBoxStrut margins = ComputeMargins(*constraint_space_, *item.Style(), + constraint_space_->WritingMode(), + constraint_space_->Direction()); + NGBoxStrut borders = ComputeBorders(*constraint_space_, *item.Style()); + NGBoxStrut paddings = ComputePadding(*constraint_space_, *item.Style()); + item_result->inline_size = + margins.inline_start + borders.inline_start + paddings.inline_start; + position_ += item_result->inline_size; + } + UpdateBreakIterator(*item.Style()); + MoveToNextOf(item); +} + +void NGLineBreaker::HandleCloseTag(const NGInlineItem& item, + NGInlineItemResult* item_result) { + if (item.HasEndEdge()) { + DCHECK(item.Style()); + NGBoxStrut margins = ComputeMargins(*constraint_space_, *item.Style(), + constraint_space_->WritingMode(), + constraint_space_->Direction()); + NGBoxStrut borders = ComputeBorders(*constraint_space_, *item.Style()); + NGBoxStrut paddings = ComputePadding(*constraint_space_, *item.Style()); + item_result->inline_size = + margins.inline_end + borders.inline_end + paddings.inline_end; + position_ += item_result->inline_size; + } + DCHECK(item.GetLayoutObject() && item.GetLayoutObject()->Parent()); + UpdateBreakIterator(item.GetLayoutObject()->Parent()->StyleRef()); + MoveToNextOf(item); } // Handles when the last item overflows. // At this point, item_results does not fit into the current line, and there // are no break opportunities in item_results.back(). -void NGLineBreaker::HandleOverflow( - NGInlineItemResults* item_results, - const LazyLineBreakIterator& break_iterator) { - DCHECK_GT(offset_, 0u); - - // Find the last break opportunity. If none, let this line overflow. - unsigned line_start_offset = item_results->front().start_offset; - unsigned break_offset = - break_iterator.PreviousBreakOpportunity(offset_ - 1, line_start_offset); - if (!break_offset || break_offset <= line_start_offset) { - AppendCloseTags(item_results); - return; - } - - // Truncate the end of the line to the break opportunity. +void NGLineBreaker::HandleOverflow(NGInlineItemResults* item_results) { const Vector<NGInlineItem>& items = node_->Items(); - unsigned new_end = item_results->size(); - while (true) { - NGInlineItemResult* item_result = &(*item_results)[--new_end]; - if (item_result->start_offset < break_offset) { - // The break is at the mid of the item. Adjust the end_offset to the new - // break offset. - const NGInlineItem& item = items[item_result->item_index]; - item.AssertEndOffset(break_offset); - DCHECK_EQ(item.Type(), NGInlineItem::kText); - DCHECK_NE(item_result->end_offset, break_offset); - item_result->end_offset = break_offset; - item_result->inline_size = - item.InlineSize(item_result->start_offset, item_result->end_offset); - // TODO(kojii): May need to reshape. Add to ShapingLineBreaker? - new_end++; - break; - } - if (item_result->start_offset == break_offset) { - // The new break offset is at the item boundary. Remove items up to the - // new break offset. - // TODO(kojii): Remove open tags as well. - break; + LayoutUnit rewind_width = available_width_ - position_; + DCHECK_LT(rewind_width, 0); + + // Search for a break opportunity that can fit. + // Also keep track of the first break opportunity in case of overflow. + unsigned break_before = 0; + unsigned break_before_if_before_allow = 0; + LayoutUnit rewind_width_if_before_allow; + bool last_item_prohibits_break_before = true; + for (unsigned i = item_results->size(); i;) { + NGInlineItemResult* item_result = &(*item_results)[--i]; + const NGInlineItem& item = items[item_result->item_index]; + rewind_width += item_result->inline_size; + if (item.Type() == NGInlineItem::kText || + item.Type() == NGInlineItem::kAtomicInline) { + // Try to break inside of this item. + if (item.Type() == NGInlineItem::kText && rewind_width >= 0 && + !item_result->no_break_opportunities_inside) { + // When the text fits but its right margin does not, the break point + // must not be at the end. + LayoutUnit item_available_width = + std::min(rewind_width, item_result->inline_size - 1); + BreakText(item_result, item, item_available_width); + if (item_result->inline_size <= item_available_width) { + DCHECK_LT(item_result->end_offset, item.EndOffset()); + DCHECK(!item_result->prohibit_break_after); + return Rewind(item_results, i + 1); + } + if (!item_result->prohibit_break_after && + !last_item_prohibits_break_before) { + break_before = i + 1; + } + } + + // Try to break after this item. + if (break_before_if_before_allow && !item_result->prohibit_break_after) { + if (rewind_width_if_before_allow >= 0) + return Rewind(item_results, break_before_if_before_allow); + break_before = break_before_if_before_allow; + } + + // Otherwise, before this item is a possible break point. + break_before_if_before_allow = i; + rewind_width_if_before_allow = rewind_width; + last_item_prohibits_break_before = false; + } else if (item.Type() == NGInlineItem::kCloseTag) { + last_item_prohibits_break_before = true; + } else { + if (i + 1 == break_before_if_before_allow) { + break_before_if_before_allow = i; + rewind_width_if_before_allow = rewind_width; + } + last_item_prohibits_break_before = false; } } - DCHECK_GT(new_end, 0u); + // The rewind point did not found, let this line overflow. + // If there was a break opporunity, the overflow should stop there. + if (break_before) + Rewind(item_results, break_before); +} + +void NGLineBreaker::Rewind(NGInlineItemResults* item_results, + unsigned new_end) { // TODO(kojii): Should we keep results for the next line? We don't need to // re-layout atomic inlines. // TODO(kojii): Removing processed floats is likely a problematic. Keep // floats in this line, or keep it for the next line. item_results->Shrink(new_end); - // Update the current item index and offset to the new break point. - const NGInlineItemResult& last_item_result = item_results->back(); - offset_ = last_item_result.end_offset; - item_index_ = last_item_result.item_index; - if (items[item_index_].EndOffset() == offset_) - item_index_++; + MoveToNextOf(item_results->back()); +} + +void NGLineBreaker::UpdateBreakIterator(const ComputedStyle& style) { + auto_wrap_ = style.AutoWrap(); + + if (auto_wrap_) { + break_iterator_.SetLocale(style.LocaleForLineBreakIterator()); + + if (style.WordBreak() == EWordBreak::kBreakAll || + style.WordBreak() == EWordBreak::kBreakWord) { + break_iterator_.SetBreakType(LineBreakType::kBreakAll); + } else if (style.WordBreak() == EWordBreak::kKeepAll) { + break_iterator_.SetBreakType(LineBreakType::kKeepAll); + } else { + break_iterator_.SetBreakType(LineBreakType::kNormal); + } + + // TODO(kojii): Implement word-wrap/overflow-wrap property + } } void NGLineBreaker::MoveToNextOf(const NGInlineItem& item) { @@ -318,6 +430,14 @@ item_index_++; } +void NGLineBreaker::MoveToNextOf(const NGInlineItemResult& item_result) { + offset_ = item_result.end_offset; + item_index_ = item_result.item_index; + const NGInlineItem& item = node_->Items()[item_result.item_index]; + if (offset_ == item.EndOffset()) + item_index_++; +} + void NGLineBreaker::SkipCollapsibleWhitespaces() { const Vector<NGInlineItem>& items = node_->Items(); if (item_index_ >= items.size()) @@ -336,17 +456,6 @@ } } -void NGLineBreaker::AppendCloseTags(NGInlineItemResults* item_results) { - const Vector<NGInlineItem>& items = node_->Items(); - for (; item_index_ < items.size(); item_index_++) { - const NGInlineItem& item = items[item_index_]; - if (item.Type() != NGInlineItem::kCloseTag) - break; - DCHECK_EQ(offset_, item.EndOffset()); - item_results->push_back(NGInlineItemResult(item_index_, offset_, offset_)); - } -} - RefPtr<NGInlineBreakToken> NGLineBreaker::CreateBreakToken() const { const Vector<NGInlineItem>& items = node_->Items(); if (item_index_ >= items.size())
diff --git a/third_party/WebKit/Source/core/layout/ng/inline/ng_line_breaker.h b/third_party/WebKit/Source/core/layout/ng/inline/ng_line_breaker.h index 3823bd94..9260b0ac 100644 --- a/third_party/WebKit/Source/core/layout/ng/inline/ng_line_breaker.h +++ b/third_party/WebKit/Source/core/layout/ng/inline/ng_line_breaker.h
@@ -8,11 +8,12 @@ #include "core/CoreExport.h" #include "core/layout/ng/inline/ng_inline_item_result.h" #include "platform/heap/Handle.h" +#include "platform/text/TextBreakIterator.h" +#include "platform/wtf/Allocator.h" #include "platform/wtf/text/AtomicString.h" namespace blink { -class LazyLineBreakIterator; class NGInlineBreakToken; class NGInlineItem; class NGInlineNode; @@ -43,24 +44,50 @@ private: void BreakLine(NGInlineItemResults*, NGInlineLayoutAlgorithm*); - bool HandleControlItem(const NGInlineItem&, - const String& text, - NGInlineItemResult*, - LayoutUnit position); - void LayoutAtomicInline(const NGInlineItem&, NGInlineItemResult*); + enum class LineBreakState { + // The current position is not breakable. + kNotBreakable, + // The current position is breakable. + kIsBreakable, + // Break by including trailing items (CloseTag). + kBreakAfterTrailings, + // Break immediately. + kForcedBreak + }; - void HandleOverflow(NGInlineItemResults*, const LazyLineBreakIterator&); + LineBreakState HandleText(const NGInlineItem&, NGInlineItemResult*); + void BreakText(NGInlineItemResult*, + const NGInlineItem&, + LayoutUnit available_width); + + LineBreakState HandleControlItem(const NGInlineItem&, NGInlineItemResult*); + LineBreakState HandleAtomicInline(const NGInlineItem&, NGInlineItemResult*); + void HandleFloat(const NGInlineItem&, + NGInlineItemResults*, + NGInlineLayoutAlgorithm*); + + void HandleOpenTag(const NGInlineItem&, NGInlineItemResult*); + void HandleCloseTag(const NGInlineItem&, NGInlineItemResult*); + + void HandleOverflow(NGInlineItemResults*); + void Rewind(NGInlineItemResults*, unsigned new_end); + + void UpdateBreakIterator(const ComputedStyle&); void MoveToNextOf(const NGInlineItem&); + void MoveToNextOf(const NGInlineItemResult&); void SkipCollapsibleWhitespaces(); - void AppendCloseTags(NGInlineItemResults*); - Persistent<NGInlineNode> node_; const NGConstraintSpace* constraint_space_; const AtomicString locale_; unsigned item_index_; unsigned offset_; + LayoutUnit available_width_; + LayoutUnit position_; + LazyLineBreakIterator break_iterator_; + + unsigned auto_wrap_ : 1; }; } // namespace blink
diff --git a/third_party/WebKit/Source/core/layout/ng/inline/ng_line_breaker_test.cc b/third_party/WebKit/Source/core/layout/ng/inline/ng_line_breaker_test.cc new file mode 100644 index 0000000..e3d8a98 --- /dev/null +++ b/third_party/WebKit/Source/core/layout/ng/inline/ng_line_breaker_test.cc
@@ -0,0 +1,252 @@ +// Copyright 2017 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 "core/layout/ng/ng_base_layout_algorithm_test.h" + +#include "core/layout/ng/inline/ng_inline_layout_algorithm.h" +#include "core/layout/ng/inline/ng_inline_node.h" +#include "core/layout/ng/inline/ng_line_breaker.h" +#include "core/layout/ng/layout_ng_block_flow.h" +#include "core/layout/ng/ng_constraint_space_builder.h" +#include "platform/wtf/text/StringBuilder.h" + +namespace blink { + +class NGLineBreakerTest : public NGBaseLayoutAlgorithmTest { + protected: + NGInlineNode* CreateInlineNode(const String& html_content) { + SetBodyInnerHTML(html_content); + + LayoutNGBlockFlow* block_flow = + ToLayoutNGBlockFlow(GetLayoutObjectByElementId("container")); + NGInlineNode* inline_node = + new NGInlineNode(block_flow->FirstChild(), block_flow); + + return inline_node; + } + + // Break lines using the specified available width. + Vector<NGInlineItemResults> BreakLines(NGInlineNode* node, + LayoutUnit available_width) { + if (!node->IsPrepareLayoutFinished()) + node->PrepareLayout(); + + RefPtr<NGConstraintSpace> space = + NGConstraintSpaceBuilder(NGWritingMode::kHorizontalTopBottom) + .SetAvailableSize({available_width, NGSizeIndefinite}) + .ToConstraintSpace(NGWritingMode::kHorizontalTopBottom); + NGLineBreaker line_breaker(node, space.Get()); + NGInlineLayoutAlgorithm algorithm(node, space.Get()); + Vector<NGInlineItemResults> lines; + while (true) { + NGInlineItemResults item_results; + line_breaker.NextLine(&item_results, &algorithm); + if (item_results.IsEmpty()) + break; + lines.push_back(item_results); + } + return lines; + } +}; + +namespace { + +String ToString(NGInlineItemResults line, NGInlineNode* node) { + StringBuilder builder; + for (const auto& item_result : line) { + builder.Append( + node->Text(item_result.start_offset, item_result.end_offset)); + } + return builder.ToString(); +} + +TEST_F(NGLineBreakerTest, SingleNode) { + LoadAhem(); + NGInlineNode* node = CreateInlineNode(R"HTML( + <!DOCTYPE html> + <style> + #container { + font: 10px/1 Ahem; + } + </style> + <div id=container>123 456 789</div> + )HTML"); + + Vector<NGInlineItemResults> lines; + lines = BreakLines(node, LayoutUnit(80)); + EXPECT_EQ(2u, lines.size()); + EXPECT_EQ("123 456", ToString(lines[0], node)); + EXPECT_EQ("789", ToString(lines[1], node)); + + lines = BreakLines(node, LayoutUnit(60)); + EXPECT_EQ(3u, lines.size()); + EXPECT_EQ("123", ToString(lines[0], node)); + EXPECT_EQ("456", ToString(lines[1], node)); + EXPECT_EQ("789", ToString(lines[2], node)); +} + +TEST_F(NGLineBreakerTest, OverflowWord) { + LoadAhem(); + NGInlineNode* node = CreateInlineNode(R"HTML( + <!DOCTYPE html> + <style> + #container { + font: 10px/1 Ahem; + } + </style> + <div id=container>12345 678</div> + )HTML"); + + // The first line overflows, but the last line does not. + Vector<NGInlineItemResults> lines; + lines = BreakLines(node, LayoutUnit(40)); + EXPECT_EQ(2u, lines.size()); + EXPECT_EQ("12345", ToString(lines[0], node)); + EXPECT_EQ("678", ToString(lines[1], node)); + + // Both lines overflow. + lines = BreakLines(node, LayoutUnit(20)); + EXPECT_EQ(2u, lines.size()); + EXPECT_EQ("12345", ToString(lines[0], node)); + EXPECT_EQ("678", ToString(lines[1], node)); +} + +TEST_F(NGLineBreakerTest, OverflowAtomicInline) { + LoadAhem(); + NGInlineNode* node = CreateInlineNode(R"HTML( + <!DOCTYPE html> + <style> + #container { + font: 10px/1 Ahem; + } + span { + display: inline-block; + width: 30px; + height: 10px; + } + </style> + <div id=container>12345<span></span>678</div> + )HTML"); + + Vector<NGInlineItemResults> lines; + lines = BreakLines(node, LayoutUnit(80)); + EXPECT_EQ(2u, lines.size()); + EXPECT_EQ(String(u"12345\uFFFC"), ToString(lines[0], node)); + EXPECT_EQ("678", ToString(lines[1], node)); + + lines = BreakLines(node, LayoutUnit(70)); + EXPECT_EQ(2u, lines.size()); + EXPECT_EQ("12345", ToString(lines[0], node)); + EXPECT_EQ(String(u"\uFFFC678"), ToString(lines[1], node)); + + lines = BreakLines(node, LayoutUnit(40)); + EXPECT_EQ(3u, lines.size()); + EXPECT_EQ("12345", ToString(lines[0], node)); + EXPECT_EQ(String(u"\uFFFC"), ToString(lines[1], node)); + EXPECT_EQ("678", ToString(lines[2], node)); + + lines = BreakLines(node, LayoutUnit(20)); + EXPECT_EQ(3u, lines.size()); + EXPECT_EQ("12345", ToString(lines[0], node)); + EXPECT_EQ(String(u"\uFFFC"), ToString(lines[1], node)); + EXPECT_EQ("678", ToString(lines[2], node)); +} + +TEST_F(NGLineBreakerTest, OverflowMargin) { + LoadAhem(); + NGInlineNode* node = CreateInlineNode(R"HTML( + <!DOCTYPE html> + <style> + #container { + font: 10px/1 Ahem; + } + span { + margin-right: 4em; + } + </style> + <div id=container><span>123 456</span> 789</div> + )HTML"); + const Vector<NGInlineItem>& items = node->Items(); + + // While "123 456" can fit in a line, "456" has a right margin that cannot + // fit. Since "456" and its right margin is not breakable, "456" should be on + // the next line. + Vector<NGInlineItemResults> lines; + lines = BreakLines(node, LayoutUnit(80)); + EXPECT_EQ(3u, lines.size()); + EXPECT_EQ("123", ToString(lines[0], node)); + EXPECT_EQ("456", ToString(lines[1], node)); + DCHECK_EQ(NGInlineItem::kCloseTag, items[lines[1].back().item_index].Type()); + EXPECT_EQ("789", ToString(lines[2], node)); + + // Same as above, but this time "456" overflows the line because it is 70px. + lines = BreakLines(node, LayoutUnit(60)); + EXPECT_EQ(3u, lines.size()); + EXPECT_EQ("123", ToString(lines[0], node)); + EXPECT_EQ("456", ToString(lines[1], node)); + DCHECK_EQ(NGInlineItem::kCloseTag, items[lines[1].back().item_index].Type()); + EXPECT_EQ("789", ToString(lines[2], node)); +} + +TEST_F(NGLineBreakerTest, BoundaryInWord) { + LoadAhem(); + NGInlineNode* node = CreateInlineNode(R"HTML( + <!DOCTYPE html> + <style> + #container { + font: 10px/1 Ahem; + } + </style> + <div id=container><span>123 456</span>789 abc</div> + )HTML"); + + // The element boundary within "456789" should not cause a break. + // Since "789" does not fit, it should go to the next line along with "456". + Vector<NGInlineItemResults> lines; + lines = BreakLines(node, LayoutUnit(80)); + EXPECT_EQ(3u, lines.size()); + EXPECT_EQ("123", ToString(lines[0], node)); + EXPECT_EQ("456789", ToString(lines[1], node)); + EXPECT_EQ("abc", ToString(lines[2], node)); + + // Same as above, but this time "456789" overflows the line because it is + // 60px. + lines = BreakLines(node, LayoutUnit(50)); + EXPECT_EQ(3u, lines.size()); + EXPECT_EQ("123", ToString(lines[0], node)); + EXPECT_EQ("456789", ToString(lines[1], node)); + EXPECT_EQ("abc", ToString(lines[2], node)); +} + +TEST_F(NGLineBreakerTest, BoundaryInFirstWord) { + LoadAhem(); + NGInlineNode* node = CreateInlineNode(R"HTML( + <!DOCTYPE html> + <style> + #container { + font: 10px/1 Ahem; + } + </style> + <div id=container><span>123</span>456 789</div> + )HTML"); + + Vector<NGInlineItemResults> lines; + lines = BreakLines(node, LayoutUnit(80)); + EXPECT_EQ(2u, lines.size()); + EXPECT_EQ("123456", ToString(lines[0], node)); + EXPECT_EQ("789", ToString(lines[1], node)); + + lines = BreakLines(node, LayoutUnit(50)); + EXPECT_EQ(2u, lines.size()); + EXPECT_EQ("123456", ToString(lines[0], node)); + EXPECT_EQ("789", ToString(lines[1], node)); + + lines = BreakLines(node, LayoutUnit(20)); + EXPECT_EQ(2u, lines.size()); + EXPECT_EQ("123456", ToString(lines[0], node)); + EXPECT_EQ("789", ToString(lines[1], node)); +} + +} // namespace +} // namespace blink
diff --git a/third_party/WebKit/Source/core/paint/TextPainterBase.cpp b/third_party/WebKit/Source/core/paint/TextPainterBase.cpp index bdfbac04..9f11fca4 100644 --- a/third_party/WebKit/Source/core/paint/TextPainterBase.cpp +++ b/third_party/WebKit/Source/core/paint/TextPainterBase.cpp
@@ -199,9 +199,9 @@ switch (baseline_type) { case kAlphabeticBaseline: switch (style.GetTextUnderlinePosition()) { - case kTextUnderlinePositionAuto: + case TextUnderlinePosition::kAuto: return ResolvedUnderlinePosition::kRoman; - case kTextUnderlinePositionUnder: + case TextUnderlinePosition::kUnder: return ResolvedUnderlinePosition::kUnder; } break;
diff --git a/third_party/WebKit/Source/core/style/ComputedStyle.cpp b/third_party/WebKit/Source/core/style/ComputedStyle.cpp index 5a5a4a57..3bb53f5 100644 --- a/third_party/WebKit/Source/core/style/ComputedStyle.cpp +++ b/third_party/WebKit/Source/core/style/ComputedStyle.cpp
@@ -570,14 +570,14 @@ if (rare_non_inherited_data_.Get() != other.rare_non_inherited_data_.Get()) { if (rare_non_inherited_data_->appearance_ != other.rare_non_inherited_data_->appearance_ || - rare_non_inherited_data_->margin_before_collapse != - other.rare_non_inherited_data_->margin_before_collapse || - rare_non_inherited_data_->margin_after_collapse != - other.rare_non_inherited_data_->margin_after_collapse || - rare_non_inherited_data_->line_clamp != - other.rare_non_inherited_data_->line_clamp || - rare_non_inherited_data_->text_overflow != - other.rare_non_inherited_data_->text_overflow || + rare_non_inherited_data_->margin_before_collapse_ != + other.rare_non_inherited_data_->margin_before_collapse_ || + rare_non_inherited_data_->margin_after_collapse_ != + other.rare_non_inherited_data_->margin_after_collapse_ || + rare_non_inherited_data_->line_clamp_ != + other.rare_non_inherited_data_->line_clamp_ || + rare_non_inherited_data_->text_overflow_ != + other.rare_non_inherited_data_->text_overflow_ || rare_non_inherited_data_->shape_margin_ != other.rare_non_inherited_data_->shape_margin_ || rare_non_inherited_data_->order_ != @@ -745,8 +745,8 @@ return true; if (rare_non_inherited_data_.Get() != other.rare_non_inherited_data_.Get()) { - if (rare_non_inherited_data_->user_drag != - other.rare_non_inherited_data_->user_drag || + if (rare_non_inherited_data_->user_drag_ != + other.rare_non_inherited_data_->user_drag_ || rare_non_inherited_data_->object_fit_ != other.rare_non_inherited_data_->object_fit_ || rare_non_inherited_data_->object_position_ != @@ -852,8 +852,8 @@ } if (rare_non_inherited_data_.Get() != other.rare_non_inherited_data_.Get()) { - if (rare_non_inherited_data_->opacity != - other.rare_non_inherited_data_->opacity) + if (rare_non_inherited_data_->opacity_ != + other.rare_non_inherited_data_->opacity_) diff.SetOpacityChanged(); }
diff --git a/third_party/WebKit/Source/core/style/ComputedStyle.h b/third_party/WebKit/Source/core/style/ComputedStyle.h index aec4bd46..b93502c 100644 --- a/third_party/WebKit/Source/core/style/ComputedStyle.h +++ b/third_party/WebKit/Source/core/style/ComputedStyle.h
@@ -1034,10 +1034,10 @@ } EMarginCollapse MarginAfterCollapse() const { return static_cast<EMarginCollapse>( - rare_non_inherited_data_->margin_after_collapse); + rare_non_inherited_data_->margin_after_collapse_); } void SetMarginBeforeCollapse(EMarginCollapse c) { - SET_VAR(rare_non_inherited_data_, margin_before_collapse, c); + SET_VAR(rare_non_inherited_data_, margin_before_collapse_, c); } // -webkit-margin-after-collapse (aka -webkit-margin-bottom-collapse) @@ -1046,10 +1046,10 @@ } EMarginCollapse MarginBeforeCollapse() const { return static_cast<EMarginCollapse>( - rare_non_inherited_data_->margin_before_collapse); + rare_non_inherited_data_->margin_before_collapse_); } void SetMarginAfterCollapse(EMarginCollapse c) { - SET_VAR(rare_non_inherited_data_, margin_after_collapse, c); + SET_VAR(rare_non_inherited_data_, margin_after_collapse_, c); } // mix-blend-mode @@ -1137,10 +1137,10 @@ // opacity (aka -webkit-opacity) static float InitialOpacity() { return 1.0f; } - float Opacity() const { return rare_non_inherited_data_->opacity; } + float Opacity() const { return rare_non_inherited_data_->opacity_; } void SetOpacity(float f) { float v = clampTo<float>(f, 0, 1); - SET_VAR(rare_non_inherited_data_, opacity, v); + SET_VAR(rare_non_inherited_data_, opacity_, v); } // order (aka -webkit-order) @@ -1266,10 +1266,10 @@ } ETransformStyle3D TransformStyle3D() const { return static_cast<ETransformStyle3D>( - rare_non_inherited_data_->transform_style3d_); + rare_non_inherited_data_->transform_style_3d_); } void SetTransformStyle3D(ETransformStyle3D b) { - SET_VAR(rare_non_inherited_data_, transform_style3d_, b); + SET_VAR(rare_non_inherited_data_, transform_style_3d_, b); } // -webkit-transform-origin-x @@ -1475,14 +1475,15 @@ // text-underline-position static TextUnderlinePosition InitialTextUnderlinePosition() { - return kTextUnderlinePositionAuto; + return TextUnderlinePosition::kAuto; } TextUnderlinePosition GetTextUnderlinePosition() const { return static_cast<TextUnderlinePosition>( rare_inherited_data_->text_underline_position_); } void SetTextUnderlinePosition(TextUnderlinePosition v) { - SET_VAR(rare_inherited_data_, text_underline_position_, v); + SET_VAR(rare_inherited_data_, text_underline_position_, + static_cast<unsigned>(v)); } // text-decoration-skip @@ -1501,10 +1502,10 @@ // text-overflow static TextOverflow InitialTextOverflow() { return kTextOverflowClip; } TextOverflow GetTextOverflow() const { - return static_cast<TextOverflow>(rare_non_inherited_data_->text_overflow); + return static_cast<TextOverflow>(rare_non_inherited_data_->text_overflow_); } void SetTextOverflow(TextOverflow overflow) { - SET_VAR(rare_non_inherited_data_, text_overflow, overflow); + SET_VAR(rare_non_inherited_data_, text_overflow_, overflow); } // touch-action @@ -1705,45 +1706,16 @@ bool TextShadowDataEquivalent(const ComputedStyle&) const; - // -webkit-line-break - static LineBreak InitialLineBreak() { return LineBreak::kAuto; } - LineBreak GetLineBreak() const { - return static_cast<LineBreak>(rare_inherited_data_->line_break_); - } - void SetLineBreak(LineBreak b) { - SET_VAR(rare_inherited_data_, line_break_, static_cast<unsigned>(b)); - } - // Text emphasis properties. - static TextEmphasisFill InitialTextEmphasisFill() { - return TextEmphasisFill::kFilled; - } static TextEmphasisMark InitialTextEmphasisMark() { return TextEmphasisMark::kNone; } - static const AtomicString& InitialTextEmphasisCustomMark() { - return g_null_atom; - } - TextEmphasisFill GetTextEmphasisFill() const { - return static_cast<TextEmphasisFill>( - rare_inherited_data_->text_emphasis_fill_); - } TextEmphasisMark GetTextEmphasisMark() const; - const AtomicString& TextEmphasisCustomMark() const { - return rare_inherited_data_->text_emphasis_custom_mark_; - } - const AtomicString& TextEmphasisMarkString() const; - void SetTextEmphasisFill(TextEmphasisFill fill) { - SET_VAR(rare_inherited_data_, text_emphasis_fill_, - static_cast<unsigned>(fill)); - } void SetTextEmphasisMark(TextEmphasisMark mark) { SET_VAR(rare_inherited_data_, text_emphasis_mark_, static_cast<unsigned>(mark)); } - void SetTextEmphasisCustomMark(const AtomicString& mark) { - SET_VAR(rare_inherited_data_, text_emphasis_custom_mark_, mark); - } + const AtomicString& TextEmphasisMarkString() const; // -webkit-text-emphasis-color (aka -epub-text-emphasis-color) void SetTextEmphasisColor(const StyleColor& color) { @@ -1752,36 +1724,13 @@ color.IsCurrentColor()); } - // -webkit-text-emphasis-position - static TextEmphasisPosition InitialTextEmphasisPosition() { - return TextEmphasisPosition::kOver; - } - TextEmphasisPosition GetTextEmphasisPosition() const { - return static_cast<TextEmphasisPosition>( - rare_inherited_data_->text_emphasis_position_); - } - void SetTextEmphasisPosition(TextEmphasisPosition position) { - SET_VAR(rare_inherited_data_, text_emphasis_position_, - static_cast<unsigned>(position)); - } - // -webkit-line-clamp static LineClampValue InitialLineClamp() { return LineClampValue(); } const LineClampValue& LineClamp() const { - return rare_non_inherited_data_->line_clamp; + return rare_non_inherited_data_->line_clamp_; } void SetLineClamp(LineClampValue c) { - SET_VAR(rare_non_inherited_data_, line_clamp, c); - } - - // -webkit-ruby-position - static RubyPosition InitialRubyPosition() { return RubyPosition::kBefore; } - RubyPosition GetRubyPosition() const { - return static_cast<RubyPosition>(rare_inherited_data_->ruby_position_); - } - void SetRubyPosition(RubyPosition position) { - SET_VAR(rare_inherited_data_, ruby_position_, - static_cast<unsigned>(position)); + SET_VAR(rare_non_inherited_data_, line_clamp_, c); } // -webkit-text-fill-color @@ -1801,10 +1750,10 @@ // -webkit-user-drag static EUserDrag InitialUserDrag() { return DRAG_AUTO; } EUserDrag UserDrag() const { - return static_cast<EUserDrag>(rare_non_inherited_data_->user_drag); + return static_cast<EUserDrag>(rare_non_inherited_data_->user_drag_); } void SetUserDrag(EUserDrag d) { - SET_VAR(rare_non_inherited_data_, user_drag, d); + SET_VAR(rare_non_inherited_data_, user_drag_, d); } // -webkit-user-modify
diff --git a/third_party/WebKit/Source/core/style/ComputedStyleConstants.h b/third_party/WebKit/Source/core/style/ComputedStyleConstants.h index 42b84ced..45238ad 100644 --- a/third_party/WebKit/Source/core/style/ComputedStyleConstants.h +++ b/third_party/WebKit/Source/core/style/ComputedStyleConstants.h
@@ -194,8 +194,6 @@ kObjectFitScaleDown }; -enum class LineBreak { kAuto, kLoose, kNormal, kStrict, kAfterWhiteSpace }; - enum EResize { RESIZE_NONE, RESIZE_BOTH, RESIZE_HORIZONTAL, RESIZE_VERTICAL }; enum QuoteType { OPEN_QUOTE, CLOSE_QUOTE, NO_OPEN_QUOTE, NO_CLOSE_QUOTE }; @@ -248,10 +246,10 @@ return a = a | b; } -enum TextUnderlinePosition { +enum class TextUnderlinePosition { // FIXME: Implement support for 'under left' and 'under right' values. - kTextUnderlinePositionAuto, - kTextUnderlinePositionUnder + kAuto, + kUnder }; enum ETransformStyle3D { kTransformStyle3DFlat, kTransformStyle3DPreserve3D }; @@ -265,8 +263,6 @@ enum ELineClampType { kLineClampLineCount, kLineClampPercentage }; -enum class TextEmphasisFill { kFilled, kOpen }; - enum class TextEmphasisMark { kNone, kAuto, @@ -278,8 +274,6 @@ kCustom }; -enum class TextEmphasisPosition { kOver, kUnder }; - enum class TextOrientation { kMixed, kUpright, kSideways }; enum TextOverflow { kTextOverflowClip = 0, kTextOverflowEllipsis }; @@ -292,8 +286,6 @@ kPixelated }; -enum class RubyPosition { kBefore, kAfter }; - static const size_t kGridAutoFlowBits = 4; enum InternalGridAutoFlowAlgorithm { kInternalAutoFlowAlgorithmSparse = 0x1,
diff --git a/third_party/WebKit/Source/core/style/StyleRareNonInheritedData.cpp b/third_party/WebKit/Source/core/style/StyleRareNonInheritedData.cpp index 5a43faa..5ec1022 100644 --- a/third_party/WebKit/Source/core/style/StyleRareNonInheritedData.cpp +++ b/third_party/WebKit/Source/core/style/StyleRareNonInheritedData.cpp
@@ -71,13 +71,13 @@ "StyleRareNonInheritedData_should_stay_small"); StyleRareNonInheritedData::StyleRareNonInheritedData() - : opacity(ComputedStyle::InitialOpacity()), + : opacity_(ComputedStyle::InitialOpacity()), perspective_(ComputedStyle::InitialPerspective()), shape_image_threshold_(ComputedStyle::InitialShapeImageThreshold()), order_(ComputedStyle::InitialOrder()), perspective_origin_(ComputedStyle::InitialPerspectiveOrigin()), object_position_(ComputedStyle::InitialObjectPosition()), - line_clamp(ComputedStyle::InitialLineClamp()), + line_clamp_(ComputedStyle::InitialLineClamp()), draggable_region_mode_(static_cast<unsigned>(kDraggableRegionNone)), shape_outside_(ComputedStyle::InitialShapeOutside()), clip_path_(ComputedStyle::InitialClipPath()), @@ -101,12 +101,12 @@ justify_items_(ComputedStyle::InitialSelfAlignment()), justify_self_(ComputedStyle::InitialSelfAlignment()), page_size_type_(static_cast<unsigned>(PageSizeType::kAuto)), - transform_style3d_(ComputedStyle::InitialTransformStyle3D()), + transform_style_3d_(ComputedStyle::InitialTransformStyle3D()), backface_visibility_(ComputedStyle::InitialBackfaceVisibility()), - user_drag(ComputedStyle::InitialUserDrag()), - text_overflow(ComputedStyle::InitialTextOverflow()), - margin_before_collapse(kMarginCollapseCollapse), - margin_after_collapse(kMarginCollapseCollapse), + user_drag_(ComputedStyle::InitialUserDrag()), + text_overflow_(ComputedStyle::InitialTextOverflow()), + margin_before_collapse_(kMarginCollapseCollapse), + margin_after_collapse_(kMarginCollapseCollapse), appearance_(ComputedStyle::InitialAppearance()), text_decoration_style_(ComputedStyle::InitialTextDecorationStyle()), has_current_opacity_animation_(false), @@ -135,13 +135,13 @@ StyleRareNonInheritedData::StyleRareNonInheritedData( const StyleRareNonInheritedData& o) : RefCounted<StyleRareNonInheritedData>(), - opacity(o.opacity), + opacity_(o.opacity_), perspective_(o.perspective_), shape_image_threshold_(o.shape_image_threshold_), order_(o.order_), perspective_origin_(o.perspective_origin_), object_position_(o.object_position_), - line_clamp(o.line_clamp), + line_clamp_(o.line_clamp_), draggable_region_mode_(o.draggable_region_mode_), deprecated_flexible_box_(o.deprecated_flexible_box_), flexible_box_(o.flexible_box_), @@ -184,12 +184,12 @@ justify_items_(o.justify_items_), justify_self_(o.justify_self_), page_size_type_(o.page_size_type_), - transform_style3d_(o.transform_style3d_), + transform_style_3d_(o.transform_style_3d_), backface_visibility_(o.backface_visibility_), - user_drag(o.user_drag), - text_overflow(o.text_overflow), - margin_before_collapse(o.margin_before_collapse), - margin_after_collapse(o.margin_after_collapse), + user_drag_(o.user_drag_), + text_overflow_(o.text_overflow_), + margin_before_collapse_(o.margin_before_collapse_), + margin_after_collapse_(o.margin_after_collapse_), appearance_(o.appearance_), text_decoration_style_(o.text_decoration_style_), has_current_opacity_animation_(o.has_current_opacity_animation_), @@ -225,10 +225,11 @@ bool StyleRareNonInheritedData::operator==( const StyleRareNonInheritedData& o) const { - return opacity == o.opacity && perspective_ == o.perspective_ && + return opacity_ == o.opacity_ && perspective_ == o.perspective_ && shape_image_threshold_ == o.shape_image_threshold_ && order_ == o.order_ && perspective_origin_ == o.perspective_origin_ && - object_position_ == o.object_position_ && line_clamp == o.line_clamp && + object_position_ == o.object_position_ && + line_clamp_ == o.line_clamp_ && draggable_region_mode_ == o.draggable_region_mode_ && deprecated_flexible_box_ == o.deprecated_flexible_box_ && flexible_box_ == o.flexible_box_ && multi_col_ == o.multi_col_ && @@ -265,11 +266,11 @@ justify_items_ == o.justify_items_ && justify_self_ == o.justify_self_ && page_size_type_ == o.page_size_type_ && - transform_style3d_ == o.transform_style3d_ && + transform_style_3d_ == o.transform_style_3d_ && backface_visibility_ == o.backface_visibility_ && - user_drag == o.user_drag && text_overflow == o.text_overflow && - margin_before_collapse == o.margin_before_collapse && - margin_after_collapse == o.margin_after_collapse && + user_drag_ == o.user_drag_ && text_overflow_ == o.text_overflow_ && + margin_before_collapse_ == o.margin_before_collapse_ && + margin_after_collapse_ == o.margin_after_collapse_ && appearance_ == o.appearance_ && text_decoration_style_ == o.text_decoration_style_ && has_current_opacity_animation_ == o.has_current_opacity_animation_ &&
diff --git a/third_party/WebKit/Source/core/style/StyleRareNonInheritedData.h b/third_party/WebKit/Source/core/style/StyleRareNonInheritedData.h index cef14d2..912ff09 100644 --- a/third_party/WebKit/Source/core/style/StyleRareNonInheritedData.h +++ b/third_party/WebKit/Source/core/style/StyleRareNonInheritedData.h
@@ -86,7 +86,7 @@ return !(*this == o); } - float opacity; // Whether or not we're transparent. + float opacity_; // Whether or not we're transparent. float perspective_; float shape_image_threshold_; @@ -96,7 +96,7 @@ LengthPoint perspective_origin_; LengthPoint object_position_; - LineClampValue line_clamp; // An Apple extension. + LineClampValue line_clamp_; // An Apple extension. unsigned draggable_region_mode_ : 2; // DraggableRegionMode DataRef<StyleDeprecatedFlexibleBoxData> @@ -159,14 +159,14 @@ StyleSelfAlignmentData justify_self_; unsigned page_size_type_ : 2; // PageSizeType - unsigned transform_style3d_ : 1; // ETransformStyle3D + unsigned transform_style_3d_ : 1; // ETransformStyle3D unsigned backface_visibility_ : 1; // EBackfaceVisibility - unsigned user_drag : 2; // EUserDrag - unsigned text_overflow : 1; // Whether or not lines that spill out should be - // truncated with "..." - unsigned margin_before_collapse : 2; // EMarginCollapse - unsigned margin_after_collapse : 2; // EMarginCollapse + unsigned user_drag_ : 2; // EUserDrag + unsigned text_overflow_ : 1; // Whether or not lines that spill out should be + // truncated with "..." + unsigned margin_before_collapse_ : 2; // EMarginCollapse + unsigned margin_after_collapse_ : 2; // EMarginCollapse unsigned appearance_ : 6; // EAppearance unsigned text_decoration_style_ : 3; // TextDecorationStyle
diff --git a/third_party/WebKit/Source/core/workers/MainThreadWorklet.cpp b/third_party/WebKit/Source/core/workers/MainThreadWorklet.cpp index 3f5a977..4ff8973 100644 --- a/third_party/WebKit/Source/core/workers/MainThreadWorklet.cpp +++ b/third_party/WebKit/Source/core/workers/MainThreadWorklet.cpp
@@ -13,26 +13,9 @@ #include "core/workers/WorkletPendingTasks.h" #include "platform/WebTaskRunner.h" #include "platform/wtf/WTF.h" -#include "public/platform/WebURLRequest.h" namespace blink { -namespace { - -WebURLRequest::FetchCredentialsMode ParseCredentialsOption( - const String& credentials_option) { - if (credentials_option == "omit") - return WebURLRequest::kFetchCredentialsModeOmit; - if (credentials_option == "same-origin") - return WebURLRequest::kFetchCredentialsModeSameOrigin; - if (credentials_option == "include") - return WebURLRequest::kFetchCredentialsModeInclude; - NOTREACHED(); - return WebURLRequest::kFetchCredentialsModeOmit; -} - -} // namespace - MainThreadWorklet::MainThreadWorklet(LocalFrame* frame) : Worklet(frame) {} WorkletGlobalScopeProxy* MainThreadWorklet::FindAvailableGlobalScope() const {
diff --git a/third_party/WebKit/Source/core/workers/ThreadedWorklet.cpp b/third_party/WebKit/Source/core/workers/ThreadedWorklet.cpp index 06b37398..7a873ded 100644 --- a/third_party/WebKit/Source/core/workers/ThreadedWorklet.cpp +++ b/third_party/WebKit/Source/core/workers/ThreadedWorklet.cpp
@@ -9,16 +9,25 @@ #include "core/dom/DOMException.h" #include "core/dom/Document.h" #include "core/dom/ExceptionCode.h" +#include "core/dom/TaskRunnerHelper.h" #include "core/frame/LocalFrame.h" #include "core/workers/WorkletGlobalScopeProxy.h" +#include "core/workers/WorkletPendingTasks.h" +#include "platform/WebTaskRunner.h" #include "platform/wtf/WTF.h" namespace blink { ThreadedWorklet::ThreadedWorklet(LocalFrame* frame) : Worklet(frame) {} +// Implementation of the second half of the "addModule(moduleURL, options)" +// algorithm: +// https://drafts.css-houdini.org/worklets/#dom-worklet-addmodule +// TODO(nhiroki): MainThreadWorklet::FetchAndInvokeScript will be moved to the +// parent class (i.e., Worklet) and this function will be replaced with it. +// (https://crbug.com/727194) void ThreadedWorklet::FetchAndInvokeScript(const KURL& module_url_record, - const WorkletOptions&, + const WorkletOptions& options, ScriptPromiseResolver* resolver) { DCHECK(IsMainThread()); if (!GetExecutionContext()) @@ -27,39 +36,60 @@ if (!IsInitialized()) Initialize(); - WorkletScriptLoader* script_loader = WorkletScriptLoader::Create( - ToDocument(GetExecutionContext())->Fetcher(), this); - loader_to_resolver_map_.Set(script_loader, resolver); - script_loader->FetchScript(module_url_record); -} + // Step 6: "Let credentialOptions be the credentials member of options." + // TODO(nhiroki): Add tests for credentialOptions (https://crbug.com/710837). + WebURLRequest::FetchCredentialsMode credentials_mode = + ParseCredentialsOption(options.credentials()); -void ThreadedWorklet::NotifyWorkletScriptLoadingFinished( - WorkletScriptLoader* script_loader, - const ScriptSourceCode& source_code) { - DCHECK(IsMainThread()); - ScriptPromiseResolver* resolver = loader_to_resolver_map_.at(script_loader); - loader_to_resolver_map_.erase(script_loader); + // Step 7: "Let outsideSettings be the relevant settings object of this." + // In the specification, outsideSettings is used for posting a task to the + // document's responsible event loop. In our implementation, we use the + // document's UnspecedLoading task runner as that is what we commonly use for + // module loading. + RefPtr<WebTaskRunner> outside_settings_task_runner = + TaskRunnerHelper::Get(TaskType::kUnspecedLoading, GetExecutionContext()); - if (!script_loader->WasScriptLoadSuccessful()) { - resolver->Reject(DOMException::Create(kNetworkError)); - return; - } + // Step 8: "Let moduleResponsesMap be worklet's module responses map." + // TODO(nhiroki): Implement moduleResponsesMap (https://crbug.com/627945). - GetWorkletGlobalScopeProxy()->EvaluateScript(source_code); - resolver->Resolve(); + // Step 9: "Let workletGlobalScopeType be worklet's worklet global scope + // type." + // workletGlobalScopeType is encoded into the class name (e.g., PaintWorklet). + + // Step 10: "If the worklet's WorkletGlobalScopes is empty, run the following + // steps:" + // 10.1: "Create a WorkletGlobalScope given workletGlobalScopeType, + // moduleResponsesMap, and outsideSettings." + // 10.2: "Add the WorkletGlobalScope to worklet's WorkletGlobalScopes." + // "Depending on the type of worklet the user agent may create additional + // WorkletGlobalScopes at this time." + // TODO(nhiroki): These steps will be supported after MainThreadWorklet and + // ThreadedWorklet are merged into Worklet (https://crbug.com/727194) + + // Step 11: "Let pendingTaskStruct be a new pending tasks struct with counter + // initialized to the length of worklet's WorkletGlobalScopes." + const int kNumberOfGlobalScopes = 1; + WorkletPendingTasks* pending_tasks = + new WorkletPendingTasks(kNumberOfGlobalScopes, resolver); + + // Step 12: "For each workletGlobalScope in the worklet's + // WorkletGlobalScopes, queue a task on the workletGlobalScope to fetch and + // invoke a worklet script given workletGlobalScope, moduleURLRecord, + // moduleResponsesMap, credentialOptions, outsideSettings, pendingTaskStruct, + // and promise." + // TODO(nhiroki): Queue a task instead of executing this here. + GetWorkletGlobalScopeProxy()->FetchAndInvokeScript( + module_url_record, credentials_mode, + std::move(outside_settings_task_runner), pending_tasks); } void ThreadedWorklet::ContextDestroyed(ExecutionContext* execution_context) { DCHECK(IsMainThread()); - for (const auto& script_loader : loader_to_resolver_map_.Keys()) - script_loader->Cancel(); - loader_to_resolver_map_.clear(); if (IsInitialized()) GetWorkletGlobalScopeProxy()->TerminateWorkletGlobalScope(); } DEFINE_TRACE(ThreadedWorklet) { - visitor->Trace(loader_to_resolver_map_); Worklet::Trace(visitor); }
diff --git a/third_party/WebKit/Source/core/workers/ThreadedWorklet.h b/third_party/WebKit/Source/core/workers/ThreadedWorklet.h index e7a2921..297d997 100644 --- a/third_party/WebKit/Source/core/workers/ThreadedWorklet.h +++ b/third_party/WebKit/Source/core/workers/ThreadedWorklet.h
@@ -9,7 +9,6 @@ #include "bindings/core/v8/ScriptPromiseResolver.h" #include "core/CoreExport.h" -#include "core/loader/WorkletScriptLoader.h" #include "platform/heap/Handle.h" namespace blink { @@ -22,18 +21,13 @@ // threaded worklets while module loading is being implemented for main thread // worklets. This and MainThreadWorklet will be merged into the base Worklet // class once threaded worklets are also ready to use module loading. -class CORE_EXPORT ThreadedWorklet : public Worklet, - public WorkletScriptLoader::Client { +class CORE_EXPORT ThreadedWorklet : public Worklet { USING_GARBAGE_COLLECTED_MIXIN(ThreadedWorklet); WTF_MAKE_NONCOPYABLE(ThreadedWorklet); public: virtual ~ThreadedWorklet() = default; - // WorkletScriptLoader::Client - void NotifyWorkletScriptLoadingFinished(WorkletScriptLoader*, - const ScriptSourceCode&) final; - // ContextLifecycleObserver void ContextDestroyed(ExecutionContext*) final; @@ -54,9 +48,6 @@ // Called when addModule() is called for the first time. virtual void Initialize() = 0; virtual bool IsInitialized() const = 0; - - HeapHashMap<Member<WorkletScriptLoader>, Member<ScriptPromiseResolver>> - loader_to_resolver_map_; }; } // namespace blink
diff --git a/third_party/WebKit/Source/core/workers/ThreadedWorkletMessagingProxy.cpp b/third_party/WebKit/Source/core/workers/ThreadedWorkletMessagingProxy.cpp index cf38c7b..83d35477 100644 --- a/third_party/WebKit/Source/core/workers/ThreadedWorkletMessagingProxy.cpp +++ b/third_party/WebKit/Source/core/workers/ThreadedWorkletMessagingProxy.cpp
@@ -19,6 +19,61 @@ namespace blink { +// TODO(nhiroki): This should be in sync with WorkletModuleTreeClient as much as +// possible because this will be replaced with that when module loading is ready +// for threaded worklets (https://crbug.com/727194). +class ThreadedWorkletMessagingProxy::LoaderClient final + : public GarbageCollectedFinalized< + ThreadedWorkletMessagingProxy::LoaderClient>, + public WorkletScriptLoader::Client { + USING_GARBAGE_COLLECTED_MIXIN(LoaderClient); + + public: + LoaderClient(RefPtr<WebTaskRunner> outside_settings_task_runner, + WorkletPendingTasks* pending_tasks, + ThreadedWorkletMessagingProxy* proxy) + : outside_settings_task_runner_(std::move(outside_settings_task_runner)), + pending_tasks_(pending_tasks), + proxy_(proxy) {} + + // Implementation of the second half of the "fetch and invoke a worklet + // script" algorithm: + // https://drafts.css-houdini.org/worklets/#fetch-and-invoke-a-worklet-script + void NotifyWorkletScriptLoadingFinished( + WorkletScriptLoader* loader, + const ScriptSourceCode& source_code) final { + DCHECK(IsMainThread()); + proxy_->NotifyLoadingFinished(loader); + + if (!loader->WasScriptLoadSuccessful()) { + // Step 3: "If script is null, then queue a task on outsideSettings's + // responsible event loop to run these steps:" + // The steps are implemented in WorkletPendingTasks::Abort(). + outside_settings_task_runner_->PostTask( + BLINK_FROM_HERE, WTF::Bind(&WorkletPendingTasks::Abort, + WrapPersistent(pending_tasks_.Get()))); + return; + } + + // Step 4: "Run a module script given script." + proxy_->EvaluateScript(source_code); + + // Step 5: "Queue a task on outsideSettings's responsible event loop to run + // these steps:" + // The steps are implemented in WorkletPendingTasks::DecrementCounter(). + outside_settings_task_runner_->PostTask( + BLINK_FROM_HERE, WTF::Bind(&WorkletPendingTasks::DecrementCounter, + WrapPersistent(pending_tasks_.Get()))); + } + + DEFINE_INLINE_TRACE() { visitor->Trace(pending_tasks_); } + + private: + RefPtr<WebTaskRunner> outside_settings_task_runner_; + Member<WorkletPendingTasks> pending_tasks_; + ThreadedWorkletMessagingProxy* proxy_; +}; + ThreadedWorkletMessagingProxy::ThreadedWorkletMessagingProxy( ExecutionContext* execution_context) : ThreadedMessagingProxyBase(execution_context), weak_ptr_factory_(this) { @@ -27,7 +82,7 @@ } void ThreadedWorkletMessagingProxy::Initialize() { - DCHECK(IsParentContextThread()); + DCHECK(IsMainThread()); if (AskedToTerminate()) return; @@ -57,8 +112,36 @@ script_url); } +void ThreadedWorkletMessagingProxy::FetchAndInvokeScript( + const KURL& module_url_record, + WebURLRequest::FetchCredentialsMode credentials_mode, + RefPtr<WebTaskRunner> outside_settings_task_runner, + WorkletPendingTasks* pending_tasks) { + DCHECK(IsMainThread()); + LoaderClient* client = new LoaderClient( + std::move(outside_settings_task_runner), pending_tasks, this); + WorkletScriptLoader* loader = WorkletScriptLoader::Create( + ToDocument(GetExecutionContext())->Fetcher(), client); + loaders_.insert(loader); + loader->FetchScript(module_url_record); +} + +void ThreadedWorkletMessagingProxy::TerminateWorkletGlobalScope() { + DCHECK(IsMainThread()); + for (const auto& loader : loaders_) + loader->Cancel(); + loaders_.clear(); + TerminateGlobalScope(); +} + +void ThreadedWorkletMessagingProxy::NotifyLoadingFinished( + WorkletScriptLoader* loader) { + loaders_.erase(loader); +} + void ThreadedWorkletMessagingProxy::EvaluateScript( const ScriptSourceCode& script_source_code) { + DCHECK(IsMainThread()); TaskRunnerHelper::Get(TaskType::kMiscPlatformAPI, GetWorkerThread()) ->PostTask( BLINK_FROM_HERE, @@ -68,8 +151,4 @@ CrossThreadUnretained(GetWorkerThread()))); } -void ThreadedWorkletMessagingProxy::TerminateWorkletGlobalScope() { - TerminateGlobalScope(); -} - } // namespace blink
diff --git a/third_party/WebKit/Source/core/workers/ThreadedWorkletMessagingProxy.h b/third_party/WebKit/Source/core/workers/ThreadedWorkletMessagingProxy.h index 3e288c7..a096d09 100644 --- a/third_party/WebKit/Source/core/workers/ThreadedWorkletMessagingProxy.h +++ b/third_party/WebKit/Source/core/workers/ThreadedWorkletMessagingProxy.h
@@ -6,12 +6,15 @@ #define ThreadedWorkletMessagingProxy_h #include "core/CoreExport.h" +#include "core/loader/WorkletScriptLoader.h" #include "core/workers/ThreadedMessagingProxyBase.h" #include "core/workers/WorkletGlobalScopeProxy.h" +#include "core/workers/WorkletPendingTasks.h" #include "platform/wtf/WeakPtr.h" namespace blink { +class ScriptSourceCode; class ThreadedWorkletObjectProxy; class CORE_EXPORT ThreadedWorkletMessagingProxy @@ -19,7 +22,10 @@ public WorkletGlobalScopeProxy { public: // WorkletGlobalScopeProxy implementation. - void EvaluateScript(const ScriptSourceCode&) final; + void FetchAndInvokeScript(const KURL& module_url_record, + WebURLRequest::FetchCredentialsMode, + RefPtr<WebTaskRunner> outside_settings_task_runner, + WorkletPendingTasks*) final; void TerminateWorkletGlobalScope() final; void Initialize(); @@ -33,9 +39,15 @@ private: friend class ThreadedWorkletMessagingProxyForTest; + class LoaderClient; + + void NotifyLoadingFinished(WorkletScriptLoader*); + void EvaluateScript(const ScriptSourceCode&); std::unique_ptr<ThreadedWorkletObjectProxy> worklet_object_proxy_; + HashSet<Persistent<WorkletScriptLoader>> loaders_; + WeakPtrFactory<ThreadedWorkletMessagingProxy> weak_ptr_factory_; };
diff --git a/third_party/WebKit/Source/core/workers/Worklet.cpp b/third_party/WebKit/Source/core/workers/Worklet.cpp index 6620d822..deb56b048 100644 --- a/third_party/WebKit/Source/core/workers/Worklet.cpp +++ b/third_party/WebKit/Source/core/workers/Worklet.cpp
@@ -62,6 +62,18 @@ return promise; } +WebURLRequest::FetchCredentialsMode Worklet::ParseCredentialsOption( + const String& credentials_option) { + if (credentials_option == "omit") + return WebURLRequest::kFetchCredentialsModeOmit; + if (credentials_option == "same-origin") + return WebURLRequest::kFetchCredentialsModeSameOrigin; + if (credentials_option == "include") + return WebURLRequest::kFetchCredentialsModeInclude; + NOTREACHED(); + return WebURLRequest::kFetchCredentialsModeOmit; +} + DEFINE_TRACE(Worklet) { ContextLifecycleObserver::Trace(visitor); }
diff --git a/third_party/WebKit/Source/core/workers/Worklet.h b/third_party/WebKit/Source/core/workers/Worklet.h index 7c2c2f2..690d9aa 100644 --- a/third_party/WebKit/Source/core/workers/Worklet.h +++ b/third_party/WebKit/Source/core/workers/Worklet.h
@@ -11,6 +11,7 @@ #include "core/workers/WorkletOptions.h" #include "platform/bindings/ScriptWrappable.h" #include "platform/heap/Handle.h" +#include "public/platform/WebURLRequest.h" namespace blink { @@ -43,6 +44,9 @@ // The Worklet inherits the url and userAgent from the frame->document(). explicit Worklet(LocalFrame*); + static WebURLRequest::FetchCredentialsMode ParseCredentialsOption( + const String& credentials_option); + private: virtual void FetchAndInvokeScript(const KURL& module_url_record, const WorkletOptions&,
diff --git a/third_party/WebKit/Source/core/workers/WorkletGlobalScopeProxy.h b/third_party/WebKit/Source/core/workers/WorkletGlobalScopeProxy.h index 5fd7dec77..c26f9bf 100644 --- a/third_party/WebKit/Source/core/workers/WorkletGlobalScopeProxy.h +++ b/third_party/WebKit/Source/core/workers/WorkletGlobalScopeProxy.h
@@ -12,7 +12,6 @@ namespace blink { -class ScriptSourceCode; class WorkletPendingTasks; // Abstracts communication from (Main/Threaded)Worklet on the main thread to @@ -30,10 +29,6 @@ RefPtr<WebTaskRunner> outside_settings_task_runner, WorkletPendingTasks*) {} - // Evaluates the given script source code. This should be called only for - // threaded worklets that still use classic script loading. - virtual void EvaluateScript(const ScriptSourceCode&) = 0; - // Terminates the worklet global scope from the main thread. virtual void TerminateWorkletGlobalScope() = 0; };
diff --git a/third_party/WebKit/Source/devtools/front_end/ui/InspectorView.js b/third_party/WebKit/Source/devtools/front_end/ui/InspectorView.js index fc8cf5f..9f0a6ad 100644 --- a/third_party/WebKit/Source/devtools/front_end/ui/InspectorView.js +++ b/third_party/WebKit/Source/devtools/front_end/ui/InspectorView.js
@@ -46,7 +46,7 @@ // Create drawer tabbed pane. this._drawerTabbedLocation = - UI.viewManager.createTabbedLocation(this._showDrawer.bind(this, false), 'drawer-view', true); + UI.viewManager.createTabbedLocation(this._showDrawer.bind(this, false), 'drawer-view', true, true); this._drawerTabbedLocation.enableMoreTabsButton(); this._drawerTabbedPane = this._drawerTabbedLocation.tabbedPane(); this._drawerTabbedPane.setMinimumSize(0, 27);
diff --git a/third_party/WebKit/Source/devtools/front_end/ui/TabbedPane.js b/third_party/WebKit/Source/devtools/front_end/ui/TabbedPane.js index 2004274f..f237768 100644 --- a/third_party/WebKit/Source/devtools/front_end/ui/TabbedPane.js +++ b/third_party/WebKit/Source/devtools/front_end/ui/TabbedPane.js
@@ -816,7 +816,7 @@ * @param {number} index */ _insertBefore(tab, index) { - this._tabsElement.insertBefore(tab._tabElement || null, this._tabsElement.childNodes[index]); + this._tabsElement.insertBefore(tab.tabElement, this._tabsElement.childNodes[index]); var oldIndex = this._tabs.indexOf(tab); this._tabs.splice(oldIndex, 1); if (oldIndex < index) @@ -1081,7 +1081,6 @@ tabElement.id = 'tab-' + this._id; UI.ARIAUtils.markAsTab(tabElement); UI.ARIAUtils.setSelected(tabElement, false); - tabElement.selectTabForTest = this._tabbedPane.selectTab.bind(this._tabbedPane, this.id, true); var titleElement = tabElement.createChild('span', 'tabbed-pane-header-tab-title'); titleElement.textContent = this.title;
diff --git a/third_party/WebKit/Source/devtools/front_end/ui/TextPrompt.js b/third_party/WebKit/Source/devtools/front_end/ui/TextPrompt.js index 678d1e9b..fc8404cb 100644 --- a/third_party/WebKit/Source/devtools/front_end/ui/TextPrompt.js +++ b/third_party/WebKit/Source/devtools/front_end/ui/TextPrompt.js
@@ -45,6 +45,7 @@ this._currentSuggestion = ''; this._completionRequestId = 0; this._ghostTextElement = createElementWithClass('span', 'auto-complete-text'); + this._ghostTextElement.setAttribute('contenteditable', 'false'); } /**
diff --git a/third_party/WebKit/Source/devtools/front_end/ui/View.js b/third_party/WebKit/Source/devtools/front_end/ui/View.js index 7343e1dc..3a7ce3ef 100644 --- a/third_party/WebKit/Source/devtools/front_end/ui/View.js +++ b/third_party/WebKit/Source/devtools/front_end/ui/View.js
@@ -712,6 +712,16 @@ } } this._appendTab(view, index); + + if (view.isCloseable()) { + var tabs = this._closeableTabSetting.get(); + var tabId = view.viewId(); + if (!tabs[tabId]) { + tabs[tabId] = true; + this._closeableTabSetting.set(tabs); + } + } + this._persistTabOrder(); } /** @@ -750,17 +760,6 @@ var tabId = /** @type {string} */ (event.data.tabId); if (this._lastSelectedTabSetting && event.data['isUserGesture']) this._lastSelectedTabSetting.set(tabId); - var view = this._views.get(tabId); - if (!view) - return; - - if (view.isCloseable()) { - var tabs = this._closeableTabSetting.get(); - if (!tabs[tabId]) { - tabs[tabId] = true; - this._closeableTabSetting.set(tabs); - } - } } /** @@ -775,10 +774,7 @@ } } - /** - * @param {!Common.Event} event - */ - _persistTabOrder(event) { + _persistTabOrder() { var tabIds = this._tabbedPane.tabIds(); var tabOrders = {}; for (var i = 0; i < tabIds.length; i++)
diff --git a/third_party/WebKit/Source/modules/csspaint/PaintWorkletGlobalScopeProxy.cpp b/third_party/WebKit/Source/modules/csspaint/PaintWorkletGlobalScopeProxy.cpp index 5a047fb..aaa6fe7 100644 --- a/third_party/WebKit/Source/modules/csspaint/PaintWorkletGlobalScopeProxy.cpp +++ b/third_party/WebKit/Source/modules/csspaint/PaintWorkletGlobalScopeProxy.cpp
@@ -40,13 +40,6 @@ pending_tasks); } -void PaintWorkletGlobalScopeProxy::EvaluateScript( - const ScriptSourceCode& script_source_code) { - // This should be called only for threaded worklets that still use classic - // script loading. - NOTREACHED(); -} - void PaintWorkletGlobalScopeProxy::TerminateWorkletGlobalScope() { DCHECK(IsMainThread()); global_scope_->Terminate();
diff --git a/third_party/WebKit/Source/modules/csspaint/PaintWorkletGlobalScopeProxy.h b/third_party/WebKit/Source/modules/csspaint/PaintWorkletGlobalScopeProxy.h index f79293f..e6e81ad 100644 --- a/third_party/WebKit/Source/modules/csspaint/PaintWorkletGlobalScopeProxy.h +++ b/third_party/WebKit/Source/modules/csspaint/PaintWorkletGlobalScopeProxy.h
@@ -30,7 +30,6 @@ WebURLRequest::FetchCredentialsMode, RefPtr<WebTaskRunner> outside_settings_task_runner, WorkletPendingTasks*) override; - void EvaluateScript(const ScriptSourceCode&) override; void TerminateWorkletGlobalScope() override; CSSPaintDefinition* FindDefinition(const String& name);
diff --git a/third_party/WebKit/Source/modules/imagecapture/ImageCapture.cpp b/third_party/WebKit/Source/modules/imagecapture/ImageCapture.cpp index 13644c5..95506429 100644 --- a/third_party/WebKit/Source/modules/imagecapture/ImageCapture.cpp +++ b/third_party/WebKit/Source/modules/imagecapture/ImageCapture.cpp
@@ -128,7 +128,7 @@ resolver->Reject(DOMException::Create(kNotFoundError, kNoServiceError)); return promise; } - service_requests_.insert(resolver); + service_requests_.insert(resolver, HeapVector<MediaTrackConstraintSet>()); // m_streamTrack->component()->source()->id() is the renderer "name" of the // camera; @@ -158,7 +158,7 @@ resolver->Reject(DOMException::Create(kNotFoundError, kNoServiceError)); return promise; } - service_requests_.insert(resolver); + service_requests_.insert(resolver, HeapVector<MediaTrackConstraintSet>()); // TODO(mcasas): should be using a mojo::StructTraits instead. auto settings = media::mojom::blink::PhotoSettings::New(); @@ -233,7 +233,7 @@ return promise; } - service_requests_.insert(resolver); + service_requests_.insert(resolver, HeapVector<MediaTrackConstraintSet>()); // m_streamTrack->component()->source()->id() is the renderer "name" of the // camera; @@ -294,10 +294,8 @@ resolver->Reject(DOMException::Create(kNotFoundError, kNoServiceError)); return; } - service_requests_.insert(resolver); - // TODO(mcasas): add support more than one single advanced constraint. - const auto constraints = constraints_vector[0]; + auto constraints = constraints_vector[0]; if ((constraints.hasWhiteBalanceMode() && !capabilities_.hasWhiteBalanceMode()) || @@ -501,6 +499,8 @@ current_constraints_ = temp_constraints; + service_requests_.insert(resolver, constraints_vector); + service_->SetOptions( stream_track_->Component()->Source()->Id(), std::move(settings), ConvertToBaseCallback( @@ -637,7 +637,19 @@ return; } - resolver->Resolve(photo_capabilities_); + // If this is a response to a SetMediaTrackConstraints() request, it will have + // the original constraints to apply: Resolve() with those; Resolve() with the + // |photo_capabilities_| otherwise. + const HeapVector<MediaTrackConstraintSet>& originalConstraints = + service_requests_.at(resolver); + if (originalConstraints.IsEmpty()) { + resolver->Resolve(photo_capabilities_); + } else { + MediaTrackConstraints constraints; + constraints.setAdvanced(originalConstraints); + resolver->Resolve(constraints); + } + service_requests_.erase(resolver); } @@ -778,8 +790,8 @@ void ImageCapture::OnServiceConnectionError() { service_.reset(); - for (ScriptPromiseResolver* resolver : service_requests_) - resolver->Reject(DOMException::Create(kNotFoundError, kNoServiceError)); + for (const auto& resolver : service_requests_) + resolver.key->Reject(DOMException::Create(kNotFoundError, kNoServiceError)); service_requests_.clear(); }
diff --git a/third_party/WebKit/Source/modules/imagecapture/ImageCapture.h b/third_party/WebKit/Source/modules/imagecapture/ImageCapture.h index 7f0056b..0e81c2c 100644 --- a/third_party/WebKit/Source/modules/imagecapture/ImageCapture.h +++ b/third_party/WebKit/Source/modules/imagecapture/ImageCapture.h
@@ -102,7 +102,9 @@ Member<PhotoCapabilities> photo_capabilities_; - HeapHashSet<Member<ScriptPromiseResolver>> service_requests_; + HeapHashMap<Member<ScriptPromiseResolver>, + HeapVector<MediaTrackConstraintSet>> + service_requests_; }; } // namespace blink
diff --git a/third_party/WebKit/Source/modules/mediastream/MediaStreamTrack.idl b/third_party/WebKit/Source/modules/mediastream/MediaStreamTrack.idl index 950f06b2..e5e701a0 100644 --- a/third_party/WebKit/Source/modules/mediastream/MediaStreamTrack.idl +++ b/third_party/WebKit/Source/modules/mediastream/MediaStreamTrack.idl
@@ -53,5 +53,8 @@ [RuntimeEnabled=MediaTrackCapabilities] MediaTrackCapabilities getCapabilities(); MediaTrackConstraints getConstraints(); MediaTrackSettings getSettings(); - [RuntimeEnabled=MediaTrackApplyConstraints, CallWith=ScriptState] Promise<void> applyConstraints(optional MediaTrackConstraints constraints); + // Spec resolves applyConstraints() with void, here we resolve with the + // applied MediaTrackConstraints instead: + // https://github.com/w3c/mediacapture-main/issues/462 + [RuntimeEnabled=MediaTrackApplyConstraints, CallWith=ScriptState] Promise<MediaTrackConstraints> applyConstraints(optional MediaTrackConstraints constraints); };
diff --git a/third_party/WebKit/Source/modules/webaudio/BiquadDSPKernel.cpp b/third_party/WebKit/Source/modules/webaudio/BiquadDSPKernel.cpp index 1b2c73d..85fda7f 100644 --- a/third_party/WebKit/Source/modules/webaudio/BiquadDSPKernel.cpp +++ b/third_party/WebKit/Source/modules/webaudio/BiquadDSPKernel.cpp
@@ -31,6 +31,13 @@ namespace blink { +// FIXME: As a recursive linear filter, depending on its parameters, a biquad +// filter can have an infinite tailTime. In practice, Biquad filters do not +// usually (except for very high resonance values) have a tailTime of longer +// than approx. 200ms. This value could possibly be calculated based on the +// settings of the Biquad. +static const double kMaxBiquadDelayTime = 0.2; + void BiquadDSPKernel::UpdateCoefficientsIfNecessary(int frames_to_process) { if (GetBiquadProcessor()->FilterCoefficientsDirty()) { float cutoff_frequency[AudioUtilities::kRenderQuantumFrames]; @@ -114,23 +121,6 @@ break; } } - - UpdateTailTime(number_of_frames - 1); -} - -void BiquadDSPKernel::UpdateTailTime(int coef_index) { - // A reasonable upper limit for the tail time. While it's easy to - // create biquad filters whose tail time can be much larger than - // this, limit the maximum to this value so that we don't keep such - // nodes alive "forever". - // TODO: What is a reasonable upper limit? - const double kMaxTailTime = 30; - - double sample_rate = SampleRate(); - double tail = - biquad_.TailFrame(coef_index, kMaxTailTime * sample_rate) / sample_rate; - - tail_time_ = clampTo(tail, 0.0, kMaxTailTime); } void BiquadDSPKernel::Process(const float* source, @@ -209,7 +199,7 @@ } double BiquadDSPKernel::TailTime() const { - return tail_time_; + return kMaxBiquadDelayTime; } double BiquadDSPKernel::LatencyTime() const {
diff --git a/third_party/WebKit/Source/modules/webaudio/BiquadDSPKernel.h b/third_party/WebKit/Source/modules/webaudio/BiquadDSPKernel.h index 99069ef..efeb3eb0 100644 --- a/third_party/WebKit/Source/modules/webaudio/BiquadDSPKernel.h +++ b/third_party/WebKit/Source/modules/webaudio/BiquadDSPKernel.h
@@ -75,15 +75,8 @@ const float* detune); private: - // Compute the tail time using the BiquadFilter coefficients at - // index |coef_index|. - void UpdateTailTime(int coef_index); - // Synchronize process() with getting and setting the filter coefficients. mutable Mutex process_lock_; - - // The current tail time for biquad filter. - double tail_time_; }; } // namespace blink
diff --git a/third_party/WebKit/Source/platform/audio/Biquad.cpp b/third_party/WebKit/Source/platform/audio/Biquad.cpp index 8505bda..64d6b26 100644 --- a/third_party/WebKit/Source/platform/audio/Biquad.cpp +++ b/third_party/WebKit/Source/platform/audio/Biquad.cpp
@@ -591,298 +591,4 @@ } } -static double RepeatedRootResponse(double n, - double c1, - double c2, - double r, - double log_eps) { - // The response is h(n) = r^(n-2)*[c1*(n+1)*r^2+c2]. We're looking - // for n such that |h(n)| = eps. Equivalently, we want a root - // of the equation log(|h(n)|) - log(eps) = 0 or - // - // (n-2)*log(r) + log(|c1*(n+1)*r^2+c2|) - log(eps) - // - // This helps with finding a nuemrical solution because this - // approximately linearizes the response for large n. - - return (n - 2) * log(r) + log(fabs(c1 * (n + 1) * r * r + c2)) - log_eps; -} - -// Regula Falsi root finder, Illinois variant -// (https://en.wikipedia.org/wiki/False_position_method#The_Illinois_algorithm). -// -// This finds a root of the repeated root response where the root is -// assumed to lie between |low| and |high|. The response is given by -// |c1|, |c2|, and |r| as determined by |RepeatedRootResponse|. -// |log_eps| is the log the the maximum allowed amplitude in the -// response. -static double RootFinder(double low, - double high, - double log_eps, - double c1, - double c2, - double r) { - // Desired accuray of the root (in frames). This doesn't need to be - // super-accurate, so half frame is good enough, and should be less - // than 1 because the algorithm may prematurely terminate. - const double kAccuracyThreshold = 0.5; - // Max number of iterations to do. If we haven't converged by now, - // just return whatever we've found. - const int kMaxIterations = 10; - - int side = 0; - double root = 0; - double f_low = RepeatedRootResponse(low, c1, c2, r, log_eps); - double f_high = RepeatedRootResponse(high, c1, c2, r, log_eps); - - // The function values must be finite and have opposite signs! - DCHECK(std::isfinite(f_low)); - DCHECK(std::isfinite(f_high)); - DCHECK_LE(f_low * f_high, 0); - - int iteration; - for (iteration = 0; iteration < kMaxIterations; ++iteration) { - root = (f_low * high - f_high * low) / (f_low - f_high); - if (fabs(high - low) < kAccuracyThreshold * fabs(high + low)) - break; - double fr = RepeatedRootResponse(root, c1, c2, r, log_eps); - - DCHECK(std::isfinite(fr)); - - if (fr * f_high > 0) { - // fr and f_high have same sign. Copy root to f_high - high = root; - f_high = fr; - side = -1; - } else if (f_low * fr > 0) { - // fr and f_low have same sign. Copy root to f_low - low = root; - f_low = fr; - if (side == 1) - f_high /= 2; - side = 1; - } else { - // f_low * fr looks like zero, so assume we've converged. - break; - } - } - - // Want to know if the max number of iterations is ever exceeded so - // we can understand why that happened. - DCHECK_LT(iteration, kMaxIterations); - - return root; -} - -double Biquad::TailFrame(int coef_index, double max_frame) { - // The Biquad filter is given by - // - // H(z) = (b0 + b1/z + b2/z^2)/(1 + a1/z + a2/z^2). - // - // To compute the tail time, compute the impulse response, h(n), of - // H(z), which we can do analytically. From this impulse response, - // find the value n0 where |h(n)| <= eps for n >= n0. - // - // Assume first that the two poles of H(z) are not repeated, say r1 - // and r2. Then, we can compute a partial fraction expansion of - // H(z): - // - // H(z) = (b0+b1/z+b2/z^2)/[(1-r1/z)*(1-r2/z)] - // = b0 + C2/(z-r2) - C1/(z-r1) - // - // where - // C2 = (b0*r2^2+b1*r2+b2)/(r2-r1) - // C1 = (b0*r1^2+b1*r1+b2)/(r2-r1) - // - // Expand H(z) then this in powers of 1/z gives: - // - // H(z) = b0 -(C2/r2+C1/r1) + sum(C2*r2^(i-1)/z^i + C1*r1^(i-1)/z^i) - // - // Thus, for n > 1 (we don't care about small n), - // - // h(n) = C2*r2^(n-1) + C1*r1^(n-1) - // - // We need to find n0 such that |h(n)| < eps for n > n0. - // - // Case 1: r1 and r2 are real and distinct, with |r1|>=|r2|. - // - // Then - // - // h(n) = C1*r1^(n-1)*(1 + C2/C1*(r2/r1)^(n-1)) - // - // so - // - // |h(n)| = |C1|*|r|^(n-1)*|1+C2/C1*(r2/r1)^(n-1)| - // <= |C1|*|r|^(n-1)*[1 + |C2/C1|*|r2/r1|^(n-1)] - // <= |C1|*|r|^(n-1)*[1 + |C2/C1|] - // - // by using the triangle inequality and the fact that |r2|<=|r1|. - // And we want |h(n)|<=eps which is true if - // - // |C1|*|r|^(n-1)*[1 + |C2/C1|] <= eps - // - // or - // - // n >= 1 + log(eps/C)/log(|r1|) - // - // where C = |C1|*[1+|C2/C1|] = |C1| + |C2|. - // - // Case 2: r1 and r2 are complex - // - // Thne we can write r1=r*exp(i*p) and r2=r*exp(-i*p). So, - // - // |h(n)| = |C2*r^(n-1)*exp(-i*p*(n-1)) + C1*r^(n-1)*exp(i*p*(n-1))| - // = |C1|*r^(n-1)*|1 + C2/C1*exp(-i*p*(n-1))/exp(i*n*(n-1))| - // <= |C1|*r^(n-1)*[1 + |C2/C1|] - // - // Again, this is easily solved to give - // - // n >= 1 + log(eps/C)/log(r) - // - // where C = |C1|*[1+|C2/C1|] = |C1| + |C2|. - // - // Case 3: Repeated roots, r1=r2=r. - // - // In this case, - // - // H(z) = (b0+b1/z+b2/z^2)/[(1-r/z)^2 - // - // Expanding this in powers of 1/z gives: - // - // H(z) = C1*sum((i+1)*r^i/z^i) - C2 * sum(r^(i-2)/z^i) + b2/r^2 - // = b2/r^2 + sum([C1*(i+1)*r^i + C2*r^(i-2)]/z^i) - // where - // C1 = (b0*r^2+b1*r+b2)/r^2 - // C2 = b1*r+2*b2 - // - // Thus, the impulse response is - // - // h(n) = C1*(n+1)*r^n + C2*r^(n-2) - // = r^(n-2)*[C1*(n+1)*r^2+C2] - // - // So - // - // |h(n)| = |r|^(n-2)*|C1*(n+1)*r^2+C2| - // - // To find n such that |h(n)| < eps, we need a numerical method in - // general, so there's no real reason to simplify this or use other - // approximations. Just solve |h(n)|=eps directly. - // - // Thus, for an set of filter coefficients, we can compute the tail - // time. - // - - // If the maximum amplitude of the impulse response is less than - // this, we assume that we've reached the tail of the response. - // Currently, this means that the impulse is less than 1 bit of a - // 16-bit PCM value. - const double kMaxTailAmplitude = 1 / 32768.0; - - // Find the roots of 1+a1/z+a2/z^2 = 0. Or equivalently, - // z^2+a1*z+a2 = 0. From the quadratic formula the roots are - // (-a1+/-sqrt(a1^2-4*a2))/2. - - double a1 = a1_[coef_index]; - double a2 = a2_[coef_index]; - double b0 = b0_[coef_index]; - double b1 = b1_[coef_index]; - double b2 = b2_[coef_index]; - - double tail_frame = 0; - double discrim = a1 * a1 - 4 * a2; - - if (discrim > 0) { - // Compute the real roots so that r1 has the largest magnitude. - double r1; - double r2; - if (a1 < 0) { - r1 = (-a1 + sqrt(discrim)) / 2; - } else { - r1 = (-a1 - sqrt(discrim)) / 2; - } - r2 = a2 / r1; - - double c1 = (b0 * r1 * r1 + b1 * r1 + b2) / (r2 - r1); - double c2 = (b0 * r2 * r2 + b1 * r2 + b2) / (r2 - r1); - - DCHECK(std::isfinite(r1)); - DCHECK(std::isfinite(r2)); - DCHECK(std::isfinite(c1)); - DCHECK(std::isfinite(c2)); - - // It's possible for kMaxTailAmplitude to be greater than c1 + c2. - // This may produce a negative tail frame. Just clamp the tail - // frame to 0. - tail_frame = clampTo( - 1 + log(kMaxTailAmplitude / (fabs(c1) + fabs(c2))) / log(r1), 0); - - DCHECK(std::isfinite(tail_frame)); - } else if (discrim < 0) { - // Two complex roots. - // One root is -a1/2 + i*sqrt(-discrim)/2. - double x = -a1 / 2; - double y = sqrt(-discrim) / 2; - std::complex<double> r1(x, y); - std::complex<double> r2(x, -y); - double r = hypot(x, y); - - DCHECK(std::isfinite(r)); - - // It's possible for r to be 1. (LPF with Q very large can cause this.) - if (r == 1) { - tail_frame = max_frame; - } else { - double c1 = abs((b0 * r1 * r1 + b1 * r1 + b2) / (r2 - r1)); - double c2 = abs((b0 * r2 * r2 + b1 * r2 + b2) / (r2 - r1)); - - DCHECK(std::isfinite(c1)); - DCHECK(std::isfinite(c2)); - - tail_frame = 1 + log(kMaxTailAmplitude / (c1 + c2)) / log(r); - DCHECK(std::isfinite(tail_frame)); - } - } else { - // Repeated roots. This should be pretty rare because all the - // coefficients need to be just the right values to get a - // discriminant of exactly zero. - double r = -a1 / 2; - - if (r == 0) { - // Double pole at 0. This just delays the signal by 2 frames, - // so set the tail frame to 2. - tail_frame = 2; - } else { - double c1 = (b0 * r * r + b1 * r + b2) / (r * r); - double c2 = b1 * r + 2 * b2; - - DCHECK(std::isfinite(c1)); - DCHECK(std::isfinite(c2)); - - // It can happen that c1=c2=0. This basically means that H(z) = - // constant, which is the limiting case for several of the - // biquad filters. - if (c1 == 0 && c2 == 0) { - tail_frame = 0; - } else { - // The function c*(n+1)*r^n is not monotonic, but it's easy to - // find the max point since the derivative is - // c*r^n*(1+(n+1)*log(r)). This has a root at - // -(1+log(r))/log(r). so we can start our search from that - // point to max_frames. - - double low = clampTo(-(1 + log(r)) / log(r), 1.0, - static_cast<double>(max_frame - 1)); - double high = max_frame; - - DCHECK(std::isfinite(low)); - DCHECK(std::isfinite(high)); - - tail_frame = RootFinder(low, high, log(kMaxTailAmplitude), c1, c2, r); - } - } - } - - return tail_frame; -} - } // namespace blink
diff --git a/third_party/WebKit/Source/platform/audio/Biquad.h b/third_party/WebKit/Source/platform/audio/Biquad.h index 9d51ba3..7a6bde19 100644 --- a/third_party/WebKit/Source/platform/audio/Biquad.h +++ b/third_party/WebKit/Source/platform/audio/Biquad.h
@@ -70,13 +70,6 @@ // Resets filter state void Reset(); - // Compute tail frame based on the filter coefficents at index - // |coef_index|. The tail frame is the frame number where the - // impulse response of the filter falls below a threshold value. - // The maximum allowed frame value is given by |max_frame|. This - // limits how much work is done in computing the frame numer. - double TailFrame(int coef_index, double max_frame); - // Filter response at a set of n frequencies. The magnitude and // phase response are returned in magResponse and phaseResponse. // The phase response is in radians.
diff --git a/third_party/WebKit/Source/platform/text/TextBreakIterator.cpp b/third_party/WebKit/Source/platform/text/TextBreakIterator.cpp index 33fbe792d..3af733ca 100644 --- a/third_party/WebKit/Source/platform/text/TextBreakIterator.cpp +++ b/third_party/WebKit/Source/platform/text/TextBreakIterator.cpp
@@ -391,7 +391,7 @@ } unsigned LazyLineBreakIterator::NextBreakOpportunity(unsigned offset) const { - int next_break = 0; + int next_break = -1; IsBreakable(offset, next_break); return next_break; } @@ -400,8 +400,7 @@ unsigned min) const { unsigned pos = std::min(offset, string_.length()); for (; pos > min; pos--) { - int next_break = 0; - if (IsBreakable(pos, next_break)) + if (IsBreakable(pos)) return pos; } return min;
diff --git a/third_party/WebKit/Source/platform/text/TextBreakIterator.h b/third_party/WebKit/Source/platform/text/TextBreakIterator.h index 88b63e5..3a587a5 100644 --- a/third_party/WebKit/Source/platform/text/TextBreakIterator.h +++ b/third_party/WebKit/Source/platform/text/TextBreakIterator.h
@@ -217,6 +217,11 @@ return IsBreakable(pos, next_breakable, break_type_); } + inline bool IsBreakable(int pos) const { + int next_breakable = -1; + return IsBreakable(pos, next_breakable, break_type_); + } + // Returns the break opportunity at or after |offset|. unsigned NextBreakOpportunity(unsigned offset) const;
diff --git a/tools/checkteamtags/checkteamtags.py b/tools/checkteamtags/checkteamtags.py index c06c354..02bbe516 100755 --- a/tools/checkteamtags/checkteamtags.py +++ b/tools/checkteamtags/checkteamtags.py
@@ -90,9 +90,11 @@ error_message = 'The component "%s" has more than one team: ' % component team_details = [] for team in teams: + offending_dirs = [d for d in team_to_dir[team] + if new_dir_to_component[d] == component] team_details.append('%(team)s is used in %(paths)s' % { 'team': team, - 'paths': ', '.join(team_to_dir[team]), + 'paths': ', '.join(offending_dirs), }) error_message += '; '.join(team_details) result.append({
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml index cb0382b..b107d9bd 100644 --- a/tools/metrics/histograms/enums.xml +++ b/tools/metrics/histograms/enums.xml
@@ -21554,6 +21554,7 @@ <int value="-2040471724" label="CrOSComponent:disabled"/> <int value="-2040115518" label="load-media-router-component-extension"/> <int value="-2036149591" label="FaviconsFromWebManifest:disabled"/> + <int value="-2035126988" label="enabled-new-style-notification"/> <int value="-2033225430" label="NTPMostLikelyFaviconsFromServer:disabled"/> <int value="-2029912304" label="StaleWhileRevalidate2:enabled"/> <int value="-2025367104" label="enable-material-design-ntp"/> @@ -22207,6 +22208,7 @@ <int value="334802038" label="OfflinePreviews:disabled"/> <int value="346711293" label="enable-save-password-bubble"/> <int value="348854923" label="v8-cache-strategies-for-cache-storage"/> + <int value="352191859" label="disabled-new-style-notification"/> <int value="358399482" label="enable-high-dpi-fixed-position-compositing"/> <int value="358493847" label="BackgroundLoader:disabled"/> <int value="360391863" label="NTPOfflineBadge:enabled"/> @@ -23691,9 +23693,16 @@ <int value="0" label="Neutral (deprecated February 2017)"/> <int value="1" label="Non-Secure"/> <int value="2" label="Neutral with a verbose warning on sensitive fields"/> + <int value="3" label="Neutral with a verbose warning after editing a form"/> + <int value="4" label="Neutral with a verbose warning in Incognito mode"/> + <int value="5" label="Neutral with a verbose warning after editing a form or + in Incognito mode"/> </enum> <enum name="MarkNonSecureAsStatus" type="int"> + <obsolete> + Deprecated 09/2016 and replaced with MarkHttpAsStatus. + </obsolete> <int value="0" label="Neutral"/> <int value="1" label="Dubious (deprecated August 2015)"/> <int value="2" label="Non-Secure"/> @@ -35140,6 +35149,7 @@ <int value="7" label="Sites"/> <int value="8" label="Sync"/> <int value="9" label="WebApk"/> + <int value="10" label="Browser Actions"/> </enum> <enum name="TabBackgroundLoadStatus" type="int">
diff --git a/tools/perf/page_sets/webrtc_cases.py b/tools/perf/page_sets/webrtc_cases.py index 582dc53..cd7fb321 100644 --- a/tools/perf/page_sets/webrtc_cases.py +++ b/tools/perf/page_sets/webrtc_cases.py
@@ -152,7 +152,3 @@ self.DisableStory('audio_call_isac/1600_10s', [story.expectations.ALL], 'crbug.com/468732') - - self.DisableStory('30s_datachannel_transfer', - [story.expectations.ALL_DESKTOP], - 'crbug.com/726811')
diff --git a/ui/accessibility/platform/ax_platform_node_win.cc b/ui/accessibility/platform/ax_platform_node_win.cc index 44af7cd..603f69a 100644 --- a/ui/accessibility/platform/ax_platform_node_win.cc +++ b/ui/accessibility/platform/ax_platform_node_win.cc
@@ -550,8 +550,6 @@ return E_FAIL; } -// IAccessible functions not supported. - STDMETHODIMP AXPlatformNodeWin::get_accSelection(VARIANT* selected) { COM_OBJECT_VALIDATE_1_ARG(selected); if (selected) @@ -561,7 +559,17 @@ STDMETHODIMP AXPlatformNodeWin::accSelect( LONG flagsSelect, VARIANT var_id) { - return E_NOTIMPL; + AXPlatformNodeWin* target; + COM_OBJECT_VALIDATE_VAR_ID_AND_GET_TARGET(var_id, target); + + if (flagsSelect & SELFLAG_TAKEFOCUS) { + ui::AXActionData action_data; + action_data.action = ui::AX_ACTION_FOCUS; + target->delegate_->AccessibilityPerformAction(action_data); + return S_OK; + } + + return S_FALSE; } STDMETHODIMP AXPlatformNodeWin::get_accHelpTopic(
diff --git a/ui/app_list/BUILD.gn b/ui/app_list/BUILD.gn index ce45400..dd99897 100644 --- a/ui/app_list/BUILD.gn +++ b/ui/app_list/BUILD.gn
@@ -140,8 +140,6 @@ "views/search_result_actions_view.cc", "views/search_result_actions_view.h", "views/search_result_actions_view_delegate.h", - "views/search_result_answer_card_view.cc", - "views/search_result_answer_card_view.h", "views/search_result_container_view.cc", "views/search_result_container_view.h", "views/search_result_list_view.cc",
diff --git a/ui/app_list/views/contents_view.cc b/ui/app_list/views/contents_view.cc index bb8218b..aad1125 100644 --- a/ui/app_list/views/contents_view.cc +++ b/ui/app_list/views/contents_view.cc
@@ -17,23 +17,56 @@ #include "ui/app_list/views/apps_grid_view.h" #include "ui/app_list/views/custom_launcher_page_view.h" #include "ui/app_list/views/search_box_view.h" -#include "ui/app_list/views/search_result_answer_card_view.h" #include "ui/app_list/views/search_result_list_view.h" #include "ui/app_list/views/search_result_page_view.h" #include "ui/app_list/views/search_result_tile_item_list_view.h" #include "ui/app_list/views/start_page_view.h" #include "ui/events/event.h" +#include "ui/views/layout/box_layout.h" #include "ui/views/view_model.h" #include "ui/views/widget/widget.h" namespace app_list { +namespace { + +// Container of the search answer view. +class SearchAnswerContainerView : public views::View { + public: + explicit SearchAnswerContainerView(views::View* search_results_page_view) + : search_results_page_view_(search_results_page_view) { + views::BoxLayout* answer_container_layout = + new views::BoxLayout(views::BoxLayout::kHorizontal, 0, 0, 0); + answer_container_layout->set_main_axis_alignment( + views::BoxLayout::MAIN_AXIS_ALIGNMENT_CENTER); + SetLayoutManager(answer_container_layout); + } + + // views::View overrides: + void ChildPreferredSizeChanged(View* child) override { + if (visible()) + search_results_page_view_->Layout(); + } + + const char* GetClassName() const override { + return "SearchAnswerContainerView"; + } + + private: + views::View* const search_results_page_view_; + + DISALLOW_COPY_AND_ASSIGN(SearchAnswerContainerView); +}; + +} // namespace + ContentsView::ContentsView(AppListMainView* app_list_main_view) : model_(nullptr), apps_container_view_(nullptr), search_results_page_view_(nullptr), start_page_view_(nullptr), custom_page_view_(nullptr), + search_answer_container_view_(nullptr), app_list_main_view_(app_list_main_view), page_before_search_(0) { pagination_model_.SetTransitionDurations(kPageTransitionDurationInMs, @@ -43,6 +76,8 @@ ContentsView::~ContentsView() { pagination_model_.RemoveObserver(this); + if (model_) + model_->RemoveObserver(this); } void ContentsView::Init(AppListModel* model) { @@ -69,14 +104,14 @@ // Search results UI. search_results_page_view_ = new SearchResultPageView(); - // Search result containers. - views::View* const search_answer_view = - view_delegate->GetSearchAnswerWebView(); - if (search_answer_view) { - search_results_page_view_->AddSearchResultContainerView( - nullptr, new SearchResultAnswerCardView( - model_, search_results_page_view_, search_answer_view)); - } + // Search answer container UI. + search_answer_container_view_ = + new SearchAnswerContainerView(search_results_page_view_); + search_answer_container_view_->SetVisible(false); + views::View* search_answer_view = view_delegate->GetSearchAnswerWebView(); + if (search_answer_view) + search_answer_container_view_->AddChildView(search_answer_view); + search_results_page_view_->AddChildView(search_answer_container_view_); AppListModel::SearchResults* results = view_delegate->GetModel()->results(); search_results_page_view_->AddSearchResultContainerView( @@ -107,6 +142,8 @@ pagination_model_.SelectPage(initial_page_index, false); ActivePageChanged(); + + model_->AddObserver(this); } void ContentsView::CancelDrag() { @@ -487,4 +524,12 @@ UpdatePageBounds(); } +void ContentsView::OnSearchAnswerAvailableChanged(bool has_answer) { + if (has_answer == search_answer_container_view_->visible()) + return; + + search_answer_container_view_->SetVisible(has_answer); + search_results_page_view_->Layout(); +} + } // namespace app_list
diff --git a/ui/app_list/views/contents_view.h b/ui/app_list/views/contents_view.h index 62a52cd2..435ff00 100644 --- a/ui/app_list/views/contents_view.h +++ b/ui/app_list/views/contents_view.h
@@ -13,6 +13,7 @@ #include "base/macros.h" #include "ui/app_list/app_list_export.h" #include "ui/app_list/app_list_model.h" +#include "ui/app_list/app_list_model_observer.h" #include "ui/app_list/pagination_model.h" #include "ui/app_list/pagination_model_observer.h" #include "ui/views/view.h" @@ -42,7 +43,8 @@ // interface for switching between launcher pages, and animates the transition // between them. class APP_LIST_EXPORT ContentsView : public views::View, - public PaginationModelObserver { + public PaginationModelObserver, + public AppListModelObserver { public: explicit ContentsView(AppListMainView* app_list_main_view); ~ContentsView() override; @@ -134,6 +136,9 @@ void TransitionStarted() override; void TransitionChanged() override; + // Overridden from AppListModelObserver: + void OnSearchAnswerAvailableChanged(bool has_answer) override; + private: // Sets the active launcher page, accounting for whether the change is for // search results. @@ -185,6 +190,10 @@ StartPageView* start_page_view_; CustomLauncherPageView* custom_page_view_; + // Unowned pointer to the container of the search answer web view. This + // container view is a sub-view of search_results_page_view_. + View* search_answer_container_view_; + // The child page views. Owned by the views hierarchy. std::vector<AppListPage*> app_list_pages_;
diff --git a/ui/app_list/views/search_result_answer_card_view.cc b/ui/app_list/views/search_result_answer_card_view.cc deleted file mode 100644 index 89078c0..0000000 --- a/ui/app_list/views/search_result_answer_card_view.cc +++ /dev/null
@@ -1,144 +0,0 @@ -// Copyright 2017 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 "ui/app_list/views/search_result_answer_card_view.h" - -#include "ui/app_list/app_list_constants.h" -#include "ui/app_list/app_list_features.h" -#include "ui/app_list/views/search_result_page_view.h" -#include "ui/views/background.h" -#include "ui/views/controls/button/custom_button.h" -#include "ui/views/layout/box_layout.h" -#include "ui/views/layout/fill_layout.h" - -namespace app_list { - -namespace { - -// Answer card relevance is high to always have it first. -constexpr double kSearchAnswerCardRelevance = 100; - -// Container of the search answer view. -class SearchAnswerContainerView : public views::CustomButton { - public: - explicit SearchAnswerContainerView(views::View* search_results_page_view) - : CustomButton(nullptr), - search_results_page_view_(search_results_page_view) { - // Center the card horizontally in the container. - views::BoxLayout* answer_container_layout = - new views::BoxLayout(views::BoxLayout::kHorizontal, 0, 0, 0); - answer_container_layout->set_main_axis_alignment( - views::BoxLayout::MAIN_AXIS_ALIGNMENT_CENTER); - SetLayoutManager(answer_container_layout); - } - - void SetSelected(bool selected) { - if (selected == selected_) - return; - selected_ = selected; - UpdateBackgroundColor(); - } - - // views::CustomButton overrides: - void ChildPreferredSizeChanged(View* child) override { - // Card size changed. - if (visible()) - search_results_page_view_->Layout(); - } - - int GetHeightForWidth(int w) const override { - return visible() ? CustomButton::GetHeightForWidth(w) : 0; - } - - const char* GetClassName() const override { - return "SearchAnswerContainerView"; - } - - void StateChanged(ButtonState old_state) override { UpdateBackgroundColor(); } - - private: - void UpdateBackgroundColor() { - views::Background* background = nullptr; - - if (selected_) { - background = views::Background::CreateSolidBackground(kSelectedColor); - } else if (state() == STATE_HOVERED || state() == STATE_PRESSED) { - background = views::Background::CreateSolidBackground(kHighlightedColor); - } - - set_background(background); - SchedulePaint(); - } - - views::View* const search_results_page_view_; - bool selected_ = false; - - DISALLOW_COPY_AND_ASSIGN(SearchAnswerContainerView); -}; - -} // namespace - -SearchResultAnswerCardView::SearchResultAnswerCardView( - AppListModel* model, - SearchResultPageView* search_results_page_view, - views::View* search_answer_view) - : model_(model), - search_answer_container_view_( - new SearchAnswerContainerView(search_results_page_view)) { - search_answer_container_view_->SetVisible(false); - search_answer_container_view_->AddChildView(search_answer_view); - AddChildView(search_answer_container_view_); - model->AddObserver(this); - SetLayoutManager(new views::FillLayout); -} - -SearchResultAnswerCardView::~SearchResultAnswerCardView() { - model_->RemoveObserver(this); -} - -const char* SearchResultAnswerCardView::GetClassName() const { - return "SearchResultAnswerCardView"; -} - -void SearchResultAnswerCardView::OnContainerSelected( - bool from_bottom, - bool directional_movement) { - if (num_results() == 0) - return; - - SetSelectedIndex(0); -} - -int SearchResultAnswerCardView::GetYSize() { - return num_results(); -} - -int SearchResultAnswerCardView::DoUpdate() { - const bool have_result = search_answer_container_view_->visible(); - set_container_score(have_result ? kSearchAnswerCardRelevance : 0); - return have_result ? 1 : 0; -} - -void SearchResultAnswerCardView::UpdateSelectedIndex(int old_selected, - int new_selected) { - if (new_selected == old_selected) - return; - - const bool is_selected = new_selected == 0; - search_answer_container_view_->SetSelected(is_selected); - if (is_selected) - NotifyAccessibilityEvent(ui::AX_EVENT_SELECTION, true); -} - -void SearchResultAnswerCardView::OnSearchAnswerAvailableChanged( - bool has_answer) { - const bool visible = has_answer && !features::IsAnswerCardDarkRunEnabled(); - if (visible == search_answer_container_view_->visible()) - return; - - search_answer_container_view_->SetVisible(visible); - ScheduleUpdate(); -} - -} // namespace app_list
diff --git a/ui/app_list/views/search_result_answer_card_view.h b/ui/app_list/views/search_result_answer_card_view.h deleted file mode 100644 index 8c985f0..0000000 --- a/ui/app_list/views/search_result_answer_card_view.h +++ /dev/null
@@ -1,57 +0,0 @@ -// Copyright 2017 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 UI_APP_LIST_VIEWS_SEARCH_RESULT_ANSWER_CARD_VIEW_H_ -#define UI_APP_LIST_VIEWS_SEARCH_RESULT_ANSWER_CARD_VIEW_H_ - -#include "ui/app_list/app_list_model_observer.h" -#include "ui/app_list/views/search_result_container_view.h" - -namespace app_list { - -class AppListModel; -class SearchResultPageView; - -namespace { -class SearchAnswerContainerView; -} - -// Result container for the search answer card. -class APP_LIST_EXPORT SearchResultAnswerCardView - : public SearchResultContainerView, - public AppListModelObserver { - public: - SearchResultAnswerCardView(AppListModel* model, - SearchResultPageView* search_results_page_view, - views::View* search_answer_view); - ~SearchResultAnswerCardView() override; - - private: - // Overridden from views::View: - const char* GetClassName() const override; - - // Overridden from SearchResultContainerView: - void OnContainerSelected(bool from_bottom, - bool directional_movement) override; - void NotifyFirstResultYIndex(int y_index) override {} - int GetYSize() override; - int DoUpdate() override; - void UpdateSelectedIndex(int old_selected, int new_selected) override; - - // Overridden from AppListModelObserver - void OnSearchAnswerAvailableChanged(bool has_answer) override; - - // Unowned pointer to application list model. - AppListModel* const model_; - - // Pointer to the container of the search answer; owned by the view hierarchy. - // It's visible iff we have a search answer result. - SearchAnswerContainerView* const search_answer_container_view_; - - DISALLOW_COPY_AND_ASSIGN(SearchResultAnswerCardView); -}; - -} // namespace app_list - -#endif // UI_APP_LIST_VIEWS_SEARCH_RESULT_ANSWER_CARD_VIEW_H_
diff --git a/ui/app_list/views/search_result_container_view.h b/ui/app_list/views/search_result_container_view.h index 19c02c13..b5f8496 100644 --- a/ui/app_list/views/search_result_container_view.h +++ b/ui/app_list/views/search_result_container_view.h
@@ -84,12 +84,11 @@ virtual void OnContainerSelected(bool from_bottom, bool directional_movement) = 0; - protected: + private: // Schedules an Update call using |update_factory_|. Do nothing if there is a // pending call. void ScheduleUpdate(); - private: // Updates UI with model. Returns the number of visible results. virtual int DoUpdate() = 0;
diff --git a/ui/arc/notification/arc_custom_notification_view.cc b/ui/arc/notification/arc_custom_notification_view.cc index c84e6464..d5ec3161 100644 --- a/ui/arc/notification/arc_custom_notification_view.cc +++ b/ui/arc/notification/arc_custom_notification_view.cc
@@ -10,6 +10,7 @@ #include "components/exo/notification_surface.h" #include "components/exo/surface.h" #include "ui/accessibility/ax_action_data.h" +#include "ui/accessibility/ax_node_data.h" #include "ui/arc/notification/arc_notification_view.h" #include "ui/base/l10n/l10n_util.h" #include "ui/base/resource/resource_bundle.h" @@ -251,6 +252,7 @@ // Create a layer as an anchor to insert surface copy during a slide. SetPaintToLayer(); UpdatePreferredSize(); + UpdateAccessibleName(); } ArcCustomNotificationView::~ArcCustomNotificationView() { @@ -483,6 +485,14 @@ return false; } +void ArcCustomNotificationView::UpdateAccessibleName() { + // Don't update the accessible name when we are about to be destroyed. + if (!item_) + return; + + accessible_name_ = item_->GetAccessibleName(); +} + void ArcCustomNotificationView::ViewHierarchyChanged( const views::View::ViewHierarchyChangedDetails& details) { views::Widget* widget = GetWidget(); @@ -623,6 +633,12 @@ return false; } +void ArcCustomNotificationView::GetAccessibleNodeData( + ui::AXNodeData* node_data) { + node_data->role = ui::AX_ROLE_BUTTON; + node_data->SetName(accessible_name_); +} + void ArcCustomNotificationView::ButtonPressed(views::Button* sender, const ui::Event& event) { if (item_ && !item_->GetPinned() && sender == close_button_.get()) { @@ -659,6 +675,7 @@ } void ArcCustomNotificationView::OnItemUpdated() { + UpdateAccessibleName(); UpdatePinnedState(); UpdateSnapshot(); if (ShouldUpdateControlButtonsColor())
diff --git a/ui/arc/notification/arc_custom_notification_view.h b/ui/arc/notification/arc_custom_notification_view.h index ce2d65d..0257b3d 100644 --- a/ui/arc/notification/arc_custom_notification_view.h +++ b/ui/arc/notification/arc_custom_notification_view.h
@@ -87,6 +87,7 @@ void ActivateToast(); void StartControlButtonsColorAnimation(); bool ShouldUpdateControlButtonsColor() const; + void UpdateAccessibleName(); // views::NativeViewHost void ViewHierarchyChanged( @@ -99,6 +100,7 @@ void OnBlur() override; views::FocusTraversable* GetFocusTraversable() override; bool HandleAccessibleAction(const ui::AXActionData& action) override; + void GetAccessibleNodeData(ui::AXNodeData* node_data) override; // views::ButtonListener void ButtonPressed(views::Button* sender, const ui::Event& event) override; @@ -154,6 +156,8 @@ std::unique_ptr<gfx::LinearAnimation> control_button_color_animation_; + base::string16 accessible_name_; + DISALLOW_COPY_AND_ASSIGN(ArcCustomNotificationView); };
diff --git a/ui/arc/notification/arc_notification_item.h b/ui/arc/notification/arc_notification_item.h index 5dac895..397b324 100644 --- a/ui/arc/notification/arc_notification_item.h +++ b/ui/arc/notification/arc_notification_item.h
@@ -77,6 +77,8 @@ virtual const std::string& GetNotificationKey() const = 0; // Returns the notification ID used in the Chrome message center. virtual const std::string& GetNotificationId() const = 0; + // Returnes the accessible name of the notification. + virtual const base::string16& GetAccessibleName() const = 0; }; } // namespace arc
diff --git a/ui/arc/notification/arc_notification_item_impl.cc b/ui/arc/notification/arc_notification_item_impl.cc index 610b4e1..18ac549 100644 --- a/ui/arc/notification/arc_notification_item_impl.cc +++ b/ui/arc/notification/arc_notification_item_impl.cc
@@ -77,8 +77,14 @@ rich_data.priority = ConvertAndroidPriority(data->priority); if (data->small_icon) rich_data.small_image = gfx::Image::CreateFrom1xBitmap(*data->small_icon); - if (data->accessible_name.has_value()) - rich_data.accessible_name = base::UTF8ToUTF16(*data->accessible_name); + if (data->accessible_name.has_value()) { + accessible_name_ = base::UTF8ToUTF16(*data->accessible_name); + } else { + accessible_name_ = base::JoinString( + {base::UTF8ToUTF16(data->title), base::UTF8ToUTF16(data->message)}, + base::ASCIIToUTF16("\n")); + } + rich_data.accessible_name = accessible_name_; message_center::NotifierId notifier_id( message_center::NotifierId::SYSTEM_COMPONENT, kNotifierId); @@ -195,4 +201,8 @@ return notification_id_; } +const base::string16& ArcNotificationItemImpl::GetAccessibleName() const { + return accessible_name_; +} + } // namespace arc
diff --git a/ui/arc/notification/arc_notification_item_impl.h b/ui/arc/notification/arc_notification_item_impl.h index 2fbf3147..9f69b57 100644 --- a/ui/arc/notification/arc_notification_item_impl.h +++ b/ui/arc/notification/arc_notification_item_impl.h
@@ -48,6 +48,7 @@ mojom::ArcNotificationShownContents GetShownContents() const override; const std::string& GetNotificationKey() const override; const std::string& GetNotificationId() const override; + const base::string16& GetAccessibleName() const override; private: // Return true if it's on the thread this instance is created on. @@ -68,6 +69,8 @@ mojom::ArcNotificationShownContents::CONTENTS_SHOWN; // The reference counter of the window. int window_ref_count_ = 0; + // The accessible name of the latest notification. + base::string16 accessible_name_; base::ObserverList<Observer> observers_;
diff --git a/ui/gfx/color_space.cc b/ui/gfx/color_space.cc index 8f72e63..9230410 100644 --- a/ui/gfx/color_space.cc +++ b/ui/gfx/color_space.cc
@@ -455,6 +455,17 @@ primaries.fWY = 0.3290f; break; + case ColorSpace::PrimaryID::WIDE_GAMUT_COLOR_SPIN: + primaries.fRX = 0.01f; + primaries.fRY = 0.01f; + primaries.fGX = 0.98f; + primaries.fGY = 0.01f; + primaries.fBX = 0.01f; + primaries.fBY = 0.98f; + primaries.fWX = 0.3127f; + primaries.fWY = 0.3290f; + break; + case ColorSpace::PrimaryID::FILM: primaries.fRX = 0.681f; primaries.fRY = 0.319f;
diff --git a/ui/gfx/color_space.h b/ui/gfx/color_space.h index aaca2de..35d3425 100644 --- a/ui/gfx/color_space.h +++ b/ui/gfx/color_space.h
@@ -45,6 +45,8 @@ // Corresponds the the primaries of the "Generic RGB" profile used in the // Apple ColorSync application, used by layout tests on Mac. APPLE_GENERIC_RGB, + // A very wide gamut space with rotated primaries. Used by layout tests. + WIDE_GAMUT_COLOR_SPIN, // Primaries defined by the primary matrix |custom_primary_matrix_|. CUSTOM, // For color spaces defined by an ICC profile which cannot be represented
diff --git a/ui/gfx/color_space_win.cc b/ui/gfx/color_space_win.cc index a012e1f..4189aff62 100644 --- a/ui/gfx/color_space_win.cc +++ b/ui/gfx/color_space_win.cc
@@ -79,6 +79,7 @@ case gfx::ColorSpace::PrimaryID::XYZ_D50: case gfx::ColorSpace::PrimaryID::ADOBE_RGB: case gfx::ColorSpace::PrimaryID::APPLE_GENERIC_RGB: + case gfx::ColorSpace::PrimaryID::WIDE_GAMUT_COLOR_SPIN: case gfx::ColorSpace::PrimaryID::ICC_BASED: case gfx::ColorSpace::PrimaryID::CUSTOM: case gfx::ColorSpace::PrimaryID::INVALID:
diff --git a/ui/gfx/icc_profile.cc b/ui/gfx/icc_profile.cc index f22c9cf..723d062 100644 --- a/ui/gfx/icc_profile.cc +++ b/ui/gfx/icc_profile.cc
@@ -132,10 +132,11 @@ ColorSpace generic_rgb_color_space(ColorSpace::PrimaryID::APPLE_GENERIC_RGB, ColorSpace::TransferID::GAMMA18); generic_rgb_color_space.GetICCProfile(&icc_profile); - } else if (value == "bt2020-gamma18") { - ColorSpace generic_rgb_color_space(ColorSpace::PrimaryID::BT2020, - ColorSpace::TransferID::GAMMA18); - generic_rgb_color_space.GetICCProfile(&icc_profile); + } else if (value == "color-spin-gamma24") { + ColorSpace color_spin_color_space( + ColorSpace::PrimaryID::WIDE_GAMUT_COLOR_SPIN, + ColorSpace::TransferID::GAMMA24); + color_spin_color_space.GetICCProfile(&icc_profile); } else { LOG(ERROR) << "Invalid forced color profile"; }
diff --git a/ui/gfx/switches.cc b/ui/gfx/switches.cc index f991cd8..0219b5e 100644 --- a/ui/gfx/switches.cc +++ b/ui/gfx/switches.cc
@@ -27,8 +27,7 @@ // Force all monitors to be treated as though they have the specified color // profile. Accepted values are "srgb" and "generic-rgb" (currently used by Mac -// layout tests) and "bt2020-gamma18" (to be used by Mac layout tests in the -// future). +// layout tests) and "color-spin-gamma24" (used by layout tests). const char kForceColorProfile[] = "force-color-profile"; } // namespace switches
diff --git a/ui/gl/gl_context.h b/ui/gl/gl_context.h index e72b1ec..69f4c38 100644 --- a/ui/gl/gl_context.h +++ b/ui/gl/gl_context.h
@@ -39,6 +39,19 @@ class RealGLApi; class TraceGLApi; +// Where available, choose a GL context priority for devices that support it. +// Currently this requires the EGL_IMG_context_priority extension that is +// present on Daydream ready Android devices. Default is Medium, and the +// attribute is ignored if the extension is missing. +// +// "High" priority must only be used for special cases with strong realtime +// requirements, it is incompatible with other critical system GL work such as +// the GVR library's asynchronous reprojection for VR viewing. Please avoid +// using it for any GL contexts that may be used during VR presentation, +// see crbug.com/727800. +// +// Instead, consider using "Low" priority for possibly-slow GL work such as +// user WebGL content. enum ContextPriority { ContextPriorityLow, ContextPriorityMedium,
diff --git a/ui/message_center/BUILD.gn b/ui/message_center/BUILD.gn index a01d1709..51f90da 100644 --- a/ui/message_center/BUILD.gn +++ b/ui/message_center/BUILD.gn
@@ -15,6 +15,8 @@ "notification_settings_button.icon", "notification_close_button.1x.icon", "notification_close_button.icon", + "product.1x.icon", + "product.icon", ] } @@ -138,6 +140,8 @@ "views/notification_button.h", "views/notification_view.cc", "views/notification_view.h", + "views/notification_view_md.cc", + "views/notification_view_md.h", "views/notifier_settings_view.cc", "views/notifier_settings_view.h", "views/padded_button.cc",
diff --git a/ui/message_center/message_center_switches.cc b/ui/message_center/message_center_switches.cc index 4caba23f..44f45888 100644 --- a/ui/message_center/message_center_switches.cc +++ b/ui/message_center/message_center_switches.cc
@@ -17,4 +17,11 @@ const char kMessageCenterChangesWhileOpen[] = "message-center-changes-while-open"; +// Flag to enable or disable new-style notification. This flag will be removed +// once the feature gets stable. +const char kEnableMessageCenterNewStyleNotification[] = + "enabled-new-style-notification"; +const char kDisableMessageCenterNewStyleNotification[] = + "disabled-new-style-notification"; + } // namespace switches
diff --git a/ui/message_center/message_center_switches.h b/ui/message_center/message_center_switches.h index f0c37962..8093b6c 100644 --- a/ui/message_center/message_center_switches.h +++ b/ui/message_center/message_center_switches.h
@@ -18,6 +18,11 @@ // This flag will be removed once the feature gets stable. MESSAGE_CENTER_EXPORT extern const char kMessageCenterChangesWhileOpen[]; +MESSAGE_CENTER_EXPORT extern const char + kEnableMessageCenterNewStyleNotification[]; +MESSAGE_CENTER_EXPORT extern const char + kDisableMessageCenterNewStyleNotification[]; + } // namespace switches #endif // UI_MESSAGE_CENTER_MESSAGE_CENTER_SWITCHES_H_
diff --git a/ui/message_center/vector_icons/product.1x.icon b/ui/message_center/vector_icons/product.1x.icon new file mode 100644 index 0000000..4997801 --- /dev/null +++ b/ui/message_center/vector_icons/product.1x.icon
@@ -0,0 +1,38 @@ +// Copyright 2017 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. + +CANVAS_DIMENSIONS, 16, +MOVE_TO, 8, 5.3f, +LINE_TO, 13.34f, 5.3f, +CUBIC_TO, 12.38f, 3.32f, 10.34f, 2, 8, 2, +CUBIC_TO, 6.14f, 2, 4.46f, 2.84f, 3.38f, 4.16f, +LINE_TO, 5.36f, 7.58f, +CUBIC_TO, 5.54f, 6.32f, 6.68f, 5.3f, 8, 5.3f, +LINE_TO, 8, 5.3f, +CLOSE, +MOVE_TO, 8, 10.7f, +CUBIC_TO, 6.98f, 10.7f, 6.14f, 10.16f, 5.66f, 9.32f, +LINE_TO, 2.96f, 4.7f, +CUBIC_TO, 2.36f, 5.66f, 2, 6.8f, 2, 8, +CUBIC_TO, 2, 11, 4.16f, 13.46f, 7.04f, 13.94f, +LINE_TO, 9.02f, 10.52f, +CUBIC_TO, 8.66f, 10.64f, 8.36f, 10.7f, 8, 10.7f, +LINE_TO, 8, 10.7f, +CLOSE, +MOVE_TO, 10.7f, 8, +CUBIC_TO, 10.7f, 8.48f, 10.58f, 8.96f, 10.34f, 9.32f, +LINE_TO, 7.64f, 14, +LINE_TO, 8, 14, +CUBIC_TO, 11.3f, 14, 14, 11.3f, 14, 8, +CUBIC_TO, 14, 7.28f, 13.88f, 6.56f, 13.64f, 5.9f, +LINE_TO, 9.68f, 5.9f, +CUBIC_TO, 10.28f, 6.38f, 10.7f, 7.16f, 10.7f, 8, +CLOSE, +MOVE_TO, 8, 10.1f, +CUBIC_TO, 9.16f, 10.1f, 10.1f, 9.16f, 10.1f, 8, +CUBIC_TO, 10.1f, 6.84f, 9.16f, 5.9f, 8, 5.9f, +CUBIC_TO, 6.84f, 5.9f, 5.9f, 6.84f, 5.9f, 8, +CUBIC_TO, 5.9f, 9.16f, 6.84f, 10.1f, 8, 10.1f, +CLOSE, +END
diff --git a/ui/message_center/vector_icons/product.icon b/ui/message_center/vector_icons/product.icon new file mode 100644 index 0000000..98ce8d51 --- /dev/null +++ b/ui/message_center/vector_icons/product.icon
@@ -0,0 +1,38 @@ +// Copyright 2017 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. + +CANVAS_DIMENSIONS, 32, +MOVE_TO, 16, 10.6f, +LINE_TO, 26.68f, 10.6f, +CUBIC_TO, 24.76f, 6.64f, 20.68f, 4, 16, 4, +CUBIC_TO, 12.28f, 4, 8.92f, 5.68f, 6.76f, 8.32f, +LINE_TO, 10.72f, 15.16f, +CUBIC_TO, 11.08f, 12.64f, 13.36f, 10.6f, 16, 10.6f, +LINE_TO, 16, 10.6f, +CLOSE, +MOVE_TO, 16, 21.4f, +CUBIC_TO, 13.96f, 21.4f, 12.28f, 20.32f, 11.32f, 18.64f, +LINE_TO, 5.92f, 9.4f, +CUBIC_TO, 4.72f, 11.32f, 4, 13.6f, 4, 16, +CUBIC_TO, 4, 22, 8.32f, 26.92f, 14.08f, 27.88f, +LINE_TO, 18.04f, 21.04f, +CUBIC_TO, 17.32f, 21.28f, 16.72f, 21.4f, 16, 21.4f, +LINE_TO, 16, 21.4f, +CLOSE, +MOVE_TO, 21.4f, 16, +CUBIC_TO, 21.4f, 16.96f, 21.16f, 17.92f, 20.68f, 18.64f, +LINE_TO, 15.28f, 28, +LINE_TO, 16, 28, +CUBIC_TO, 22.6f, 28, 28, 22.6f, 28, 16, +CUBIC_TO, 28, 14.56f, 27.76f, 13.12f, 27.28f, 11.8f, +LINE_TO, 19.36f, 11.8f, +CUBIC_TO, 20.56f, 12.76f, 21.4f, 14.32f, 21.4f, 16, +CLOSE, +MOVE_TO, 16, 20.2f, +CUBIC_TO, 18.32f, 20.2f, 20.2f, 18.32f, 20.2f, 16, +CUBIC_TO, 20.2f, 13.68f, 18.32f, 11.8f, 16, 11.8f, +CUBIC_TO, 13.68f, 11.8f, 11.8f, 13.68f, 11.8f, 16, +CUBIC_TO, 11.8f, 18.32f, 13.68f, 20.2f, 16, 20.2f, +CLOSE, +END
diff --git a/ui/message_center/views/message_view.cc b/ui/message_center/views/message_view.cc index f7a5d4b..6559660 100644 --- a/ui/message_center/views/message_view.cc +++ b/ui/message_center/views/message_view.cc
@@ -171,6 +171,8 @@ } void MessageView::Layout() { + views::View::Layout(); + gfx::Rect content_bounds = GetContentsBounds(); // Background.
diff --git a/ui/message_center/views/message_view_factory.cc b/ui/message_center/views/message_view_factory.cc index f1c982f4..60567af 100644 --- a/ui/message_center/views/message_view_factory.cc +++ b/ui/message_center/views/message_view_factory.cc
@@ -4,8 +4,11 @@ #include "ui/message_center/views/message_view_factory.h" +#include "base/command_line.h" +#include "ui/message_center/message_center_switches.h" #include "ui/message_center/notification_types.h" #include "ui/message_center/views/notification_view.h" +#include "ui/message_center/views/notification_view_md.h" #if defined(OS_WIN) #include "ui/base/win/shell.h" @@ -23,10 +26,23 @@ case NOTIFICATION_TYPE_IMAGE: case NOTIFICATION_TYPE_MULTIPLE: case NOTIFICATION_TYPE_SIMPLE: - case NOTIFICATION_TYPE_PROGRESS: + case NOTIFICATION_TYPE_PROGRESS: { + bool new_style_notification_enabled = false; // default value + if (base::CommandLine::ForCurrentProcess()->HasSwitch( + switches::kEnableMessageCenterNewStyleNotification)) { + new_style_notification_enabled = true; + } else if (base::CommandLine::ForCurrentProcess()->HasSwitch( + switches::kDisableMessageCenterNewStyleNotification)) { + new_style_notification_enabled = false; + } + // All above roads lead to the generic NotificationView. - notification_view = new NotificationView(controller, notification); + if (new_style_notification_enabled) + notification_view = new NotificationViewMD(controller, notification); + else + notification_view = new NotificationView(controller, notification); break; + } #if defined(TOOLKIT_VIEWS) && !defined(OS_MACOSX) case NOTIFICATION_TYPE_CUSTOM: notification_view =
diff --git a/ui/message_center/views/notification_view_md.cc b/ui/message_center/views/notification_view_md.cc new file mode 100644 index 0000000..5bc79bc9 --- /dev/null +++ b/ui/message_center/views/notification_view_md.cc
@@ -0,0 +1,455 @@ +// Copyright 2017 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 "ui/message_center/views/notification_view_md.h" + +#include <stddef.h> + +#include "base/strings/string_util.h" +#include "ui/base/cursor/cursor.h" +#include "ui/base/l10n/l10n_util.h" +#include "ui/gfx/geometry/size.h" +#include "ui/gfx/image/image_skia_operations.h" +#include "ui/gfx/paint_vector_icon.h" +#include "ui/gfx/skia_util.h" +#include "ui/gfx/text_elider.h" +#include "ui/message_center/message_center.h" +#include "ui/message_center/message_center_style.h" +#include "ui/message_center/notification.h" +#include "ui/message_center/notification_types.h" +#include "ui/message_center/vector_icons.h" +#include "ui/message_center/views/bounded_label.h" +#include "ui/message_center/views/constants.h" +#include "ui/message_center/views/message_center_controller.h" +#include "ui/message_center/views/notification_button.h" +#include "ui/message_center/views/padded_button.h" +#include "ui/strings/grit/ui_strings.h" +#include "ui/views/background.h" +#include "ui/views/border.h" +#include "ui/views/controls/image_view.h" +#include "ui/views/controls/label.h" +#include "ui/views/focus/focus_manager.h" +#include "ui/views/layout/box_layout.h" +#include "ui/views/native_cursor.h" +#include "ui/views/view_targeter.h" + +namespace message_center { + +namespace { + +// Dimensions. +constexpr int kNotificationRightPadding = 13; +constexpr int kNotificationLeftPadding = 13; +constexpr int kNotificationTopPadding = 6; +constexpr int kNotificationBottomPadding = 10; +constexpr gfx::Insets kNotificationPadding(kNotificationTopPadding, + kNotificationLeftPadding, + kNotificationBottomPadding, + kNotificationRightPadding); + +constexpr int kMaxContextTitleLines = 1; + +// Foreground of small icon image. +constexpr SkColor kSmallImageBackgroundColor = SK_ColorWHITE; +// Background of small icon image. +const SkColor kSmallImageColor = SkColorSetRGB(0x43, 0x43, 0x43); + +const gfx::ImageSkia CreateSolidColorImage(int width, + int height, + SkColor color) { + SkBitmap bitmap; + bitmap.allocN32Pixels(width, height); + bitmap.eraseColor(color); + return gfx::ImageSkia::CreateFrom1xBitmap(bitmap); +} + +// Take the alpha channel of icon, mask it with the foreground, +// then add the masked foreground on top of the background +const gfx::ImageSkia GetMaskedIcon(const gfx::ImageSkia& icon) { + int width = icon.width(); + int height = icon.height(); + + // Background color grey + const gfx::ImageSkia background = CreateSolidColorImage( + width, height, message_center::kSmallImageBackgroundColor); + // Foreground color white + const gfx::ImageSkia foreground = + CreateSolidColorImage(width, height, message_center::kSmallImageColor); + const gfx::ImageSkia masked_small_image = + gfx::ImageSkiaOperations::CreateMaskedImage(foreground, icon); + return gfx::ImageSkiaOperations::CreateSuperimposedImage(background, + masked_small_image); +} + +const gfx::ImageSkia GetProductIcon() { + return gfx::CreateVectorIcon(kProductIcon, kSmallImageColor); +} + +} // anonymous namespace + +// //////////////////////////////////////////////////////////// +// NotificationViewMD +// //////////////////////////////////////////////////////////// + +views::View* NotificationViewMD::TargetForRect(views::View* root, + const gfx::Rect& rect) { + CHECK_EQ(root, this); + + // TODO(tdanderson): Modify this function to support rect-based event + // targeting. Using the center point of |rect| preserves this function's + // expected behavior for the time being. + gfx::Point point = rect.CenterPoint(); + + // Want to return this for underlying views, otherwise GetCursor is not + // called. But buttons are exceptions, they'll have their own event handlings. + std::vector<views::View*> buttons(action_buttons_.begin(), + action_buttons_.end()); + if (settings_button_) + buttons.push_back(settings_button_.get()); + if (close_button_) + buttons.push_back(close_button_.get()); + + for (size_t i = 0; i < buttons.size(); ++i) { + gfx::Point point_in_child = point; + ConvertPointToTarget(this, buttons[i], &point_in_child); + if (buttons[i]->HitTestPoint(point_in_child)) + return buttons[i]->GetEventHandlerForPoint(point_in_child); + } + + return root; +} + +void NotificationViewMD::CreateOrUpdateViews(const Notification& notification) { + CreateOrUpdateContextTitleView(notification); + CreateOrUpdateTitleView(notification); + CreateOrUpdateMessageView(notification); + CreateOrUpdateProgressBarView(notification); + CreateOrUpdateListItemViews(notification); + CreateOrUpdateIconView(notification); + CreateOrUpdateSmallIconView(notification); + CreateOrUpdateImageView(notification); + CreateOrUpdateActionButtonViews(notification); + CreateOrUpdateCloseButtonView(notification); + CreateOrUpdateSettingsButtonView(notification); +} + +NotificationViewMD::NotificationViewMD(MessageCenterController* controller, + const Notification& notification) + : MessageView(controller, notification), + clickable_(notification.clickable()) { + layout_ = new views::BoxLayout(views::BoxLayout::kVertical, 0, 0, 2); + layout_->set_inside_border_insets(kNotificationPadding); + SetLayoutManager(layout_); + + // Create the top_view_, which collects into a vertical box all content + // at the top of the notification (to the right of the icon) except for the + // close button. + top_view_ = new views::View(); + views::BoxLayout* top_box_layout = + new views::BoxLayout(views::BoxLayout::kHorizontal, 0, 1, 5); + top_box_layout->set_cross_axis_alignment( + views::BoxLayout::CROSS_AXIS_ALIGNMENT_CENTER); + top_view_->SetLayoutManager(top_box_layout); + AddChildView(top_view_); + + main_view_ = new views::View(); + main_view_->SetLayoutManager( + new views::BoxLayout(views::BoxLayout::kVertical, 0, 0, 0)); + AddChildView(main_view_); + + // Create the bottom_view_, which collects notification icon. + bottom_view_ = new views::View(); + bottom_view_->SetLayoutManager( + new views::BoxLayout(views::BoxLayout::kVertical, 0, 0, 0)); + AddChildView(bottom_view_); + + views::ImageView* small_image_view = new views::ImageView(); + small_image_view->SetImageSize(gfx::Size(kSmallImageSize, kSmallImageSize)); + small_image_view->set_owned_by_client(); + small_image_view_.reset(small_image_view); + top_view_->AddChildView(small_image_view_.get()); + + CreateOrUpdateViews(notification); + + SetEventTargeter( + std::unique_ptr<views::ViewTargeter>(new views::ViewTargeter(this))); +} + +NotificationViewMD::~NotificationViewMD() {} + +void NotificationViewMD::Layout() { + MessageView::Layout(); + + // Before any resizing, set or adjust the number of message lines. + int title_lines = 0; + if (title_view_) { + title_lines = title_view_->GetLinesForWidthAndLimit(title_view_->width(), + kMaxTitleLines); + } + if (message_view_) { + message_view_->SetLineLimit( + std::max(0, message_center::kMessageExpandedLineLimit - title_lines)); + } + + // Settings & Bottom views. + if (settings_button_) { + gfx::Rect content_bounds = GetContentsBounds(); + const gfx::Size settings_size(settings_button_->GetPreferredSize()); + int marginFromRight = settings_size.width() + kControlButtonPadding; + if (close_button_) + marginFromRight += close_button_->GetPreferredSize().width(); + gfx::Rect settings_rect(content_bounds.right() - marginFromRight, + GetContentsBounds().y() + kControlButtonPadding, + settings_size.width(), settings_size.height()); + settings_button_->SetBoundsRect(settings_rect); + } + + // Close button. + if (close_button_) { + gfx::Rect content_bounds = GetContentsBounds(); + gfx::Size close_size(close_button_->GetPreferredSize()); + gfx::Rect close_rect( + content_bounds.right() - close_size.width() - kControlButtonPadding, + content_bounds.y() + kControlButtonPadding, close_size.width(), + close_size.height()); + close_button_->SetBoundsRect(close_rect); + } +} + +void NotificationViewMD::OnFocus() { + MessageView::OnFocus(); + ScrollRectToVisible(GetLocalBounds()); +} + +void NotificationViewMD::ScrollRectToVisible(const gfx::Rect& rect) { + // Notification want to show the whole notification when a part of it (like + // a button) gets focused. + views::View::ScrollRectToVisible(GetLocalBounds()); +} + +gfx::NativeCursor NotificationViewMD::GetCursor(const ui::MouseEvent& event) { + if (!clickable_ || !controller()->HasClickedListener(notification_id())) + return views::View::GetCursor(event); + + return views::GetNativeHandCursor(); +} + +void NotificationViewMD::OnMouseEntered(const ui::MouseEvent& event) { + MessageView::OnMouseEntered(event); + UpdateControlButtonsVisibility(); +} + +void NotificationViewMD::OnMouseExited(const ui::MouseEvent& event) { + MessageView::OnMouseExited(event); + UpdateControlButtonsVisibility(); +} + +void NotificationViewMD::UpdateWithNotification( + const Notification& notification) { + MessageView::UpdateWithNotification(notification); + + CreateOrUpdateViews(notification); + Layout(); + SchedulePaint(); +} + +void NotificationViewMD::ButtonPressed(views::Button* sender, + const ui::Event& event) { + // Certain operations can cause |this| to be destructed, so copy the members + // we send to other parts of the code. + // TODO(dewittj): Remove this hack. + std::string id(notification_id()); + + if (close_button_ && sender == close_button_.get()) { + // Warning: This causes the NotificationViewMD itself to be deleted, so + // don't do anything afterwards. + OnCloseButtonPressed(); + return; + } + + if (sender == settings_button_.get()) { + controller()->ClickOnSettingsButton(id); + return; + } + + // See if the button pressed was an action button. + for (size_t i = 0; i < action_buttons_.size(); ++i) { + if (sender == action_buttons_[i]) { + controller()->ClickOnNotificationButton(id, i); + return; + } + } +} + +bool NotificationViewMD::IsCloseButtonFocused() const { + if (!close_button_) + return false; + + const views::FocusManager* focus_manager = GetFocusManager(); + return focus_manager && + focus_manager->GetFocusedView() == close_button_.get(); +} + +void NotificationViewMD::RequestFocusOnCloseButton() { + if (close_button_) + close_button_->RequestFocus(); +} + +void NotificationViewMD::CreateOrUpdateContextTitleView( + const Notification& notification) { + DCHECK(top_view_); + + const gfx::FontList& font_list = views::Label().font_list().Derive( + -2, gfx::Font::NORMAL, gfx::Font::Weight::NORMAL); + + base::string16 sub_title = notification.display_source(); + if (!context_title_view_) { + context_title_view_ = new BoundedLabel(sub_title, font_list); + context_title_view_->SetLineHeight(kTitleLineHeight); + context_title_view_->SetLineLimit(kMaxContextTitleLines); + top_view_->AddChildView(context_title_view_); + } else { + context_title_view_->SetText(sub_title); + } +} + +void NotificationViewMD::CreateOrUpdateTitleView( + const Notification& notification) { + DCHECK(top_view_ != NULL); + + const gfx::FontList& font_list = views::Label().font_list().Derive( + 1, gfx::Font::NORMAL, gfx::Font::Weight::NORMAL); + + int title_character_limit = + kNotificationWidth * kMaxTitleLines / kMinPixelsPerTitleCharacter; + + base::string16 title = gfx::TruncateString( + notification.title(), title_character_limit, gfx::WORD_BREAK); + if (!title_view_) { + title_view_ = new BoundedLabel(title, font_list); + title_view_->SetLineHeight(kMessageLineHeight); + title_view_->SetColors(message_center::kRegularTextColor, + kDimTextBackgroundColor); + main_view_->AddChildView(title_view_); + } else { + title_view_->SetText(title); + } +} + +void NotificationViewMD::CreateOrUpdateMessageView( + const Notification& notification) { + if (notification.message().empty()) { + // Deletion will also remove |context_message_view_| from its parent. + delete message_view_; + message_view_ = nullptr; + return; + } + + DCHECK(top_view_ != NULL); + + base::string16 text = gfx::TruncateString( + notification.message(), kMessageCharacterLimit, gfx::WORD_BREAK); + + const gfx::FontList& font_list = views::Label().font_list().Derive( + 1, gfx::Font::NORMAL, gfx::Font::Weight::NORMAL); + + if (!message_view_) { + message_view_ = new BoundedLabel(text, font_list); + message_view_->SetLineLimit(message_center::kMessageExpandedLineLimit); + message_view_->SetColors(message_center::kDimTextColor, + kContextTextBackgroundColor); + main_view_->AddChildView(message_view_); + } else { + message_view_->SetText(text); + } +} + +void NotificationViewMD::CreateOrUpdateProgressBarView( + const Notification& notification) { + // TODO(yoshiki): Implement this. +} + +void NotificationViewMD::CreateOrUpdateListItemViews( + const Notification& notification) { + // TODO(yoshiki): Implement this. +} + +void NotificationViewMD::CreateOrUpdateIconView( + const Notification& notification) { + // TODO(yoshiki): Implement this. +} + +void NotificationViewMD::CreateOrUpdateSmallIconView( + const Notification& notification) { + gfx::ImageSkia icon = + notification.small_image().IsEmpty() + ? GetProductIcon() + : GetMaskedIcon(notification.small_image().AsImageSkia()); + + small_image_view_->SetImage(icon); +} + +void NotificationViewMD::CreateOrUpdateImageView( + const Notification& notification) { + // TODO(yoshiki): Implement this. +} + +void NotificationViewMD::CreateOrUpdateActionButtonViews( + const Notification& notification) { + // TODO(yoshiki): Implement this. +} + +void NotificationViewMD::CreateOrUpdateCloseButtonView( + const Notification& notification) { + if (!notification.pinned() && !close_button_) { + close_button_ = base::MakeUnique<PaddedButton>(this); + close_button_->SetImage(views::Button::STATE_NORMAL, GetCloseIcon()); + close_button_->SetAccessibleName(l10n_util::GetStringUTF16( + IDS_MESSAGE_CENTER_CLOSE_NOTIFICATION_BUTTON_ACCESSIBLE_NAME)); + close_button_->SetTooltipText(l10n_util::GetStringUTF16( + IDS_MESSAGE_CENTER_CLOSE_NOTIFICATION_BUTTON_TOOLTIP)); + close_button_->set_owned_by_client(); + AddChildView(close_button_.get()); + UpdateControlButtonsVisibility(); + } else if (notification.pinned() && close_button_) { + close_button_.reset(); + } +} + +void NotificationViewMD::CreateOrUpdateSettingsButtonView( + const Notification& notification) { + if (!settings_button_ && notification.delegate() && + notification.delegate()->ShouldDisplaySettingsButton()) { + settings_button_ = base::MakeUnique<PaddedButton>(this); + settings_button_->SetImage(views::Button::STATE_NORMAL, GetSettingsIcon()); + settings_button_->SetAccessibleName(l10n_util::GetStringUTF16( + IDS_MESSAGE_NOTIFICATION_SETTINGS_BUTTON_ACCESSIBLE_NAME)); + settings_button_->SetTooltipText(l10n_util::GetStringUTF16( + IDS_MESSAGE_NOTIFICATION_SETTINGS_BUTTON_ACCESSIBLE_NAME)); + settings_button_->set_owned_by_client(); + AddChildView(settings_button_.get()); + } else { + settings_button_.reset(); + } + UpdateControlButtonsVisibility(); +} + +void NotificationViewMD::UpdateControlButtonsVisibility() { + const bool target_visibility = + IsMouseHovered() || HasFocus() || + (close_button_ && close_button_->HasFocus()) || + (settings_button_ && settings_button_->HasFocus()); + + if (close_button_) { + if (target_visibility != close_button_->visible()) + close_button_->SetVisible(target_visibility); + } + + if (settings_button_) { + if (target_visibility != settings_button_->visible()) + settings_button_->SetVisible(target_visibility); + } +} + +} // namespace message_center
diff --git a/ui/message_center/views/notification_view_md.h b/ui/message_center/views/notification_view_md.h new file mode 100644 index 0000000..c4d76ecf --- /dev/null +++ b/ui/message_center/views/notification_view_md.h
@@ -0,0 +1,104 @@ +// Copyright (c) 2012 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 UI_MESSAGE_CENTER_VIEWS_NOTIFICATION_VIEW_MD_H_ +#define UI_MESSAGE_CENTER_VIEWS_NOTIFICATION_VIEW_MD_H_ + +#include <vector> + +#include "base/gtest_prod_util.h" +#include "base/macros.h" +#include "ui/message_center/message_center_export.h" +#include "ui/message_center/views/message_view.h" +#include "ui/views/controls/button/button.h" +#include "ui/views/controls/button/image_button.h" +#include "ui/views/view_targeter_delegate.h" + +namespace views { +class BoxLayout; +class ImageView; +} + +namespace message_center { + +class BoundedLabel; +class NotificationButton; + +// View that displays all current types of notification (web, basic, image, and +// list) except the custom notification. Future notification types may be +// handled by other classes, in which case instances of those classes would be +// returned by the Create() factory method below. +class MESSAGE_CENTER_EXPORT NotificationViewMD + : public MessageView, + public views::ButtonListener, + public views::ViewTargeterDelegate { + public: + NotificationViewMD(MessageCenterController* controller, + const Notification& notification); + ~NotificationViewMD() override; + + // Overridden from views::View: + void Layout() override; + void OnFocus() override; + void ScrollRectToVisible(const gfx::Rect& rect) override; + gfx::NativeCursor GetCursor(const ui::MouseEvent& event) override; + void OnMouseEntered(const ui::MouseEvent& event) override; + void OnMouseExited(const ui::MouseEvent& event) override; + + // Overridden from MessageView: + void UpdateWithNotification(const Notification& notification) override; + void ButtonPressed(views::Button* sender, const ui::Event& event) override; + bool IsCloseButtonFocused() const override; + void RequestFocusOnCloseButton() override; + void UpdateControlButtonsVisibility() override; + + // views::ViewTargeterDelegate: + views::View* TargetForRect(views::View* root, const gfx::Rect& rect) override; + + private: + void CreateOrUpdateViews(const Notification& notification); + + void CreateOrUpdateContextTitleView(const Notification& notification); + void CreateOrUpdateTitleView(const Notification& notification); + void CreateOrUpdateMessageView(const Notification& notification); + void CreateOrUpdateProgressBarView(const Notification& notification); + void CreateOrUpdateListItemViews(const Notification& notification); + void CreateOrUpdateIconView(const Notification& notification); + void CreateOrUpdateSmallIconView(const Notification& notification); + void CreateOrUpdateImageView(const Notification& notification); + void CreateOrUpdateActionButtonViews(const Notification& notification); + void CreateOrUpdateCloseButtonView(const Notification& notification); + void CreateOrUpdateSettingsButtonView(const Notification& notification); + + // Describes whether the view should display a hand pointer or not. + bool clickable_; + + // Views in the top view + views::BoxLayout* layout_ = nullptr; + + // Views in the top view + views::View* top_view_ = nullptr; + BoundedLabel* context_title_view_ = nullptr; + + // Views in the main view + views::View* main_view_ = nullptr; + BoundedLabel* title_view_ = nullptr; + BoundedLabel* message_view_ = nullptr; + + // Views in the bottom view + views::View* bottom_view_ = nullptr; + + // Views in the floating controller + std::unique_ptr<views::ImageButton> settings_button_; + std::unique_ptr<views::ImageButton> close_button_; + std::unique_ptr<views::ImageView> small_image_view_; + + std::vector<NotificationButton*> action_buttons_; + + DISALLOW_COPY_AND_ASSIGN(NotificationViewMD); +}; + +} // namespace message_center + +#endif // UI_MESSAGE_CENTER_VIEWS_NOTIFICATION_VIEW_MD_H_
diff --git a/ui/views/controls/webview/BUILD.gn b/ui/views/controls/webview/BUILD.gn index 2836c31..b8daeebab 100644 --- a/ui/views/controls/webview/BUILD.gn +++ b/ui/views/controls/webview/BUILD.gn
@@ -8,8 +8,6 @@ "unhandled_keyboard_event_handler.h", "unhandled_keyboard_event_handler_mac.mm", "unhandled_keyboard_event_handler_win.cc", - "web_contents_set_background_color.cc", - "web_contents_set_background_color.h", "web_dialog_view.cc", "web_dialog_view.h", "webview.cc",