diff --git a/DEPS b/DEPS index 9482f5eb..8166e86 100644 --- a/DEPS +++ b/DEPS
@@ -129,11 +129,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': 'be2062c4305f05b4d29239e89dd7ab5108cbb7e1', + 'skia_revision': '3d50730e1246d9350e5fdc3f356cfb235fe9e7e7', # 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': '81260d20b1cfb15752913acb657113c92c6c0026', + 'v8_revision': '6abaac07e38513482384d5014c968d47c1159466', # 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. @@ -141,11 +141,11 @@ # Three lines of non-changing comments so that # the commit queue can handle CLs rolling ANGLE # and whatever else without interference from each other. - 'angle_revision': '639729c3e65e7d4c127bebf5a288ad31918dba8e', + 'angle_revision': '2664da8beb55839f3f50b11c846960b0d15dc4f7', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling SwiftShader # and whatever else without interference from each other. - 'swiftshader_revision': '428c645874c29a1116b775ed73ff780e086a9150', + 'swiftshader_revision': 'f993de30e53967ca65e0ee7d1c232a370515ad91', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling PDFium # and whatever else without interference from each other. @@ -252,7 +252,7 @@ # Three lines of non-changing comments so that # the commit queue can handle CLs rolling feed # and whatever else without interference from each other. - 'spv_tools_revision': '102e430a88dbf15e1863aafe41dfed406e2394fe', + 'spv_tools_revision': '3335c61147d76b9282aaee1a499bb40ca71905ac', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling feed # and whatever else without interference from each other. @@ -268,7 +268,7 @@ # Three lines of non-changing comments so that # the commit queue can handle CLs rolling feed # and whatever else without interference from each other. - 'dawn_revision': 'bff933affcffd6d907c5356237668ba755d4e266', + 'dawn_revision': '2ec74dcc3f75336cfd99a428e5f895cafc03a96f', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling feed # and whatever else without interference from each other. @@ -292,7 +292,7 @@ # Also, if you change these, update buildtools/DEPS too. Also update the # libc++ svn_revision in //buildtools/deps_revisions.gni. 'clang_format_revision': '96636aa0e9f047f17447f2d45a094d0b59ed7917', - 'libcxx_revision': 'fbddc46986100095d5f7ed1bc2bf795d3bb3e9e4', + 'libcxx_revision': '9b96c3dbd4e89c10d9fd8364da4b65f93c6f4276', 'libcxxabi_revision': '0d529660e32d77d9111912d73f2c74fc5fa2a858', 'libunwind_revision': '69d9b84cca8354117b9fe9705a4430d789ee599b', } @@ -805,7 +805,7 @@ # Build tools for Chrome OS. Note: This depends on third_party/pyelftools. 'src/third_party/chromite': { - 'url': Var('chromium_git') + '/chromiumos/chromite.git' + '@' + '0d4500b93834267bf2ba73404c4c944015603b86', + 'url': Var('chromium_git') + '/chromiumos/chromite.git' + '@' + '821ab18888033c8141e33eba48980494a635ff40', 'condition': 'checkout_linux', }, @@ -1183,7 +1183,7 @@ }, 'src/third_party/perfetto': - Var('android_git') + '/platform/external/perfetto.git' + '@' + 'b2622a5caffbd4aae2e7137224471e555bfd1834', + Var('android_git') + '/platform/external/perfetto.git' + '@' + '5b2d700cf0d39328f3b213e17193d08837e8d8ad', 'src/third_party/perl': { 'url': Var('chromium_git') + '/chromium/deps/perl.git' + '@' + 'ac0d98b5cee6c024b0cffeb4f8f45b6fc5ccdb78', @@ -1256,7 +1256,7 @@ 'packages': [ { 'package': 'chromium/third_party/r8', - 'version': 'SlcbUnEufAQ-iuOwGOl8yYQuctmpf7bMqh59kBfpil0C', + 'version': 'BReCwfbVwCNM2Ry4QpnrwlE3Y5gPJ2rRoyMbxFS0-4UC', }, ], 'condition': 'checkout_android', @@ -1354,7 +1354,7 @@ Var('chromium_git') + '/external/khronosgroup/webgl.git' + '@' + '3f6583d3fee4ab71866ade794504a20eb6f63f88', 'src/third_party/webrtc': - Var('webrtc_git') + '/src.git' + '@' + 'ff7730d2ba2b1d2fcf9ed55f51f20b8256d9c31d', + Var('webrtc_git') + '/src.git' + '@' + '2f92b414aeb96a5db181195a7bc0512cdae18dbd', 'src/third_party/xdg-utils': { 'url': Var('chromium_git') + '/chromium/deps/xdg-utils.git' + '@' + 'd80274d5869b17b8c9067a1022e4416ee7ed5e0d', @@ -1395,7 +1395,7 @@ Var('chromium_git') + '/v8/v8.git' + '@' + Var('v8_revision'), 'src-internal': { - 'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@e33c9cf2e4ee04c6ad5adb2c7a4de0dc626fe5fe', + 'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@2f94c859caf2b1a47c8c5441539ea1a58599c8ec', 'condition': 'checkout_src_internal', },
diff --git a/WATCHLISTS b/WATCHLISTS index d12491a47..dce208c 100644 --- a/WATCHLISTS +++ b/WATCHLISTS
@@ -1766,12 +1766,13 @@ 'webauthn': { 'filepath': 'chrome/android/java/src/org/chromium/chrome/browser/webauth/'\ '|chrome/android/javatests/src/org/chromium/chrome/browser/webauth/'\ - '|chrome/browser/webauthn/'\ '|chrome/browser/ui/webauthn/'\ '|chrome/browser/ui/views/webauthn/'\ + '|chrome/browser/webauthn/'\ '|content/browser/webauth/'\ '|device/fido/'\ - '|third_party/blink/public/platform/modules/webauth/', + '|third_party/blink/public/mojom/webauthn/'\ + '|third_party/microsoft_webauthn/', }, 'webgpu': { 'filepath': 'third_party/blink/renderer/modules/webgpu/',
diff --git a/android_webview/browser/gfx/hardware_renderer.cc b/android_webview/browser/gfx/hardware_renderer.cc index a3b2503..d00b6ef 100644 --- a/android_webview/browser/gfx/hardware_renderer.cc +++ b/android_webview/browser/gfx/hardware_renderer.cc
@@ -84,8 +84,9 @@ child_frame_queue_.emplace_back(std::move(child_frames.front())); } -void HardwareRenderer::DrawGL(HardwareRendererDrawParams* params) { - TRACE_EVENT0("android_webview", "HardwareRenderer::DrawGL"); +void HardwareRenderer::Draw(HardwareRendererDrawParams* params) { + TRACE_EVENT1("android_webview", "HardwareRenderer::Draw", "vulkan", + surfaces_->is_using_vulkan()); for (auto& pruned_frame : WaitAndPruneFrameQueue(&child_frame_queue_)) ReturnChildFrame(std::move(pruned_frame)); @@ -103,7 +104,7 @@ // We need to watch if the current Android context has changed and enforce a // clean-up in the compositor. EGLContext current_context = eglGetCurrentContext(); - DCHECK(current_context) << "DrawGL called without EGLContext"; + DCHECK(current_context) << "Draw called without EGLContext"; // TODO(boliu): Handle context loss. if (last_egl_context_ != current_context)
diff --git a/android_webview/browser/gfx/hardware_renderer.h b/android_webview/browser/gfx/hardware_renderer.h index 3c2849a7..13b3bbf9 100644 --- a/android_webview/browser/gfx/hardware_renderer.h +++ b/android_webview/browser/gfx/hardware_renderer.h
@@ -57,7 +57,7 @@ explicit HardwareRenderer(RenderThreadManager* state); ~HardwareRenderer() override; - void DrawGL(HardwareRendererDrawParams* params); + void Draw(HardwareRendererDrawParams* params); void CommitFrame(); private:
diff --git a/android_webview/browser/gfx/render_thread_manager.cc b/android_webview/browser/gfx/render_thread_manager.cc index eca5496..14ddbf2 100644 --- a/android_webview/browser/gfx/render_thread_manager.cc +++ b/android_webview/browser/gfx/render_thread_manager.cc
@@ -192,7 +192,7 @@ } if (hardware_renderer_) - hardware_renderer_->DrawGL(params); + hardware_renderer_->Draw(params); } void RenderThreadManager::DestroyHardwareRendererOnRT(bool save_restore) {
diff --git a/android_webview/glue/java/src/com/android/webview/chromium/WebViewChromiumAwInit.java b/android_webview/glue/java/src/com/android/webview/chromium/WebViewChromiumAwInit.java index 025ec3a..a7423173 100644 --- a/android_webview/glue/java/src/com/android/webview/chromium/WebViewChromiumAwInit.java +++ b/android_webview/glue/java/src/com/android/webview/chromium/WebViewChromiumAwInit.java
@@ -39,6 +39,7 @@ import org.chromium.base.BuildInfo; import org.chromium.base.ContextUtils; import org.chromium.base.FieldTrialList; +import org.chromium.base.JNIUtils; import org.chromium.base.PathService; import org.chromium.base.ThreadUtils; import org.chromium.base.TraceEvent; @@ -134,6 +135,8 @@ final Context context = ContextUtils.getApplicationContext(); + JNIUtils.setClassLoader(WebViewChromiumAwInit.class.getClassLoader()); + // We are rewriting Java resources in the background. // NOTE: Any reference to Java resources will cause a crash.
diff --git a/android_webview/java/src/org/chromium/android_webview/AwContents.java b/android_webview/java/src/org/chromium/android_webview/AwContents.java index 3ab4f14..6da0701c 100644 --- a/android_webview/java/src/org/chromium/android_webview/AwContents.java +++ b/android_webview/java/src/org/chromium/android_webview/AwContents.java
@@ -3219,7 +3219,7 @@ @CalledByNative private void updateScrollState(int maxContainerViewScrollOffsetX, - int maxContainerViewScrollOffsetY, int contentWidthDip, int contentHeightDip, + int maxContainerViewScrollOffsetY, float contentWidthDip, float contentHeightDip, float pageScaleFactor, float minPageScaleFactor, float maxPageScaleFactor) { mContentWidthDip = contentWidthDip; mContentHeightDip = contentHeightDip;
diff --git a/ash/app_list/app_list_controller_impl.cc b/ash/app_list/app_list_controller_impl.cc index cba5aad4..27917b7 100644 --- a/ash/app_list/app_list_controller_impl.cc +++ b/ash/app_list/app_list_controller_impl.cc
@@ -665,6 +665,7 @@ void AppListControllerImpl::OnHomeLauncherAnimationComplete( bool shown, int64_t display_id) { + ResetHomeLauncherIfShown(); CloseAssistantUi(shown ? AssistantExitPoint::kLauncherOpen : AssistantExitPoint::kLauncherClose); } @@ -1154,7 +1155,8 @@ auto* const keyboard_controller = keyboard::KeyboardController::Get(); if (keyboard_controller->IsKeyboardVisible()) keyboard_controller->HideKeyboardByUser(); - presenter_.GetView()->CloseOpenedPage(); + + presenter_.GetView()->ResetForHomeLauncherShow(); // Refresh the suggestion chips with empty query. StartSearch(base::string16());
diff --git a/ash/app_list/views/app_list_view.cc b/ash/app_list/views/app_list_view.cc index 05d2da1..78c260ed 100644 --- a/ash/app_list/views/app_list_view.cc +++ b/ash/app_list/views/app_list_view.cc
@@ -404,6 +404,11 @@ RecordFolderMetrics(); } +void AppListView::ResetForHomeLauncherShow() { + GetInitiallyFocusedView()->RequestFocus(); + CloseOpenedPage(); +} + void AppListView::SetDragAndDropHostOfCurrentAppList( ApplicationDragAndDropHost* drag_and_drop_host) { app_list_main_view_->SetDragAndDropHostOfCurrentAppList(drag_and_drop_host);
diff --git a/ash/app_list/views/app_list_view.h b/ash/app_list/views/app_list_view.h index 922a947..e1a6672 100644 --- a/ash/app_list/views/app_list_view.h +++ b/ash/app_list/views/app_list_view.h
@@ -133,6 +133,9 @@ // fullscreen app list feature is set. void Initialize(const InitParams& params); + // Resets AppListView to be re-shown after being dismissed. + void ResetForHomeLauncherShow(); + // If |drag_and_drop_host| is not NULL it will be called upon drag and drop // operations outside the application list. This has to be called after // Initialize was called since the app list object needs to exist so that
diff --git a/ash/media/media_notification_background.cc b/ash/media/media_notification_background.cc index 5fe4f5a..1a8a2a3 100644 --- a/ash/media/media_notification_background.cc +++ b/ash/media/media_notification_background.cc
@@ -12,6 +12,7 @@ #include "ui/gfx/color_analysis.h" #include "ui/gfx/color_utils.h" #include "ui/gfx/scoped_canvas.h" +#include "ui/views/style/typography.h" #include "ui/views/view.h" namespace ash { @@ -337,6 +338,17 @@ owner_->SchedulePaint(); } +SkColor MediaNotificationBackground::GetBackgroundColor() const { + return background_color_.value_or(kMediaNotificationDefaultBackgroundColor); +} + +SkColor MediaNotificationBackground::GetForegroundColor() const { + return color_utils::GetColorWithMinimumContrast( + foreground_color_.value_or(views::style::GetColor( + *owner_, views::style::CONTEXT_LABEL, views::style::STYLE_PRIMARY)), + GetBackgroundColor()); +} + int MediaNotificationBackground::GetArtworkWidth( const gfx::Size& view_size) const { if (artwork_.isNull())
diff --git a/ash/media/media_notification_background.h b/ash/media/media_notification_background.h index 74fbc92d..6f9245a 100644 --- a/ash/media/media_notification_background.h +++ b/ash/media/media_notification_background.h
@@ -41,6 +41,9 @@ void UpdateArtwork(const gfx::ImageSkia& image); void UpdateArtworkMaxWidthPct(double max_width_pct); + SkColor GetBackgroundColor() const; + SkColor GetForegroundColor() const; + private: friend class MediaNotificationBackgroundTest; friend class MediaNotificationViewTest;
diff --git a/ash/media/media_notification_view.cc b/ash/media/media_notification_view.cc index ace6fe00..6fc17513 100644 --- a/ash/media/media_notification_view.cc +++ b/ash/media/media_notification_view.cc
@@ -62,16 +62,34 @@ MediaSessionAction::kSeekBackward, MediaSessionAction::kSeekForward, }; -SkColor GetMediaNotificationColor(const views::View& view) { - return views::style::GetColor(view, views::style::CONTEXT_LABEL, - views::style::STYLE_PRIMARY); -} - void RecordMetadataHistogram(MediaNotificationView::Metadata metadata) { UMA_HISTOGRAM_ENUMERATION(MediaNotificationView::kMetadataHistogramName, metadata); } +const gfx::VectorIcon* GetVectorIconForMediaAction(MediaSessionAction action) { + switch (action) { + case MediaSessionAction::kPreviousTrack: + return &vector_icons::kMediaPreviousTrackIcon; + case MediaSessionAction::kSeekBackward: + return &vector_icons::kMediaSeekBackwardIcon; + case MediaSessionAction::kPlay: + return &vector_icons::kPlayArrowIcon; + case MediaSessionAction::kPause: + return &vector_icons::kPauseIcon; + case MediaSessionAction::kSeekForward: + return &vector_icons::kMediaSeekForwardIcon; + case MediaSessionAction::kNextTrack: + return &vector_icons::kMediaNextTrackIcon; + case MediaSessionAction::kStop: + case MediaSessionAction::kSkipAd: + NOTREACHED(); + break; + } + + return nullptr; +} + } // namespace // static @@ -147,33 +165,23 @@ views::BoxLayout::CROSS_AXIS_ALIGNMENT_CENTER); main_row_->AddChildView(button_row_); - CreateMediaButton(vector_icons::kMediaPreviousTrackIcon, - MediaSessionAction::kPreviousTrack); - CreateMediaButton(vector_icons::kMediaSeekBackwardIcon, - MediaSessionAction::kSeekBackward); + CreateMediaButton(MediaSessionAction::kPreviousTrack); + CreateMediaButton(MediaSessionAction::kSeekBackward); // |play_pause_button_| toggles playback. play_pause_button_ = views::CreateVectorToggleImageButton(this); play_pause_button_->set_tag(static_cast<int>(MediaSessionAction::kPlay)); play_pause_button_->SetPreferredSize(kMediaButtonSize); - SkColor play_button_color = GetMediaNotificationColor(*play_pause_button_); - views::SetImageFromVectorIcon(play_pause_button_, - vector_icons::kPlayArrowIcon, - kMediaButtonIconSize, play_button_color); - views::SetToggledImageFromVectorIcon(play_pause_button_, - vector_icons::kPauseIcon, - kMediaButtonIconSize, play_button_color); button_row_->AddChildView(play_pause_button_); - CreateMediaButton(vector_icons::kMediaSeekForwardIcon, - MediaSessionAction::kSeekForward); - CreateMediaButton(vector_icons::kMediaNextTrackIcon, - MediaSessionAction::kNextTrack); + CreateMediaButton(MediaSessionAction::kSeekForward); + CreateMediaButton(MediaSessionAction::kNextTrack); SetBackground(std::make_unique<MediaNotificationBackground>( this, message_center::kNotificationCornerRadius, message_center::kNotificationCornerRadius, kMediaImageMaxWidthPct)); + UpdateForegroundColor(); UpdateControlButtonsVisibilityWithNotification(notification); UpdateCornerRadius(message_center::kNotificationCornerRadius, message_center::kNotificationCornerRadius); @@ -318,6 +326,8 @@ UMA_HISTOGRAM_BOOLEAN(kArtworkHistogramName, has_artwork_); + UpdateForegroundColor(); + PreferredSizeChanged(); Layout(); SchedulePaint(); @@ -390,12 +400,9 @@ UpdateActionButtonsVisibility(); } -void MediaNotificationView::CreateMediaButton(const gfx::VectorIcon& icon, - MediaSessionAction action) { +void MediaNotificationView::CreateMediaButton(MediaSessionAction action) { views::ImageButton* button = views::CreateVectorImageButton(this); button->set_tag(static_cast<int>(action)); - views::SetImageFromVectorIcon(button, icon, kMediaButtonIconSize, - GetMediaNotificationColor(*button)); button->SetPreferredSize(kMediaButtonSize); button_row_->AddChildView(button); } @@ -442,4 +449,52 @@ return visible_actions; } +void MediaNotificationView::UpdateForegroundColor() { + const SkColor background = + GetMediaNotificationBackground()->GetBackgroundColor(); + const SkColor foreground = + GetMediaNotificationBackground()->GetForegroundColor(); + + title_label_->SetEnabledColor(foreground); + artist_label_->SetEnabledColor(foreground); + header_row_->SetAccentColor(foreground); + + title_label_->SetBackgroundColor(background); + artist_label_->SetBackgroundColor(background); + header_row_->SetBackgroundColor(background); + + // Update play/pause button images. + views::SetImageFromVectorIcon( + play_pause_button_, + *GetVectorIconForMediaAction(MediaSessionAction::kPlay), + kMediaButtonIconSize, foreground); + views::SetToggledImageFromVectorIcon( + play_pause_button_, + *GetVectorIconForMediaAction(MediaSessionAction::kPause), + kMediaButtonIconSize, foreground); + + // Update action buttons. + for (int i = 0; i < button_row_->child_count(); ++i) { + views::View* child = button_row_->child_at(i); + + // Skip the play pause button since it is a special case. + if (child == play_pause_button_) + continue; + + // Skip if the view is not an image button. + if (child->GetClassName() != views::ImageButton::kViewClassName) + continue; + + views::ImageButton* button = static_cast<views::ImageButton*>(child); + + views::SetImageFromVectorIcon( + button, + *GetVectorIconForMediaAction( + static_cast<MediaSessionAction>(button->tag())), + kMediaButtonIconSize, foreground); + + button->SchedulePaint(); + } +} + } // namespace ash
diff --git a/ash/media/media_notification_view.h b/ash/media/media_notification_view.h index e985d09..19539293 100644 --- a/ash/media/media_notification_view.h +++ b/ash/media/media_notification_view.h
@@ -95,10 +95,9 @@ void UpdateControlButtonsVisibilityWithNotification( const message_center::Notification& notification); - // Creates an image button with |icon| and adds it to |button_row_|. When - // clicked it will trigger |action| on the sesssion. - void CreateMediaButton(const gfx::VectorIcon& icon, - media_session::mojom::MediaSessionAction action); + // Creates an image button with an icon that matches |action| and adds it + // to |button_row_|. When clicked it will trigger |action| on the session. + void CreateMediaButton(media_session::mojom::MediaSessionAction action); void UpdateActionButtonsVisibility(); void UpdateViewForExpandedState(); @@ -111,6 +110,8 @@ std::set<media_session::mojom::MediaSessionAction> CalculateVisibleActions( bool expanded) const; + void UpdateForegroundColor(); + // View containing close and settings buttons. std::unique_ptr<message_center::NotificationControlButtonsView> control_buttons_view_;
diff --git a/ash/media/media_notification_view_unittest.cc b/ash/media/media_notification_view_unittest.cc index f0785b0..5d90f707 100644 --- a/ash/media/media_notification_view_unittest.cc +++ b/ash/media/media_notification_view_unittest.cc
@@ -675,11 +675,12 @@ TEST_F(MediaNotificationViewTest, UpdateArtworkFromItem) { int title_artist_width = title_artist_row()->width(); + const SkColor accent = header_row()->accent_color_for_testing(); gfx::Size size = view()->size(); SkBitmap image; image.allocN32Pixels(10, 10); - image.eraseColor(SK_ColorWHITE); + image.eraseColor(SK_ColorMAGENTA); EXPECT_TRUE(GetArtworkImage().isNull()); @@ -697,6 +698,7 @@ EXPECT_FALSE(GetArtworkImage().isNull()); EXPECT_EQ(gfx::Size(10, 10), GetArtworkImage().size()); EXPECT_EQ(size, view()->size()); + EXPECT_NE(accent, header_row()->accent_color_for_testing()); GetItem()->MediaControllerImageChanged( media_session::mojom::MediaSessionImageType::kArtwork, SkBitmap()); @@ -711,6 +713,7 @@ // affected. EXPECT_TRUE(GetArtworkImage().isNull()); EXPECT_EQ(size, view()->size()); + EXPECT_EQ(accent, header_row()->accent_color_for_testing()); } TEST_F(MediaNotificationViewTest, UpdateIconFromItem) {
diff --git a/ash/wm/overview/overview_session.cc b/ash/wm/overview/overview_session.cc index c8622b9..eb5f904d 100644 --- a/ash/wm/overview/overview_session.cc +++ b/ash/wm/overview/overview_session.cc
@@ -532,6 +532,8 @@ void OverviewSession::ResetDraggedWindowGesture() { window_drag_controller_->ResetGesture(); + for (std::unique_ptr<OverviewGrid>& grid : grid_list_) + grid->OnSelectorItemDragEnded(); } void OverviewSession::OnWindowDragStarted(aura::Window* dragged_window,
diff --git a/base/android/java/src/org/chromium/base/JNIUtils.java b/base/android/java/src/org/chromium/base/JNIUtils.java index 3fcec91..1b53b9f06 100644 --- a/base/android/java/src/org/chromium/base/JNIUtils.java +++ b/base/android/java/src/org/chromium/base/JNIUtils.java
@@ -13,6 +13,7 @@ @MainDex public class JNIUtils { private static Boolean sSelectiveJniRegistrationEnabled; + private static ClassLoader sJniClassLoader; /** * This returns a ClassLoader that is capable of loading Chromium Java code. Such a ClassLoader @@ -21,7 +22,18 @@ */ @CalledByNative public static Object getClassLoader() { - return JNIUtils.class.getClassLoader(); + if (sJniClassLoader == null) { + return JNIUtils.class.getClassLoader(); + } + return sJniClassLoader; + } + + /** + * Sets the ClassLoader to be used for loading Java classes from native. + * @param classLoader the ClassLoader to use. + */ + public static void setClassLoader(ClassLoader classLoader) { + sJniClassLoader = classLoader; } /**
diff --git a/base/win/pe_image_test.cc b/base/win/pe_image_test.cc index 8591495..3ce402f 100644 --- a/base/win/pe_image_test.cc +++ b/base/win/pe_image_test.cc
@@ -5,7 +5,7 @@ #include <windows.h> #include <cfgmgr32.h> -#include <shellapi.h> +#include <shlobj.h> #pragma comment(linker, "/export:FwdExport=KERNEL32.CreateFileA") @@ -22,8 +22,9 @@ CM_MapCrToWin32Err(CR_SUCCESS, ERROR_SUCCESS); // Call into shell32.dll. - SHFILEOPSTRUCT file_operation = {0}; - SHFileOperation(&file_operation); + PWSTR path = nullptr; + if (SUCCEEDED(SHGetKnownFolderPath(FOLDERID_Public, 0, nullptr, &path))) + CoTaskMemFree(path); // Call into kernel32.dll. HANDLE h = CreateEvent(NULL, FALSE, FALSE, NULL);
diff --git a/build/android/apk_operations.py b/build/android/apk_operations.py index e6ee0ab..91f6851c 100755 --- a/build/android/apk_operations.py +++ b/build/android/apk_operations.py
@@ -1132,6 +1132,25 @@ print _GenerateAvailableDevicesMessage(self.devices) +class _PackageInfoCommand(_Command): + name = 'package-info' + # TODO(ntfschr): Support this by figuring out how to construct + # self.apk_helper for bundles (http://crbug.com/952443). + description = 'Show various attributes of this APK.' + need_device_args = False + needs_package_name = True + needs_apk_path = True + + def Run(self): + # Format all (even ints) as strings, to handle cases where APIs return None + print 'Package name: "%s"' % self.args.package_name + print 'versionCode: %s' % self.apk_helper.GetVersionCode() + print 'versionName: "%s"' % self.apk_helper.GetVersionName() + print 'minSdkVersion: %s' % self.apk_helper.GetMinSdkVersion() + print 'targetSdkVersion: "%s"' % self.apk_helper.GetTargetSdkVersion() + print 'Supported ABIs: %r' % self.apk_helper.GetAbis() + + class _InstallCommand(_Command): name = 'install' description = 'Installs the APK or bundle to one or more devices.' @@ -1184,7 +1203,7 @@ def Run(self): if self.is_bundle: # TODO(ntfschr): Support this by figuring out how to construct - # self.apk_helper for bundles. + # self.apk_helper for bundles (http://crbug.com/952443). raise Exception( 'Switching WebView providers not supported for bundles yet!') if not _IsWebViewProvider(self.apk_helper): @@ -1532,6 +1551,7 @@ # Shared commands for regular APKs and app bundles. _COMMANDS = [ _DevicesCommand, + _PackageInfoCommand, _InstallCommand, _UninstallCommand, _SetWebViewProviderCommand,
diff --git a/build/android/gyp/write_build_config.py b/build/android/gyp/write_build_config.py index c5f7cc8c..6dd2018 100755 --- a/build/android/gyp/write_build_config.py +++ b/build/android/gyp/write_build_config.py
@@ -1313,12 +1313,12 @@ # Deps to add to the compile-time classpath (but not the runtime classpath). # TODO(agrieve): Might be less confusing to fold these into bootclasspath. - javac_extra_jars = [c['unprocessed_jar_path'] - for c in classpath_deps.Direct('java_library')] - extra_jars = [c['jar_path'] - for c in classpath_deps.Direct('java_library')] + javac_extra_jars = [ + c['unprocessed_jar_path'] for c in classpath_deps.All('java_library') + ] + extra_jars = [c['jar_path'] for c in classpath_deps.All('java_library')] interface_extra_jars = [ - c['interface_jar_path'] for c in classpath_deps.Direct('java_library') + c['interface_jar_path'] for c in classpath_deps.All('java_library') ] # These are jars specified by input_jars_paths that almost never change.
diff --git a/buildtools/DEPS b/buildtools/DEPS index dc241c18..55313b8 100644 --- a/buildtools/DEPS +++ b/buildtools/DEPS
@@ -18,7 +18,7 @@ # When changing these, also update the svn revisions in deps_revisions.gni 'clang_format_revision': '96636aa0e9f047f17447f2d45a094d0b59ed7917', - 'libcxx_revision': 'fbddc46986100095d5f7ed1bc2bf795d3bb3e9e4', + 'libcxx_revision': '9b96c3dbd4e89c10d9fd8364da4b65f93c6f4276', 'libcxxabi_revision': '0d529660e32d77d9111912d73f2c74fc5fa2a858', 'libunwind_revision': '69d9b84cca8354117b9fe9705a4430d789ee599b', }
diff --git a/buildtools/deps_revisions.gni b/buildtools/deps_revisions.gni index fd541ec..5b6c512 100644 --- a/buildtools/deps_revisions.gni +++ b/buildtools/deps_revisions.gni
@@ -5,5 +5,5 @@ declare_args() { # The libc++ svn revision that belongs to the git hash in DEPS. Used to cause # full rebuilds on libc++ rolls. - libcxx_svn_revision = "357619" + libcxx_svn_revision = "358423" }
diff --git a/cc/paint/oop_pixeltest.cc b/cc/paint/oop_pixeltest.cc index e4d6e3f..c1cf6f7 100644 --- a/cc/paint/oop_pixeltest.cc +++ b/cc/paint/oop_pixeltest.cc
@@ -315,7 +315,7 @@ raster_source->PlaybackToCanvas( canvas, options.content_size, options.full_raster_rect, options.playback_rect, raster_transform, settings); - surface->prepareForExternalIO(); + surface->flush(); EXPECT_EQ(gles2_context_provider_->ContextGL()->GetError(), static_cast<unsigned>(GL_NO_ERROR));
diff --git a/cc/tiles/gpu_image_decode_cache_perftest.cc b/cc/tiles/gpu_image_decode_cache_perftest.cc index 5d0f2e6..f7d485e 100644 --- a/cc/tiles/gpu_image_decode_cache_perftest.cc +++ b/cc/tiles/gpu_image_decode_cache_perftest.cc
@@ -151,7 +151,7 @@ surface->getCanvas()->drawImageRect(decoded_image.image().get(), SkRect::MakeWH(1024, 2048), SkRect::MakeWH(614, 1229), &paint); - surface->prepareForExternalIO(); + surface->flush(); } cache_->DrawWithImageFinished(image, decoded_image);
diff --git a/cc/tiles/paint_worklet_image_cache.cc b/cc/tiles/paint_worklet_image_cache.cc index 7d6cc848..639bf7d1 100644 --- a/cc/tiles/paint_worklet_image_cache.cc +++ b/cc/tiles/paint_worklet_image_cache.cc
@@ -89,11 +89,10 @@ std::pair<sk_sp<PaintRecord>, base::OnceCallback<void()>> PaintWorkletImageCache::GetPaintRecordAndRef(PaintWorkletInput* input) { base::AutoLock hold(records_lock_); - // If the |painter_| is null, then GetTaskForPaintWorkletImage will return a - // null TileTask, and hence there will be no cache entry for this input. - if (!painter_) + // If the |painter_| was null when GetTaskForPaintWorkletImage was called + // there will be no cache entry for this input. + if (records_.find(input) == records_.end()) return std::make_pair(sk_make_sp<PaintOpBuffer>(), base::DoNothing::Once()); - DCHECK(records_.find(input) != records_.end()); records_[input].used_ref_count++; records_[input].num_of_frames_not_accessed = 0u; // The PaintWorkletImageCache object lives as long as the LayerTreeHostImpl,
diff --git a/cc/tiles/paint_worklet_image_cache_unittest.cc b/cc/tiles/paint_worklet_image_cache_unittest.cc index 4a26f40..6ae3c0a 100644 --- a/cc/tiles/paint_worklet_image_cache_unittest.cc +++ b/cc/tiles/paint_worklet_image_cache_unittest.cc
@@ -265,8 +265,14 @@ EXPECT_EQ(task, nullptr); } -TEST(PaintWorkletImageCacheTest, RecordAndCallbackAreEmptyWhenPainterIsNull) { +TEST(PaintWorkletImageCacheTest, + RecordAndCallbackAreEmptyWhenInputWasntPainted) { TestPaintWorkletImageCache cache; + std::unique_ptr<TestPaintWorkletLayerPainter> painter = + std::make_unique<TestPaintWorkletLayerPainter>(); + cache.SetPaintWorkletLayerPainter(std::move(painter)); + + // We request a record and callback without ever painting the input. PaintImage paint_image = CreatePaintImage(100, 100); std::pair<sk_sp<PaintRecord>, base::OnceCallback<void()>> result = cache.GetPaintRecordAndRef(paint_image.paint_worklet_input());
diff --git a/chrome/android/BUILD.gn b/chrome/android/BUILD.gn index b3ed12a..396a871 100644 --- a/chrome/android/BUILD.gn +++ b/chrome/android/BUILD.gn
@@ -2060,7 +2060,7 @@ if (_is_trichrome) { if (defined(invoker.is_64_bit_browser) && invoker.is_64_bit_browser) { - if (invoker.build_apk_secondary_abi && invoker.include_32_bit_webview) { + if (build_apk_secondary_abi && invoker.include_32_bit_webview) { _version_code = trichrome_64_32_version_code } else { _version_code = trichrome_64_version_code @@ -2070,7 +2070,7 @@ } } else { if (defined(invoker.is_64_bit_browser) && invoker.is_64_bit_browser) { - if (invoker.build_apk_secondary_abi && invoker.include_32_bit_webview) { + if (build_apk_secondary_abi && invoker.include_32_bit_webview) { _version_code = monochrome_64_32_version_code } else { _version_code = monochrome_64_version_code @@ -2215,7 +2215,7 @@ } } - monochrome_or_trichrome_public_bundle_tmpl("trichrome_64_chrome_bundle") { + monochrome_or_trichrome_public_bundle_tmpl("trichrome_chrome_64_bundle") { bundle_suffix = "64" is_64_bit_browser = true use_trichrome_library = true @@ -2224,7 +2224,7 @@ } } - monochrome_or_trichrome_public_bundle_tmpl("trichrome_64_32_chrome_bundle") { + monochrome_or_trichrome_public_bundle_tmpl("trichrome_chrome_64_32_bundle") { bundle_suffix = "6432" is_64_bit_browser = true use_trichrome_library = true
diff --git a/chrome/android/chrome_java_sources.gni b/chrome/android/chrome_java_sources.gni index 307c040..385b518 100644 --- a/chrome/android/chrome_java_sources.gni +++ b/chrome/android/chrome_java_sources.gni
@@ -1596,6 +1596,8 @@ "java/src/org/chromium/chrome/browser/tabmodel/document/StorageDelegate.java", "java/src/org/chromium/chrome/browser/tabmodel/document/TabDelegate.java", "java/src/org/chromium/chrome/browser/tasks/ReturnToChromeExperimentsUtil.java", + "java/src/org/chromium/chrome/browser/tasks/EngagementTimeUtil.java", + "java/src/org/chromium/chrome/browser/tasks/JourneyManager.java", "java/src/org/chromium/chrome/browser/tasks/TasksUma.java", "java/src/org/chromium/chrome/browser/tasks/tabgroup/TabGroupModelFilter.java", "java/src/org/chromium/chrome/browser/tasks/tab_groups/LayoutTabGroupCreationButton.java",
diff --git a/chrome/android/chrome_junit_test_java_sources.gni b/chrome/android/chrome_junit_test_java_sources.gni index 73ab714..c5c662a 100644 --- a/chrome/android/chrome_junit_test_java_sources.gni +++ b/chrome/android/chrome_junit_test_java_sources.gni
@@ -179,6 +179,7 @@ "junit/src/org/chromium/chrome/browser/tab/TabAttributesTest.java", "junit/src/org/chromium/chrome/browser/tabmodel/TabPersistentStoreUnitTest.java", "junit/src/org/chromium/chrome/browser/tabstate/TabStateUnitTest.java", + "junit/src/org/chromium/chrome/browser/tasks/JourneyManagerTest.java", "junit/src/org/chromium/chrome/browser/toolbar/ToolbarSecurityIconTest.java", "junit/src/org/chromium/chrome/browser/usage_stats/EventTrackerTest.java", "junit/src/org/chromium/chrome/browser/usage_stats/PageViewObserverTest.java",
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridContainerViewBinder.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridContainerViewBinder.java index 79b5f6d..66fe798 100644 --- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridContainerViewBinder.java +++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridContainerViewBinder.java
@@ -12,6 +12,7 @@ import static org.chromium.chrome.browser.tasks.tab_management.TabListContainerProperties.TOP_CONTROLS_HEIGHT; import static org.chromium.chrome.browser.tasks.tab_management.TabListContainerProperties.VISIBILITY_LISTENER; +import android.support.v7.widget.GridLayoutManager; import android.widget.FrameLayout; import org.chromium.chrome.browser.util.ColorUtils; @@ -42,7 +43,9 @@ } else if (VISIBILITY_LISTENER == propertyKey) { view.setVisibilityListener(model.get(VISIBILITY_LISTENER)); } else if (INITIAL_SCROLL_INDEX == propertyKey) { - view.scrollToPosition(model.get(INITIAL_SCROLL_INDEX)); + // recyclerview.scrollToPosition() behaves incorrectly after cold start. + ((GridLayoutManager) view.getLayoutManager()) + .scrollToPositionWithOffset(model.get(INITIAL_SCROLL_INDEX), 0); } else if (TOP_CONTROLS_HEIGHT == propertyKey) { FrameLayout.LayoutParams params = (FrameLayout.LayoutParams) view.getLayoutParams(); params.topMargin = model.get(TOP_CONTROLS_HEIGHT);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ChromeActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/ChromeActivity.java index 0d6a74e..9fb74bb 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/ChromeActivity.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/ChromeActivity.java
@@ -144,6 +144,8 @@ import org.chromium.chrome.browser.tabmodel.TabModelUtils; import org.chromium.chrome.browser.tabmodel.TabSelectionType; import org.chromium.chrome.browser.tabmodel.TabWindowManager; +import org.chromium.chrome.browser.tasks.EngagementTimeUtil; +import org.chromium.chrome.browser.tasks.JourneyManager; import org.chromium.chrome.browser.toolbar.ToolbarManager; import org.chromium.chrome.browser.toolbar.top.Toolbar; import org.chromium.chrome.browser.toolbar.top.ToolbarControlContainer; @@ -1503,6 +1505,12 @@ getCompositorViewHolder().setKeyboardExtensionView( mManualFillingComponent.getKeyboardExtensionSizeManager()); + if (ChromeFeatureList.isEnabled(ChromeFeatureList.TAB_ENGAGEMENT_REPORTING_ANDROID)) { + // The lifetime of this object is managed by the lifecycle dispatcher. + new JourneyManager( + mTabModelSelector, getLifecycleDispatcher(), new EngagementTimeUtil()); + } + // Create after native initialization so subclasses that override this method have a chance // to setup. mPageViewTimer = createPageViewTimer();
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ChromeApplication.java b/chrome/android/java/src/org/chromium/chrome/browser/ChromeApplication.java index 5a454a4..36cef232 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/ChromeApplication.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/ChromeApplication.java
@@ -20,6 +20,7 @@ import org.chromium.base.CommandLineInitUtil; import org.chromium.base.ContextUtils; import org.chromium.base.DiscardableReferencePool; +import org.chromium.base.JNIUtils; import org.chromium.base.Log; import org.chromium.base.TraceEvent; import org.chromium.base.annotations.MainDex; @@ -134,6 +135,7 @@ } } AsyncTask.takeOverAndroidThreadPool(); + JNIUtils.setClassLoader(getClassLoader()); } private static Boolean shouldUseDebugFlags() {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ChromeFeatureList.java b/chrome/android/java/src/org/chromium/chrome/browser/ChromeFeatureList.java index e74fb21..b8c26ef 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/ChromeFeatureList.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/ChromeFeatureList.java
@@ -312,6 +312,7 @@ public static final String SPANNABLE_INLINE_AUTOCOMPLETE = "SpannableInlineAutocomplete"; public static final String SUBRESOURCE_FILTER = "SubresourceFilter"; public static final String QUERY_IN_OMNIBOX = "QueryInOmnibox"; + public static final String TAB_ENGAGEMENT_REPORTING_ANDROID = "TabEngagementReportingAndroid"; public static final String TAB_GROUPS_ANDROID = "TabGroupsAndroid"; public static final String TAB_GRID_LAYOUT_ANDROID = "TabGridLayoutAndroid"; public static final String TAB_REPARENTING = "TabReparenting";
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tasks/EngagementTimeUtil.java b/chrome/android/java/src/org/chromium/chrome/browser/tasks/EngagementTimeUtil.java new file mode 100644 index 0000000..059ee9a --- /dev/null +++ b/chrome/android/java/src/org/chromium/chrome/browser/tasks/EngagementTimeUtil.java
@@ -0,0 +1,28 @@ +// Copyright 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package org.chromium.chrome.browser.tasks; + +/** + * Utility class to provide engagement time helper methods. + */ +public class EngagementTimeUtil { + /** + * Provide the current time in milliseconds. + * + * @return long - the current time in milliseconds. + */ + public long currentTime() { + return System.currentTimeMillis(); + } + + /** + * Given the last engagement timestamp, return the elapsed time in milliseconds since that time. + * @param lastEngagementMs - time of the last engagement + * @return time in milliseconds that have elapsed since lastEngagementMs + */ + public long timeSinceLastEngagement(long lastEngagementMs) { + return currentTime() - lastEngagementMs; + } +}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tasks/JourneyManager.java b/chrome/android/java/src/org/chromium/chrome/browser/tasks/JourneyManager.java new file mode 100644 index 0000000..ed356f8 --- /dev/null +++ b/chrome/android/java/src/org/chromium/chrome/browser/tasks/JourneyManager.java
@@ -0,0 +1,191 @@ +// Copyright 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package org.chromium.chrome.browser.tasks; + +import android.content.Context; +import android.content.SharedPreferences; +import android.support.annotation.NonNull; + +import org.chromium.base.ContextUtils; +import org.chromium.base.VisibleForTesting; +import org.chromium.base.metrics.RecordHistogram; +import org.chromium.base.task.AsyncTask; +import org.chromium.chrome.browser.ChromeVersionInfo; +import org.chromium.chrome.browser.init.ActivityLifecycleDispatcher; +import org.chromium.chrome.browser.lifecycle.Destroyable; +import org.chromium.chrome.browser.tab.Tab; +import org.chromium.chrome.browser.tab.TabObserver; +import org.chromium.chrome.browser.tabmodel.TabModelObserver; +import org.chromium.chrome.browser.tabmodel.TabModelSelector; +import org.chromium.chrome.browser.tabmodel.TabModelSelectorTabModelObserver; +import org.chromium.chrome.browser.tabmodel.TabModelSelectorTabObserver; +import org.chromium.chrome.browser.tabmodel.TabSelectionType; +import org.chromium.content_public.browser.NavigationHandle; + +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.TimeUnit; + +/** + * Manages Journey related signals, specifically those related to tab engagement. + */ +public class JourneyManager implements Destroyable { + private static final long INVALID_START_TIME = -1; + + @VisibleForTesting + static final String PREFS_FILE = "last_engagement_for_tab_id_pref"; + + @VisibleForTesting + static final String TAB_REVISIT_METRIC = "Tabs.TimeSinceLastView.OnTabView"; + + @VisibleForTesting + static final String TAB_CLOSE_METRIC = "Tabs.TimeSinceLastView.OnTabClose"; + + // We track this in seconds because UMA can only handle 32-bit signed integers, which 45 days + // will overflow. + private static final int MAX_ENGAGEMENT_TIME_S = (int) TimeUnit.DAYS.toSeconds(45); + + private final TabModelSelectorTabObserver mTabModelSelectorTabObserver; + private final TabModelSelectorTabModelObserver mTabModelSelectorTabModelObserver; + private final ActivityLifecycleDispatcher mLifecycleDispatcher; + private final EngagementTimeUtil mEngagementTimeUtil; + + private Map<Integer, Boolean> mDidFirstPaintPerTab = new HashMap<>(); + + public JourneyManager(TabModelSelector selector, + @NonNull ActivityLifecycleDispatcher dispatcher, + EngagementTimeUtil engagementTimeUtil) { + if (!ChromeVersionInfo.isLocalBuild() && !ChromeVersionInfo.isCanaryBuild() + && !ChromeVersionInfo.isDevBuild()) { + // We do not want this in beta/stable until it's no longer backed by SharedPreferences. + mTabModelSelectorTabObserver = null; + mTabModelSelectorTabModelObserver = null; + mLifecycleDispatcher = null; + mEngagementTimeUtil = null; + return; + } + + mTabModelSelectorTabObserver = new TabModelSelectorTabObserver(selector) { + @Override + public void onShown(Tab tab, @TabSelectionType int type) { + if (type != TabSelectionType.FROM_USER) return; + + maybeRecordEngagementMetric(tab, TAB_REVISIT_METRIC); + + handleTabEngagementStarted(tab); + } + + @Override + public void onHidden(Tab tab, @Tab.TabHidingType int reason) { + handleTabEngagementStopped(tab); + } + + @Override + public void onClosingStateChanged(Tab tab, boolean closing) { + if (!closing) return; + + maybeRecordEngagementMetric(tab, TAB_CLOSE_METRIC); + } + + @Override + public void onDidStartNavigation(Tab tab, NavigationHandle navigationHandle) { + if (!navigationHandle.isInMainFrame() || navigationHandle.isSameDocument()) return; + + mDidFirstPaintPerTab.put(tab.getId(), false); + } + + @Override + public void didFirstVisuallyNonEmptyPaint(Tab tab) { + mDidFirstPaintPerTab.put(tab.getId(), true); + handleTabEngagementStarted(tab); + } + }; + + mTabModelSelectorTabModelObserver = new TabModelSelectorTabModelObserver(selector) { + @Override + public void tabClosureCommitted(Tab tab) { + getPrefs().edit().remove(String.valueOf(tab.getId())).apply(); + } + }; + + mLifecycleDispatcher = dispatcher; + mLifecycleDispatcher.register(this); + + mEngagementTimeUtil = engagementTimeUtil; + } + + @Override + public void destroy() { + mTabModelSelectorTabObserver.destroy(); + mTabModelSelectorTabModelObserver.destroy(); + mLifecycleDispatcher.unregister(this); + } + + private void handleTabEngagementStarted(Tab tab) { + long lastEngagementMs = mEngagementTimeUtil.currentTime(); + + Boolean didFirstPaint = mDidFirstPaintPerTab.get(tab.getId()); + if (didFirstPaint == null || !didFirstPaint) return; + + storeLastEngagement(tab.getId(), lastEngagementMs); + } + + private void handleTabEngagementStopped(Tab tab) { + long lastEngagementMs = mEngagementTimeUtil.currentTime(); + + Boolean didFirstPaint = mDidFirstPaintPerTab.get(tab.getId()); + if (didFirstPaint == null || !didFirstPaint) { + return; + } + + storeLastEngagement(tab.getId(), lastEngagementMs); + } + + private void storeLastEngagement(int tabId, long lastEngagementTimestampMs) { + new AsyncTask<Void>() { + @Override + protected Void doInBackground() { + getPrefs().edit().putLong(String.valueOf(tabId), lastEngagementTimestampMs).apply(); + return null; + } + + @Override + protected void onPostExecute(Void result) {} + }.executeOnExecutor(AsyncTask.SERIAL_EXECUTOR); + } + + private SharedPreferences getPrefs() { + // TODO(mattsimmons): Add a native counterpart to this class and don't write directly to + // shared prefs. + return ContextUtils.getApplicationContext().getSharedPreferences( + PREFS_FILE, Context.MODE_PRIVATE); + } + + private long getLastEngagementTimestamp(Tab tab) { + return getPrefs().getLong(String.valueOf(tab.getId()), INVALID_START_TIME); + } + + private void maybeRecordEngagementMetric(Tab tab, String name) { + long lastEngagement = getLastEngagementTimestamp(tab); + + if (lastEngagement == INVALID_START_TIME) return; + + // Compute elapsed time and convert to seconds. + long elapsedMs = mEngagementTimeUtil.timeSinceLastEngagement(lastEngagement); + int elapsedSeconds = (int) TimeUnit.MILLISECONDS.toSeconds(elapsedMs); + RecordHistogram.recordCustomCountHistogram( + name, elapsedSeconds, 1, MAX_ENGAGEMENT_TIME_S, 50); + } + + @VisibleForTesting + public TabObserver getTabModelSelectorTabObserver() { + return mTabModelSelectorTabObserver; + } + + @VisibleForTesting + public TabModelObserver getTabModelSelectorTabModelObserver() { + return mTabModelSelectorTabModelObserver; + } +}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/bottom/BottomControlsMediator.java b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/bottom/BottomControlsMediator.java index 134fb7f..b4e16f80 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/bottom/BottomControlsMediator.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/bottom/BottomControlsMediator.java
@@ -204,6 +204,13 @@ } /** + * @return Whether the browser is currently in fullscreen mode. + */ + private boolean isInFullscreenMode() { + return mFullscreenManager != null && mFullscreenManager.getPersistentFullscreenMode(); + } + + /** * The composited view is the composited version of the Android View. It is used to be able to * scroll the bottom controls off-screen synchronously. Since the bottom controls live below * the webcontents we re-size the webcontents through @@ -211,7 +218,8 @@ * visibility changes. */ private void updateCompositedViewVisibility() { - final boolean isCompositedViewVisible = mIsBottomControlsVisible && !mIsKeyboardVisible; + final boolean isCompositedViewVisible = + mIsBottomControlsVisible && !mIsKeyboardVisible && !isInFullscreenMode(); mModel.set(BottomControlsProperties.COMPOSITED_VIEW_VISIBLE, isCompositedViewVisible); mFullscreenManager.setBottomControlsHeight( isCompositedViewVisible ? mBottomControlsHeight : 0); @@ -227,7 +235,8 @@ private void updateAndroidViewVisibility() { mModel.set(BottomControlsProperties.ANDROID_VIEW_VISIBLE, mIsBottomControlsVisible && !mIsKeyboardVisible && !mIsOverlayPanelShowing - && !mIsInSwipeLayout && mFullscreenManager.getBottomControlOffset() == 0); + && !mIsInSwipeLayout && mFullscreenManager.getBottomControlOffset() == 0 + && !isInFullscreenMode()); } @Override
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/tasks/JourneyManagerTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/tasks/JourneyManagerTest.java new file mode 100644 index 0000000..c1070d8d --- /dev/null +++ b/chrome/android/junit/src/org/chromium/chrome/browser/tasks/JourneyManagerTest.java
@@ -0,0 +1,270 @@ +// Copyright 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package org.chromium.chrome.browser.tasks; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.anyLong; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.verify; + +import android.content.Context; +import android.content.SharedPreferences; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.robolectric.Robolectric; +import org.robolectric.annotation.Config; + +import org.chromium.base.ContextUtils; +import org.chromium.base.metrics.test.ShadowRecordHistogram; +import org.chromium.base.task.test.BackgroundShadowAsyncTask; +import org.chromium.base.test.BaseRobolectricTestRunner; +import org.chromium.chrome.browser.init.ActivityLifecycleDispatcher; +import org.chromium.chrome.browser.tab.Tab; +import org.chromium.chrome.browser.tab.TabObserver; +import org.chromium.chrome.browser.tabmodel.TabList; +import org.chromium.chrome.browser.tabmodel.TabModel; +import org.chromium.chrome.browser.tabmodel.TabModelObserver; +import org.chromium.chrome.browser.tabmodel.TabModelSelector; +import org.chromium.chrome.browser.tabmodel.TabSelectionType; + +import java.util.ArrayList; +import java.util.List; + +/** Unit tests for JourneyManager. */ +@RunWith(BaseRobolectricTestRunner.class) +@Config(manifest = Config.NONE, + shadows = {ShadowRecordHistogram.class, BackgroundShadowAsyncTask.class}) +public final class JourneyManagerTest { + private static final int LAST_ENGAGEMENT_ELAPSED_MS = 5000; + private static final int LAST_ENGAGEMENT_ELAPSED_S = 5; + private static final int TAB_ID = 123; + private static final long BASE_TIME_MS = 1000000L; + + @Mock + private TabModel mTabModel; + + @Mock + private TabModelSelector mTabModelSelector; + + @Mock + private Tab mTab; + + @Mock + private TabList mTabList; + + @Mock + private ActivityLifecycleDispatcher mDispatcher; + + @Mock + private EngagementTimeUtil mEngagementTimeUtil; + + private JourneyManager mJourneyManager; + + private TabObserver mTabModelSelectorTabObserver; + + private TabModelObserver mTabModelSelectorTabModelObserver; + + private SharedPreferences mSharedPreferences; + + @Before + public void setUp() { + ShadowRecordHistogram.reset(); + Robolectric.getBackgroundThreadScheduler().reset(); + + MockitoAnnotations.initMocks(this); + + mSharedPreferences = ContextUtils.getApplicationContext().getSharedPreferences( + JourneyManager.PREFS_FILE, Context.MODE_PRIVATE); + mSharedPreferences.edit().clear().commit(); + + mJourneyManager = new JourneyManager(mTabModelSelector, mDispatcher, mEngagementTimeUtil); + mTabModelSelectorTabObserver = mJourneyManager.getTabModelSelectorTabObserver(); + mTabModelSelectorTabModelObserver = mJourneyManager.getTabModelSelectorTabModelObserver(); + + verify(mDispatcher).register(mJourneyManager); + + // Set up a tab. + doReturn(TAB_ID).when(mTab).getId(); + + // Set up tab model, returning tab above as current. + List<TabModel> tabModels = new ArrayList<>(); + tabModels.add(mTabModel); + doReturn(tabModels).when(mTabModelSelector).getModels(); + doReturn(mTab).when(mTabModelSelector).getCurrentTab(); + doReturn(mTabList).when(mTabModel).getComprehensiveModel(); + doReturn(0).when(mTabList).getCount(); + + doReturn(BASE_TIME_MS).when(mEngagementTimeUtil).currentTime(); + } + + @Test + public void onTabShown_noPreviousEngagement() { + mTabModelSelectorTabObserver.onShown(mTab, TabSelectionType.FROM_USER); + + assertEquals(0, + ShadowRecordHistogram.getHistogramValueCountForTesting( + JourneyManager.TAB_REVISIT_METRIC, LAST_ENGAGEMENT_ELAPSED_S)); + } + + @Test + public void onTabShown_previousEngagementExists() { + // Paint to set did paint flag. + mTabModelSelectorTabObserver.didFirstVisuallyNonEmptyPaint(mTab); + flushAsyncPrefs(); + + // Move time forward. + doReturn((long) LAST_ENGAGEMENT_ELAPSED_MS) + .when(mEngagementTimeUtil) + .timeSinceLastEngagement(anyLong()); + + mTabModelSelectorTabObserver.onShown(mTab, TabSelectionType.FROM_USER); + + assertEquals(1, + ShadowRecordHistogram.getHistogramValueCountForTesting( + JourneyManager.TAB_REVISIT_METRIC, LAST_ENGAGEMENT_ELAPSED_S)); + } + + @Test + public void onTabShown_previousEngagementExists_contentNotYetPainted() { + // Set did paint flag. + mTabModelSelectorTabObserver.onShown(mTab, TabSelectionType.FROM_USER); + flushAsyncPrefs(); + + // Advance time. + doReturn(BASE_TIME_MS + LAST_ENGAGEMENT_ELAPSED_MS).when(mEngagementTimeUtil).currentTime(); + + mTabModelSelectorTabObserver.onShown(mTab, TabSelectionType.FROM_USER); + + assertEquals(-1, mSharedPreferences.getLong(String.valueOf(mTab.getId()), -1)); + } + + @Test + public void onTabShown_previousEngagementExists_notSelectedByUser() { + // Set did paint flag. + mTabModelSelectorTabObserver.didFirstVisuallyNonEmptyPaint(mTab); + flushAsyncPrefs(); + + // Advance time. + doReturn((long) LAST_ENGAGEMENT_ELAPSED_MS) + .when(mEngagementTimeUtil) + .timeSinceLastEngagement(anyLong()); + + mTabModelSelectorTabObserver.onShown(mTab, TabSelectionType.FROM_EXIT); + + assertEquals(0, + ShadowRecordHistogram.getHistogramValueCountForTesting( + JourneyManager.TAB_REVISIT_METRIC, LAST_ENGAGEMENT_ELAPSED_S)); + } + + @Test + public void onTabHidden_shouldSaveLastEngagement() { + // Set did paint flag. + mTabModelSelectorTabObserver.didFirstVisuallyNonEmptyPaint(mTab); + flushAsyncPrefs(); + + // Advance time. + doReturn(BASE_TIME_MS + LAST_ENGAGEMENT_ELAPSED_MS).when(mEngagementTimeUtil).currentTime(); + + mTabModelSelectorTabObserver.onHidden(mTab, Tab.TabHidingType.ACTIVITY_HIDDEN); + flushAsyncPrefs(); + + assertEquals(BASE_TIME_MS + LAST_ENGAGEMENT_ELAPSED_MS, + mSharedPreferences.getLong(String.valueOf(mTab.getId()), -1)); + } + + @Test + public void onClosingStateChanged_noPreviousEngagement() { + mTabModelSelectorTabObserver.onShown(mTab, TabSelectionType.FROM_USER); + flushAsyncPrefs(); + + mTabModelSelectorTabObserver.onClosingStateChanged(mTab, true); + + assertEquals(0, + ShadowRecordHistogram.getHistogramValueCountForTesting( + JourneyManager.TAB_CLOSE_METRIC, LAST_ENGAGEMENT_ELAPSED_S)); + } + + @Test + public void onClosingStateChanged_previousEngagementExists_tabClosureNotCommitted() { + // Set did paint flag. + mTabModelSelectorTabObserver.didFirstVisuallyNonEmptyPaint(mTab); + flushAsyncPrefs(); + + // Advance time. + doReturn((long) LAST_ENGAGEMENT_ELAPSED_MS) + .when(mEngagementTimeUtil) + .timeSinceLastEngagement(anyLong()); + + mTabModelSelectorTabObserver.onClosingStateChanged(mTab, true); + + assertEquals(1, + ShadowRecordHistogram.getHistogramValueCountForTesting( + JourneyManager.TAB_CLOSE_METRIC, LAST_ENGAGEMENT_ELAPSED_S)); + + assertTrue(mSharedPreferences.contains(String.valueOf(mTab.getId()))); + } + + @Test + public void onClosingStateChanged_previousEngagementExists_notClosing() { + // Set did paint flag. + mTabModelSelectorTabObserver.didFirstVisuallyNonEmptyPaint(mTab); + flushAsyncPrefs(); + + // Advance time. + doReturn((long) LAST_ENGAGEMENT_ELAPSED_MS) + .when(mEngagementTimeUtil) + .timeSinceLastEngagement(anyLong()); + + mTabModelSelectorTabObserver.onClosingStateChanged(mTab, false); + + assertEquals(0, + ShadowRecordHistogram.getHistogramValueCountForTesting( + JourneyManager.TAB_CLOSE_METRIC, LAST_ENGAGEMENT_ELAPSED_S)); + } + + @Test + public void onClosingStateChanged_previousEngagementExists_tabClosureCommitted() { + // Set did paint flag. + mTabModelSelectorTabObserver.didFirstVisuallyNonEmptyPaint(mTab); + flushAsyncPrefs(); + + // Advance time. + doReturn((long) LAST_ENGAGEMENT_ELAPSED_MS) + .when(mEngagementTimeUtil) + .timeSinceLastEngagement(anyLong()); + + mTabModelSelectorTabObserver.onClosingStateChanged(mTab, true); + + assertEquals(1, + ShadowRecordHistogram.getHistogramValueCountForTesting( + JourneyManager.TAB_CLOSE_METRIC, LAST_ENGAGEMENT_ELAPSED_S)); + + mTabModelSelectorTabModelObserver.tabClosureCommitted(mTab); + flushAsyncPrefs(); + + assertFalse(mSharedPreferences.contains(String.valueOf(mTab.getId()))); + } + + @Test + public void destroy_unregistersLifecycleObserver() { + mJourneyManager.destroy(); + verify(mDispatcher).unregister(mJourneyManager); + } + + private void flushAsyncPrefs() { + try { + BackgroundShadowAsyncTask.runBackgroundTasks(); + } catch (Exception ex) { + } finally { + mSharedPreferences.edit().commit(); + } + } +}
diff --git a/chrome/android/touchless/java/res/layout/dialog_list_item.xml b/chrome/android/touchless/java/res/layout/dialog_list_item.xml index 2db6530..91f25a5 100644 --- a/chrome/android/touchless/java/res/layout/dialog_list_item.xml +++ b/chrome/android/touchless/java/res/layout/dialog_list_item.xml
@@ -6,6 +6,10 @@ xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="wrap_content" + android:paddingStart="12dp" + android:paddingEnd="12dp" + android:paddingTop="9dp" + android:paddingBottom="9dp" android:orientation="horizontal" android:background="@color/modern_primary_color"> @@ -14,17 +18,14 @@ android:layout_width="18dp" android:layout_height="18dp" android:layout_gravity="center_vertical" - android:layout_marginBottom="9dp" - android:layout_marginStart="12dp" - android:layout_marginTop="9dp" - android:padding="3dp" - android:scaleType="centerInside"/> + android:layout_marginEnd="10dp" + android:scaleType="centerInside" + android:visibility="gone"/> <TextView android:id="@+id/dialog_item_text" android:layout_width="wrap_content" - android:layout_height="match_parent" - android:layout_marginStart="10dp" + android:layout_height="wrap_content" android:gravity="center_vertical" android:textAppearance="@style/TextAppearance.BlackBody"/>
diff --git a/chrome/android/touchless/java/src/org/chromium/chrome/browser/touchless/dialog/TouchlessDialogPresenter.java b/chrome/android/touchless/java/src/org/chromium/chrome/browser/touchless/dialog/TouchlessDialogPresenter.java index 8898657..ff19bc3 100644 --- a/chrome/android/touchless/java/src/org/chromium/chrome/browser/touchless/dialog/TouchlessDialogPresenter.java +++ b/chrome/android/touchless/java/src/org/chromium/chrome/browser/touchless/dialog/TouchlessDialogPresenter.java
@@ -153,6 +153,7 @@ ChromeImageView imageView = view.findViewById(R.id.dialog_item_icon); TextView textView = view.findViewById(R.id.dialog_item_text); if (DialogListItemProperties.ICON == propertyKey) { + imageView.setVisibility(View.VISIBLE); Drawable icon = model.get(DialogListItemProperties.ICON).mutate(); icon.clearColorFilter(); imageView.setImageDrawable(icon);
diff --git a/chrome/app/chrome_main_delegate.cc b/chrome/app/chrome_main_delegate.cc index e4f0c7c..345f6c1 100644 --- a/chrome/app/chrome_main_delegate.cc +++ b/chrome/app/chrome_main_delegate.cc
@@ -110,6 +110,7 @@ #include "base/system/sys_info.h" #include "chrome/browser/chromeos/boot_times_recorder.h" #include "chrome/browser/chromeos/dbus/dbus_helper.h" +#include "chrome/browser/chromeos/startup_settings_cache.h" #include "chromeos/constants/chromeos_paths.h" #include "chromeos/constants/chromeos_switches.h" #include "chromeos/hugepage_text/hugepage_text.h" @@ -899,8 +900,14 @@ // via the preference prefs::kApplicationLocale. The browser process uses // the --lang flag to pass the value of the PrefService in here. Maybe // this value could be passed in a different way. - const std::string locale = - command_line.GetSwitchValueASCII(switches::kLang); + std::string locale = command_line.GetSwitchValueASCII(switches::kLang); +#if defined(OS_CHROMEOS) + if (process_type == service_manager::switches::kZygoteProcess) { + DCHECK(locale.empty()); + // See comment at ReadAppLocale() for why we do this. + locale = chromeos::startup_settings_cache::ReadAppLocale(); + } +#endif #if defined(OS_ANDROID) // The renderer sandbox prevents us from accessing our .pak files directly. // Therefore file descriptors to the .pak files that we need are passed in
diff --git a/chrome/app/chromeos_strings.grdp b/chrome/app/chromeos_strings.grdp index 818af247..493ee87 100644 --- a/chrome/app/chromeos_strings.grdp +++ b/chrome/app/chromeos_strings.grdp
@@ -869,22 +869,6 @@ <message name="IDS_LOGIN_PASSWORD_CHANGED_TRY_AGAIN" desc="Label for the retry button on the proceed anyway step in the the GAIA password changed flow"> Try again </message> - <message name="IDS_LOGIN_UNRECOVERABLE_CRYPTOHOME_ERROR_TITLE" desc="Title for the fatal cryptohome error dialog box"> - Can't sign in - </message> - <message name="IDS_LOGIN_UNRECOVERABLE_CRYPTOHOME_ERROR_MESSAGE" desc="Message for the fatal cryptohome error dialog box"> - We're sorry. We can't access your profile. Files and data stored on this device may have been lost.<ph name="BR"><br></ph> - <ph name="BR"><br></ph> - You'll have to set up your profile again.<ph name="BR"><br></ph> - <ph name="BR"><br></ph> - On the next screen, please send feedback to help us fix the issue. - </message> - <message name="IDS_LOGIN_UNRECOVERABLE_CRYPTOHOME_ERROR_CONTINUE" desc="Label of the button to continue with re-creating cryptohome for the fatal cryptohome error dialog box"> - Continue - </message> - <message name="IDS_LOGIN_UNRECOVERABLE_CRYPTOHOME_ERROR_WAIT_MESSAGE" desc="Message to show when the fatal cryptohome error dialog box is waiting for user profile re-creation to be closed."> - Re-creating profile, please wait... - </message> <message name="IDS_LOGIN_SAML_NOTICE" desc="Text message displayed above SAML portal to early indicate that the user is being redirected to another sign-in provider. This is the version of the string used in the GAIA flow."> This sign-in service is hosted by <ph name="SAML_DOMAIN">$1<ex>saml.com</ex></ph> </message>
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn index aa11e3d..c3ff09b 100644 --- a/chrome/browser/BUILD.gn +++ b/chrome/browser/BUILD.gn
@@ -1062,6 +1062,8 @@ "performance_manager/chrome_browser_main_extra_parts_performance_manager.h", "performance_manager/chrome_content_browser_client_performance_manager_part.cc", "performance_manager/chrome_content_browser_client_performance_manager_part.h", + "performance_manager/decorators/frozen_frame_aggregator.cc", + "performance_manager/decorators/frozen_frame_aggregator.h", "performance_manager/decorators/page_almost_idle_decorator.cc", "performance_manager/decorators/page_almost_idle_decorator.h", "performance_manager/graph/frame_node_impl.cc",
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc index f2f39dc..74bef82 100644 --- a/chrome/browser/about_flags.cc +++ b/chrome/browser/about_flags.cc
@@ -981,6 +981,18 @@ }; #endif // OS_ANDROID +const FeatureEntry::FeatureParam kVizHitTestDrawQuadEnabled[] = { + {"provider", "draw_quad"}}; + +const FeatureEntry::FeatureParam kVizHitTestSurfaceLayerEnabled[] = { + {"provider", "surface_layer"}}; + +const FeatureEntry::FeatureVariation kVizHitTestVariations[] = { + {"DrawQuad", kVizHitTestDrawQuadEnabled, + base::size(kVizHitTestDrawQuadEnabled), nullptr}, + {"SurfaceLayer", kVizHitTestSurfaceLayerEnabled, + base::size(kVizHitTestSurfaceLayerEnabled), nullptr}}; + // RECORDING USER METRICS FOR FLAGS: // ----------------------------------------------------------------------------- // The first line of the entry is the internal name. @@ -3076,10 +3088,11 @@ flag_descriptions::kQueryInOmniboxDescription, kOsAll, FEATURE_VALUE_TYPE(omnibox::kQueryInOmnibox)}, - {"enable-viz-hit-test-draw-quad", - flag_descriptions::kVizHitTestDrawQuadName, - flag_descriptions::kVizHitTestDrawQuadDescription, kOsAll, - FEATURE_VALUE_TYPE(features::kEnableVizHitTestDrawQuad)}, + {"enable-viz-hit-test", flag_descriptions::kVizHitTestName, + flag_descriptions::kVizHitTestDescription, kOsAll, + FEATURE_WITH_PARAMS_VALUE_TYPE(features::kEnableVizHitTest, + kVizHitTestVariations, + "VizHitTestDataSource")}, #if BUILDFLAG(ENABLE_PDF) #if defined(OS_CHROMEOS) @@ -3149,16 +3162,19 @@ {"enable-tab-groups", flag_descriptions::kTabGroupsAndroidName, flag_descriptions::kTabGroupsAndroidDescription, kOsAndroid, FEATURE_VALUE_TYPE(chrome::android::kTabGroupsAndroid)}, -#endif // OS_ANDROID -#if defined(OS_ANDROID) {"enable-tab-switcher-on-return", flag_descriptions::kTabSwitcherOnReturnName, flag_descriptions::kTabSwitcherOnReturnDescription, kOsAndroid, FEATURE_WITH_PARAMS_VALUE_TYPE(chrome::android::kTabSwitcherOnReturn, kTabSwitcherOnReturnVariations, "TabSwitcherOnReturn")}, -#endif + + {"enable-tab-engagement-reporting", + flag_descriptions::kTabEngagementReportingName, + flag_descriptions::kTabEngagementReportingDescription, kOsAndroid, + FEATURE_VALUE_TYPE(chrome::android::kTabEngagementReportingAndroid)}, +#endif // OS_ANDROID {"enable-built-in-module-all", flag_descriptions::kBuiltInModuleAllName, flag_descriptions::kBuiltInModuleAllDescription, kOsAll,
diff --git a/chrome/browser/android/chrome_feature_list.cc b/chrome/browser/android/chrome_feature_list.cc index 33f548d..5c5c183 100644 --- a/chrome/browser/android/chrome_feature_list.cc +++ b/chrome/browser/android/chrome_feature_list.cc
@@ -168,6 +168,7 @@ &kSpannableInlineAutocomplete, &kSpecialLocaleWrapper, &kSpecialUserDecision, + &kTabEngagementReportingAndroid, &kTabGroupsAndroid, &kTabGridLayoutAndroid, &kTabReparenting, @@ -493,6 +494,9 @@ const base::Feature kSpecialUserDecision{"SpecialUserDecision", base::FEATURE_DISABLED_BY_DEFAULT}; +const base::Feature kTabEngagementReportingAndroid{ + "TabEngagementReportingAndroid", base::FEATURE_DISABLED_BY_DEFAULT}; + const base::Feature kTabGroupsAndroid{"TabGroupsAndroid", base::FEATURE_DISABLED_BY_DEFAULT};
diff --git a/chrome/browser/android/chrome_feature_list.h b/chrome/browser/android/chrome_feature_list.h index ec2067f1..0264acb 100644 --- a/chrome/browser/android/chrome_feature_list.h +++ b/chrome/browser/android/chrome_feature_list.h
@@ -99,6 +99,7 @@ extern const base::Feature kSpannableInlineAutocomplete; extern const base::Feature kSpecialLocaleWrapper; extern const base::Feature kSpecialUserDecision; +extern const base::Feature kTabEngagementReportingAndroid; extern const base::Feature kTabGroupsAndroid; extern const base::Feature kTabGridLayoutAndroid; extern const base::Feature kTabReparenting;
diff --git a/chrome/browser/android/vr/arcore_device/ar_image_transport.cc b/chrome/browser/android/vr/arcore_device/ar_image_transport.cc index 7e552c9..53f6538f 100644 --- a/chrome/browser/android/vr/arcore_device/ar_image_transport.cc +++ b/chrome/browser/android/vr/arcore_device/ar_image_transport.cc
@@ -9,6 +9,7 @@ #include "base/containers/queue.h" #include "base/trace_event/traced_value.h" #include "chrome/browser/android/vr/mailbox_to_surface_bridge.h" +#include "gpu/command_buffer/common/shared_image_usage.h" #include "gpu/ipc/common/gpu_memory_buffer_impl_android_hardware_buffer.h" #include "ui/gfx/gpu_fence.h" #include "ui/gl/gl_bindings.h" @@ -40,9 +41,7 @@ shared_gpu_memory_buffer; // Resources in the remote GPU process command buffer context - std::unique_ptr<gpu::MailboxHolder> mailbox_holder; - GLuint remote_texture_id = 0; - GLuint remote_image_id = 0; + gpu::MailboxHolder mailbox_holder; // Resources in the local GL context GLuint local_texture_id = 0; @@ -66,7 +65,21 @@ mailbox_bridge_(std::move(mailbox_bridge)), swap_chain_(std::make_unique<SharedFrameBufferSwapChain>()) {} -ArImageTransport::~ArImageTransport() {} +ArImageTransport::~ArImageTransport() { + DCHECK(IsOnGlThread()); + while (!swap_chain_->buffers.empty()) { + std::unique_ptr<SharedFrameBuffer> buffer = + std::move(swap_chain_->buffers.front()); + swap_chain_->buffers.pop(); + if (!buffer->mailbox_holder.mailbox.IsZero()) { + DVLOG(2) << ": DestroySharedImage, mailbox=" + << buffer->mailbox_holder.mailbox.ToDebugString(); + // Note: the sync token in mailbox_holder may not be accurate. See comment + // in TransferFrame below. + mailbox_bridge_->DestroySharedImage(buffer->mailbox_holder); + } + } +} bool ArImageTransport::Initialize() { DCHECK(IsOnGlThread()); @@ -96,12 +109,12 @@ TRACE_EVENT0("gpu", __FUNCTION__); // Unbind previous image (if any). - if (buffer->remote_image_id) { - DVLOG(2) << ": UnbindSharedBuffer, remote_image=" - << buffer->remote_image_id; - mailbox_bridge_->UnbindSharedBuffer(buffer->remote_image_id, - buffer->remote_texture_id); - buffer->remote_image_id = 0; + if (!buffer->mailbox_holder.mailbox.IsZero()) { + DVLOG(2) << ": DestroySharedImage, mailbox=" + << buffer->mailbox_holder.mailbox.ToDebugString(); + // Note: the sync token in mailbox_holder may not be accurate. See comment + // in TransferFrame below. + mailbox_bridge_->DestroySharedImage(buffer->mailbox_holder); } DVLOG(2) << __FUNCTION__ << ": width=" << size.width() @@ -118,11 +131,14 @@ kBufferId, size, format, usage, gpu::GpuMemoryBufferImpl::DestructionCallback()); - buffer->remote_image_id = mailbox_bridge_->BindSharedBufferImage( - buffer->shared_gpu_memory_buffer.get(), size, format, usage, - buffer->remote_texture_id); - DVLOG(2) << ": BindSharedBufferImage, remote_image=" - << buffer->remote_image_id; + uint32_t shared_image_usage = gpu::SHARED_IMAGE_USAGE_SCANOUT | + gpu::SHARED_IMAGE_USAGE_DISPLAY | + gpu::SHARED_IMAGE_USAGE_GLES2; + buffer->mailbox_holder = + mailbox_bridge_->CreateSharedImage(buffer->shared_gpu_memory_buffer.get(), + gfx::ColorSpace(), shared_image_usage); + DVLOG(2) << ": CreateSharedImage, mailbox=" + << buffer->mailbox_holder.mailbox.ToDebugString(); auto img = base::MakeRefCounted<gl::GLImageAHardwareBuffer>(size); @@ -151,11 +167,6 @@ for (int i = 0; i < kSharedBufferSwapChainSize; ++i) { std::unique_ptr<SharedFrameBuffer> buffer = std::make_unique<SharedFrameBuffer>(); - // Remote resources - buffer->mailbox_holder = std::make_unique<gpu::MailboxHolder>(); - buffer->mailbox_holder->texture_target = GL_TEXTURE_2D; - buffer->remote_texture_id = - mailbox_bridge_->CreateMailboxTexture(&buffer->mailbox_holder->mailbox); // Local resources glGenTextures(1, &buffer->local_texture_id); @@ -173,6 +184,9 @@ DCHECK(IsOnGlThread()); // TODO(klausw): find out when a buffer is actually done being used // including by GL so we can know if we are overwriting one. + // A sync token needs to be returned by the client and stashed into + // shared_buffer->mailbox_holder.sync_token, then waited upon before reusing + // the buffer. DCHECK(swap_chain_->buffers.size() > 0); glBindFramebufferEXT(GL_DRAW_FRAMEBUFFER, transfer_fbo_); @@ -213,10 +227,14 @@ // Make a GpuFence and place it in the GPU stream for sequencing. std::unique_ptr<gl::GLFence> gl_fence = gl::GLFence::CreateForGpuFence(); std::unique_ptr<gfx::GpuFence> gpu_fence = gl_fence->GetGpuFence(); - mailbox_bridge_->WaitForClientGpuFence(gpu_fence.get()); - mailbox_bridge_->GenSyncToken(&shared_buffer->mailbox_holder->sync_token); - gpu::MailboxHolder rendered_frame_holder = *shared_buffer->mailbox_holder; + // Have GL wait on both the client fence and the creation/return sync token. + // TODO(piman): this should probably do an UpdateSharedImage. + mailbox_bridge_->WaitSyncToken(shared_buffer->mailbox_holder.sync_token); + mailbox_bridge_->WaitForClientGpuFence(gpu_fence.get()); + mailbox_bridge_->GenSyncToken(&shared_buffer->mailbox_holder.sync_token); + + gpu::MailboxHolder rendered_frame_holder = shared_buffer->mailbox_holder; // Done with the shared buffer. swap_chain_->buffers.push(std::move(shared_buffer));
diff --git a/chrome/browser/android/vr/arcore_device/arcore_device_unittest.cc b/chrome/browser/android/vr/arcore_device/arcore_device_unittest.cc index fe94ede..84cb67b 100644 --- a/chrome/browser/android/vr/arcore_device/arcore_device_unittest.cc +++ b/chrome/browser/android/vr/arcore_device/arcore_device_unittest.cc
@@ -73,10 +73,6 @@ void BindContextProviderToCurrentThread() override {} - uint32_t CreateMailboxTexture(gpu::Mailbox* mailbox) override { - return TEXTURE_ID; - } - bool IsConnected() override { return true; } void CallCallback() { std::move(callback_).Run(); }
diff --git a/chrome/browser/android/vr/arcore_device/arcore_gl.cc b/chrome/browser/android/vr/arcore_device/arcore_gl.cc index 73cc212..e271b0e 100644 --- a/chrome/browser/android/vr/arcore_device/arcore_gl.cc +++ b/chrome/browser/android/vr/arcore_device/arcore_gl.cc
@@ -101,7 +101,10 @@ environment_binding_(this), weak_ptr_factory_(this) {} -ArCoreGl::~ArCoreGl() {} +ArCoreGl::~ArCoreGl() { + DCHECK(IsOnGlThread()); + ar_image_transport_.reset(); +} void ArCoreGl::Initialize(vr::ArCoreInstallUtils* install_utils, ArCoreFactory* arcore_factory,
diff --git a/chrome/browser/android/vr/gvr_scheduler_delegate.cc b/chrome/browser/android/vr/gvr_scheduler_delegate.cc index 531f6cc..309d4613 100644 --- a/chrome/browser/android/vr/gvr_scheduler_delegate.cc +++ b/chrome/browser/android/vr/gvr_scheduler_delegate.cc
@@ -20,6 +20,7 @@ #include "chrome/browser/vr/scheduler_ui_interface.h" #include "content/public/common/content_features.h" #include "device/vr/android/gvr/gvr_delegate.h" +#include "gpu/command_buffer/common/shared_image_usage.h" #include "gpu/config/gpu_driver_bug_workaround_type.h" #include "gpu/ipc/common/gpu_memory_buffer_impl_android_hardware_buffer.h" #include "third_party/gvr-android-sdk/src/libraries/headers/vr/gvr/capi/include/gvr.h" @@ -91,6 +92,18 @@ GvrSchedulerDelegate::~GvrSchedulerDelegate() { ClosePresentationBindings(); webxr_.EndPresentation(); + if (webxr_use_shared_buffer_draw_) { + std::vector<std::unique_ptr<WebXrSharedBuffer>> buffers = + webxr_.TakeSharedBuffers(); + for (auto& buffer : buffers) { + if (!buffer->mailbox_holder.mailbox.IsZero()) { + DCHECK(mailbox_bridge_); + DVLOG(2) << ": DestroySharedImage, mailbox=" + << buffer->mailbox_holder.mailbox.ToDebugString(); + mailbox_bridge_->DestroySharedImage(buffer->mailbox_holder); + } + } + } } void GvrSchedulerDelegate::SetBrowserRenderer( @@ -863,7 +876,8 @@ CHECK(webxr_.HaveAnimatingFrame()); WebXrSharedBuffer* buffer = webxr_.GetAnimatingFrame()->shared_buffer.get(); DCHECK(buffer); - frame_data->buffer_holder = *buffer->mailbox_holder; + DCHECK(buffer->mailbox_holder.sync_token.verified_flush()); + frame_data->buffer_holder = buffer->mailbox_holder; } int64_t prediction_nanos = GetPredictedFrameTime().InMicroseconds() * 1000; @@ -914,12 +928,6 @@ std::make_unique<WebXrSharedBuffer>(); buffer = webxr_.GetAnimatingFrame()->shared_buffer.get(); - // Remote resources - buffer->mailbox_holder = std::make_unique<gpu::MailboxHolder>(); - buffer->mailbox_holder->texture_target = GL_TEXTURE_2D; - buffer->remote_texture = - mailbox_bridge_->CreateMailboxTexture(&buffer->mailbox_holder->mailbox); - // Local resources glGenTextures(1, &buffer->local_texture); } @@ -927,12 +935,6 @@ if (webxr_surface_size != buffer->size) { // Don't need the image for zero copy mode. WebXrCreateOrResizeSharedBufferImage(buffer, webxr_surface_size); - // We always need a valid sync token, even if not using - // the image. The Renderer waits for it before using the - // mailbox. Technically we don't need to update it - // after resize for zero copy mode, but we do need it - // after initial creation. - mailbox_bridge_->GenSyncToken(&buffer->mailbox_holder->sync_token); // Save the size to avoid expensive reallocation next time. buffer->size = webxr_surface_size; @@ -944,11 +946,10 @@ const gfx::Size& size) { TRACE_EVENT0("gpu", __func__); // Unbind previous image (if any). - if (buffer->remote_image) { - DVLOG(2) << ": UnbindSharedBuffer, remote_image=" << buffer->remote_image; - mailbox_bridge_->UnbindSharedBuffer(buffer->remote_image, - buffer->remote_texture); - buffer->remote_image = 0; + if (!buffer->mailbox_holder.mailbox.IsZero()) { + DVLOG(2) << ": DestroySharedImage, mailbox=" + << buffer->mailbox_holder.mailbox.ToDebugString(); + mailbox_bridge_->DestroySharedImage(buffer->mailbox_holder); } DVLOG(2) << __func__ << ": width=" << size.width() @@ -964,9 +965,13 @@ kBufferId, size, format, usage, gpu::GpuMemoryBufferImpl::DestructionCallback()); - buffer->remote_image = mailbox_bridge_->BindSharedBufferImage( - buffer->gmb.get(), size, format, usage, buffer->remote_texture); - DVLOG(2) << ": BindSharedBufferImage, remote_image=" << buffer->remote_image; + uint32_t shared_image_usage = gpu::SHARED_IMAGE_USAGE_SCANOUT | + gpu::SHARED_IMAGE_USAGE_DISPLAY | + gpu::SHARED_IMAGE_USAGE_GLES2; + buffer->mailbox_holder = mailbox_bridge_->CreateSharedImage( + buffer->gmb.get(), gfx::ColorSpace(), shared_image_usage); + DVLOG(2) << ": CreateSharedImage, mailbox=" + << buffer->mailbox_holder.mailbox.ToDebugString(); scoped_refptr<gl::GLImageAHardwareBuffer> img( new gl::GLImageAHardwareBuffer(graphics_->webxr_surface_size())); @@ -1043,11 +1048,21 @@ if (!IsSubmitFrameExpected(frame_index)) return; - // Renderer didn't submit a frame. Wait for the sync token to ensure - // that any mailbox_bridge_ operations for the next frame happen after - // whatever drawing the Renderer may have done before exiting. - if (webxr_.mailbox_bridge_ready()) - mailbox_bridge_->WaitSyncToken(sync_token); + if (webxr_use_shared_buffer_draw_) { + // Renderer didn't submit a frame. Stash the sync token in the mailbox + // holder, so that we use the dependency before destroying or recycling the + // shared image. + WebXrSharedBuffer* buffer = webxr_.GetAnimatingFrame()->shared_buffer.get(); + DCHECK(buffer); + DCHECK(sync_token.verified_flush()); + buffer->mailbox_holder.sync_token = sync_token; + } else { + // Renderer didn't submit a frame. Wait for the sync token to ensure + // that any mailbox_bridge_ operations for the next frame happen after + // whatever drawing the Renderer may have done before exiting. + if (webxr_.mailbox_bridge_ready()) + mailbox_bridge_->WaitSyncToken(sync_token); + } DVLOG(2) << __func__ << ": recycle unused animating frame"; DCHECK(webxr_.HaveAnimatingFrame()); @@ -1078,6 +1093,22 @@ if (!SubmitFrameCommon(frame_index, time_waited)) return; + if (webxr_use_shared_buffer_draw_) { + // Renderer submitted a frame. Stash the sync token in the mailbox + // holder, so that we use the dependency before destroying or recycling the + // shared image. + WebXrSharedBuffer* buffer = webxr_.GetAnimatingFrame()->shared_buffer.get(); + DCHECK(buffer); + DCHECK(sync_token.verified_flush()); + buffer->mailbox_holder.sync_token = sync_token; + } else { + mojo::ReportBadMessage( + "SubmitFrameDrawnIntoTexture called while using the wrong transport " + "mode"); + ClosePresentationBindings(); + return; + } + webxr_.ProcessOrDefer( base::BindOnce(&GvrSchedulerDelegate::ProcessWebVrFrameFromGMB, weak_ptr_factory_.GetWeakPtr(), frame_index, sync_token));
diff --git a/chrome/browser/android/vr/mailbox_to_surface_bridge.cc b/chrome/browser/android/vr/mailbox_to_surface_bridge.cc index 9551e24..57e22f0 100644 --- a/chrome/browser/android/vr/mailbox_to_surface_bridge.cc +++ b/chrome/browser/android/vr/mailbox_to_surface_bridge.cc
@@ -19,6 +19,8 @@ #include "gpu/GLES2/gl2extchromium.h" #include "gpu/command_buffer/client/context_support.h" #include "gpu/command_buffer/client/gles2_interface.h" +#include "gpu/command_buffer/client/shared_image_interface.h" +#include "gpu/command_buffer/common/gpu_memory_buffer_support.h" #include "gpu/command_buffer/common/mailbox.h" #include "gpu/command_buffer/common/mailbox_holder.h" #include "gpu/ipc/client/gpu_channel_host.h" @@ -355,46 +357,34 @@ gl_->DestroyGpuFenceCHROMIUM(id); } -uint32_t MailboxToSurfaceBridge::CreateMailboxTexture(gpu::Mailbox* mailbox) { +gpu::MailboxHolder MailboxToSurfaceBridge::CreateSharedImage( + gpu::GpuMemoryBufferImplAndroidHardwareBuffer* buffer, + const gfx::ColorSpace& color_space, + uint32_t usage) { TRACE_EVENT0("gpu", __FUNCTION__); DCHECK(IsConnected()); - GLuint tex = 0; - gl_->GenTextures(1, &tex); - gl_->BindTexture(GL_TEXTURE_2D, tex); - gl_->ProduceTextureDirectCHROMIUM(tex, mailbox->name); + auto* sii = context_provider_->SharedImageInterface(); + DCHECK(sii); - return tex; + gpu::MailboxHolder mailbox_holder; + mailbox_holder.mailbox = + sii->CreateSharedImage(buffer, nullptr, color_space, usage); + mailbox_holder.sync_token = sii->GenVerifiedSyncToken(); + DCHECK(!gpu::NativeBufferNeedsPlatformSpecificTextureTarget( + buffer->GetFormat())); + mailbox_holder.texture_target = GL_TEXTURE_2D; + return mailbox_holder; } -uint32_t MailboxToSurfaceBridge::BindSharedBufferImage( - gfx::GpuMemoryBuffer* buffer, - const gfx::Size& size, - gfx::BufferFormat format, - gfx::BufferUsage usage, - uint32_t texture_id) { +void MailboxToSurfaceBridge::DestroySharedImage( + const gpu::MailboxHolder& mailbox_holder) { TRACE_EVENT0("gpu", __FUNCTION__); DCHECK(IsConnected()); - auto img = gl_->CreateImageCHROMIUM(buffer->AsClientBuffer(), size.width(), - size.height(), GL_RGBA); - - gl_->BindTexture(GL_TEXTURE_2D, texture_id); - gl_->BindTexImage2DCHROMIUM(GL_TEXTURE_2D, img); - gl_->BindTexture(GL_TEXTURE_2D, 0); - - return img; -} - -void MailboxToSurfaceBridge::UnbindSharedBuffer(GLuint image_id, - GLuint texture_id) { - TRACE_EVENT0("gpu", __FUNCTION__); - DCHECK(IsConnected()); - - gl_->BindTexture(GL_TEXTURE_2D, texture_id); - gl_->ReleaseTexImage2DCHROMIUM(GL_TEXTURE_2D, image_id); - gl_->BindTexture(GL_TEXTURE_2D, 0); - gl_->DestroyImageCHROMIUM(image_id); + auto* sii = context_provider_->SharedImageInterface(); + DCHECK(sii); + sii->DestroySharedImage(mailbox_holder.sync_token, mailbox_holder.mailbox); } void MailboxToSurfaceBridge::DestroyContext() {
diff --git a/chrome/browser/android/vr/mailbox_to_surface_bridge.h b/chrome/browser/android/vr/mailbox_to_surface_bridge.h index 1103bc6..86f7365 100644 --- a/chrome/browser/android/vr/mailbox_to_surface_bridge.h +++ b/chrome/browser/android/vr/mailbox_to_surface_bridge.h
@@ -20,12 +20,12 @@ } // namespace gl namespace gfx { -class GpuMemoryBuffer; +class ColorSpace; } namespace gpu { class ContextSupport; -struct Mailbox; +class GpuMemoryBufferImplAndroidHardwareBuffer; struct MailboxHolder; struct SyncToken; namespace gles2 { @@ -58,13 +58,13 @@ // // To use entirely on the GL thread: // Call CreateAndBindContextProvider(callback) from your thread. - // When the callback is invoked, the object is ready for GL calls - // such as CreateMailboxTexture(). + // When the callback is invoked, the object is ready for calls that use the + // context, such as CreateSharedImage(). // // To create on one thread and use GL on another: // Call CreateUnboundContextProvider(callback) and then make sure // to call BindContextProviderToCurrentThread() from your GL - // thread afterwards before making an GL-related calls. + // thread afterwards before making a context-related calls. // Asynchronously create the context using the surface provided by an earlier // CreateSurface call, or an offscreen context if that wasn't called. Also @@ -77,9 +77,9 @@ // wasn't run on the GL thread. The provided callback is run on the // constructor thread. After that, you can pass the MailboxToSurfaceBridge // to another thread. You must call BindContextProviderToCurrentThread() - // on the target GL thread before using any GL methods. - // The GL methods check that they are called on this thread, so there - // will be a DCHECK error if they are not used consistently. + // on the target GL thread before using any context-related methods. + // The context-related methods check that they are called on this thread, so + // there will be a DCHECK error if they are not used consistently. virtual void CreateUnboundContextProvider(base::OnceClosure callback); // Client must call this on the target (GL) thread after @@ -112,21 +112,19 @@ const gpu::SyncToken& sync_token, base::OnceCallback<void(std::unique_ptr<gfx::GpuFence>)> callback); - // Creates a texture and binds it to a newly created mailbox. Returns its - // mailbox and texture ID in the command buffer context. (Don't use that - // in the local GL context, it's not valid there.) - virtual uint32_t CreateMailboxTexture(gpu::Mailbox* mailbox); - - // Creates a GLImage from the |buffer| and binds it to the supplied texture_id - // in the GPU process. Returns the image ID in the command buffer context. + // Creates a shared image bound to |buffer|. Returns a mailbox holder that + // references the shared image with a sync token representing a point after + // the creation. Caller must call DestroySharedImage to free the shared image. // Does not take ownership of |buffer| or retain any references to it. - uint32_t BindSharedBufferImage(gfx::GpuMemoryBuffer* buffer, - const gfx::Size& size, - gfx::BufferFormat format, - gfx::BufferUsage usage, - uint32_t texture_id); + gpu::MailboxHolder CreateSharedImage( + gpu::GpuMemoryBufferImplAndroidHardwareBuffer* buffer, + const gfx::ColorSpace& color_space, + uint32_t usage); - void UnbindSharedBuffer(uint32_t image_id, uint32_t texture_id); + // Destroys a shared image created by CreateSharedImage. The mailbox_holder's + // sync_token must have been updated to a sync token after the last use of the + // shared image. + void DestroySharedImage(const gpu::MailboxHolder& mailbox_holder); private: void CreateContextProviderInternal();
diff --git a/chrome/browser/android/vr/web_xr_presentation_state.cc b/chrome/browser/android/vr/web_xr_presentation_state.cc index ddfecd7..41c64fc5 100644 --- a/chrome/browser/android/vr/web_xr_presentation_state.cc +++ b/chrome/browser/android/vr/web_xr_presentation_state.cc
@@ -5,7 +5,6 @@ #include "chrome/browser/android/vr/web_xr_presentation_state.h" #include "base/trace_event/trace_event.h" -#include "gpu/command_buffer/common/mailbox_holder.h" #include "gpu/ipc/common/gpu_memory_buffer_impl_android_hardware_buffer.h" #include "ui/gl/gl_fence.h" #include "ui/gl/gl_image_egl.h" @@ -115,6 +114,16 @@ return can_cancel; } +std::vector<std::unique_ptr<WebXrSharedBuffer>> +WebXrPresentationState::TakeSharedBuffers() { + std::vector<std::unique_ptr<WebXrSharedBuffer>> shared_buffers; + for (auto& frame : frames_storage_) { + if (frame->shared_buffer) + shared_buffers.emplace_back(std::move(frame->shared_buffer)); + } + return shared_buffers; +} + void WebXrPresentationState::EndPresentation() { TRACE_EVENT0("gpu", __FUNCTION__);
diff --git a/chrome/browser/android/vr/web_xr_presentation_state.h b/chrome/browser/android/vr/web_xr_presentation_state.h index efb8622..97bfad7 100644 --- a/chrome/browser/android/vr/web_xr_presentation_state.h +++ b/chrome/browser/android/vr/web_xr_presentation_state.h
@@ -12,6 +12,7 @@ #include "base/containers/queue.h" #include "base/macros.h" #include "base/memory/ref_counted.h" +#include "gpu/command_buffer/common/mailbox_holder.h" #include "ui/gfx/geometry/size.h" #include "ui/gfx/transform.h" @@ -22,7 +23,6 @@ namespace gpu { class GpuMemoryBufferImplAndroidHardwareBuffer; -struct MailboxHolder; } // namespace gpu namespace vr { @@ -79,9 +79,7 @@ std::unique_ptr<gpu::GpuMemoryBufferImplAndroidHardwareBuffer> gmb; // Resources in the remote GPU process command buffer context - std::unique_ptr<gpu::MailboxHolder> mailbox_holder; - uint32_t remote_texture = 0; - uint32_t remote_image = 0; + gpu::MailboxHolder mailbox_holder; // Resources in the local GL context uint32_t local_texture = 0; @@ -178,6 +176,11 @@ bool mailbox_bridge_ready() { return mailbox_bridge_ready_; } void NotifyMailboxBridgeReady() { mailbox_bridge_ready_ = true; } + // Extracts the shared buffers from all frames, resetting said frames to an + // invalid state. + // This is intended for resource cleanup, after EndPresentation was called. + std::vector<std::unique_ptr<WebXrSharedBuffer>> TakeSharedBuffers(); + // Used by WebVrCanAnimateFrame() to detect when ui_->CanSendWebVrVSync() // transitions from false to true, as part of starting the incoming frame // timeout.
diff --git a/chrome/browser/chromeos/BUILD.gn b/chrome/browser/chromeos/BUILD.gn index 2ff583c6..4ca756f 100644 --- a/chrome/browser/chromeos/BUILD.gn +++ b/chrome/browser/chromeos/BUILD.gn
@@ -101,6 +101,7 @@ "//chromeos/dbus/cryptohome:attestation_proto", "//chromeos/dbus/cryptohome:cryptohome_proto", "//chromeos/dbus/cryptohome:cryptohome_signkey_proto", + "//chromeos/dbus/cups_proxy", "//chromeos/dbus/kerberos", "//chromeos/dbus/kerberos:kerberos_proto", "//chromeos/dbus/machine_learning", @@ -1618,8 +1619,6 @@ "policy/device_policy_decoder_chromeos.cc", "policy/device_policy_decoder_chromeos.h", "policy/device_policy_remover.h", - "policy/device_status_collector.cc", - "policy/device_status_collector.h", "policy/device_wallpaper_image_handler.cc", "policy/device_wallpaper_image_handler.h", "policy/device_wifi_allowed_handler.cc", @@ -1700,6 +1699,10 @@ "policy/server_backed_state_keys_broker.h", "policy/single_app_install_event_log.cc", "policy/single_app_install_event_log.h", + "policy/status_collector/device_status_collector.cc", + "policy/status_collector/device_status_collector.h", + "policy/status_collector/status_collector.cc", + "policy/status_collector/status_collector.h", "policy/status_uploader.cc", "policy/status_uploader.h", "policy/system_log_uploader.cc", @@ -1939,6 +1942,8 @@ "smb_client/smb_url.h", "smb_client/temp_file_manager.cc", "smb_client/temp_file_manager.h", + "startup_settings_cache.cc", + "startup_settings_cache.h", "system/automatic_reboot_manager.cc", "system/automatic_reboot_manager.h", "system/automatic_reboot_manager_observer.h", @@ -2579,6 +2584,7 @@ "smb_client/smb_task_queue_unittest.cc", "smb_client/smb_url_unittest.cc", "smb_client/temp_file_manager_unittest.cc", + "startup_settings_cache_unittest.cc", "system/automatic_reboot_manager_unittest.cc", "system/device_disabling_manager_unittest.cc", "system/procfs_util_unittest.cc",
diff --git a/chrome/browser/chromeos/child_accounts/consumer_status_reporting_service.cc b/chrome/browser/chromeos/child_accounts/consumer_status_reporting_service.cc index bdf0bbf..c9b7b904 100644 --- a/chrome/browser/chromeos/child_accounts/consumer_status_reporting_service.cc +++ b/chrome/browser/chromeos/child_accounts/consumer_status_reporting_service.cc
@@ -9,7 +9,7 @@ #include "base/threading/thread_task_runner_handle.h" #include "chrome/browser/chromeos/child_accounts/event_based_status_reporting_service_factory.h" #include "chrome/browser/chromeos/child_accounts/usage_time_limit_processor.h" -#include "chrome/browser/chromeos/policy/device_status_collector.h" +#include "chrome/browser/chromeos/policy/status_collector/device_status_collector.h" #include "chrome/browser/chromeos/policy/status_uploader.h" #include "chrome/browser/chromeos/policy/user_cloud_policy_manager_chromeos.h" #include "chrome/browser/chromeos/policy/user_policy_manager_factory_chromeos.h" @@ -51,6 +51,8 @@ DCHECK(pref_service->GetInitializationStatus() != PrefService::INITIALIZATION_STATUS_WAITING); + // We immediately upload a status report after Time Limits policy changes. + // Make sure we listen for those events. pref_change_registrar_ = std::make_unique<PrefChangeRegistrar>(); pref_change_registrar_->Init(pref_service); pref_change_registrar_->Add( @@ -83,6 +85,7 @@ VLOG(1) << "Creating status uploader for consumer status reporting."; day_reset_time_ = new_day_reset_time; + status_uploader_ = std::make_unique<policy::StatusUploader>( client, std::make_unique<policy::DeviceStatusCollector>( @@ -102,8 +105,10 @@ } base::TimeDelta ConsumerStatusReportingService::GetChildScreenTime() const { - return const_cast<policy::DeviceStatusCollector*>( - status_uploader_->device_status_collector()) + // Notice that this cast works because we know that |status_uploader_| has a + // DeviceStatusCollector (see above). + return static_cast<policy::DeviceStatusCollector*>( + status_uploader_->status_collector()) ->GetActiveChildScreenTime(); }
diff --git a/chrome/browser/chromeos/chrome_browser_main_chromeos.cc b/chrome/browser/chromeos/chrome_browser_main_chromeos.cc index 4acfefe..8527557 100644 --- a/chrome/browser/chromeos/chrome_browser_main_chromeos.cc +++ b/chrome/browser/chromeos/chrome_browser_main_chromeos.cc
@@ -101,6 +101,7 @@ #include "chrome/browser/chromeos/settings/device_oauth2_token_service_factory.h" #include "chrome/browser/chromeos/settings/device_settings_service.h" #include "chrome/browser/chromeos/settings/shutdown_policy_forwarder.h" +#include "chrome/browser/chromeos/startup_settings_cache.h" #include "chrome/browser/chromeos/system/input_device_settings.h" #include "chrome/browser/chromeos/system/user_removal_manager.h" #include "chrome/browser/chromeos/ui/low_disk_notification.h" @@ -152,6 +153,7 @@ #include "components/account_id/account_id.h" #include "components/arc/arc_util.h" #include "components/device_event_log/device_event_log.h" +#include "components/language/core/browser/pref_names.h" #include "components/metrics/metrics_service.h" #include "components/ownership/owner_key_util.h" #include "components/prefs/pref_service.h" @@ -1202,6 +1204,11 @@ g_browser_process->platform_part()->ShutdownSessionManager(); // Ash needs to be closed before UserManager is destroyed. g_browser_process->platform_part()->DestroyChromeUserManager(); + + // See comment at startup_settings_cache::ReadAppLocale() for why we do this. + startup_settings_cache::WriteAppLocale( + g_browser_process->local_state()->GetString( + language::prefs::kApplicationLocale)); } void ChromeBrowserMainPartsChromeos::PostDestroyThreads() {
diff --git a/chrome/browser/chromeos/dbus/dbus_helper.cc b/chrome/browser/chromeos/dbus/dbus_helper.cc index c12fba2..9f6fd9b 100644 --- a/chrome/browser/chromeos/dbus/dbus_helper.cc +++ b/chrome/browser/chromeos/dbus/dbus_helper.cc
@@ -13,6 +13,7 @@ #include "chromeos/dbus/audio/cras_audio_client.h" #include "chromeos/dbus/auth_policy/auth_policy_client.h" #include "chromeos/dbus/biod/biod_client.h" +#include "chromeos/dbus/cups_proxy/cups_proxy_client.h" #include "chromeos/dbus/dbus_thread_manager.h" #include "chromeos/dbus/kerberos/kerberos_client.h" #include "chromeos/dbus/machine_learning/machine_learning_client.h" @@ -57,6 +58,7 @@ BiodClient::Initialize(bus); // For device::Fingerprint. CrasAudioClient::Initialize(bus); CryptohomeClient::Initialize(bus); + CupsProxyClient::Initialize(bus); KerberosClient::Initialize(bus); MachineLearningClient::Initialize(bus); MediaAnalyticsClient::Initialize(bus); @@ -70,6 +72,7 @@ BiodClient::InitializeFake(); // For device::Fingerprint. CrasAudioClient::InitializeFake(); CryptohomeClient::InitializeFake(); + CupsProxyClient::InitializeFake(); KerberosClient::InitializeFake(); MachineLearningClient::InitializeFake(); MediaAnalyticsClient::InitializeFake(); @@ -96,6 +99,7 @@ MediaAnalyticsClient::Shutdown(); MachineLearningClient::Shutdown(); KerberosClient::Shutdown(); + CupsProxyClient::Shutdown(); CryptohomeClient::Shutdown(); CrasAudioClient::Shutdown(); BiodClient::Shutdown();
diff --git a/chrome/browser/chromeos/extensions/quick_unlock_private/quick_unlock_private_api_unittest.cc b/chrome/browser/chromeos/extensions/quick_unlock_private/quick_unlock_private_api_unittest.cc index bfef5980..1c8b56d8 100644 --- a/chrome/browser/chromeos/extensions/quick_unlock_private/quick_unlock_private_api_unittest.cc +++ b/chrome/browser/chromeos/extensions/quick_unlock_private/quick_unlock_private_api_unittest.cc
@@ -158,7 +158,7 @@ fake_user_manager_->CreateLocalState(); // Rebuild quick unlock state. - quick_unlock::EnableForTesting(); + quick_unlock::EnabledForTesting(true); quick_unlock::PinBackend::ResetForTesting(); base::RunLoop().RunUntilIdle(); @@ -185,6 +185,7 @@ } void TearDown() override { + quick_unlock::EnabledForTesting(false); quick_unlock::DisablePinByPolicyForTesting(false); base::RunLoop().RunUntilIdle();
diff --git a/chrome/browser/chromeos/login/active_directory_login_browsertest.cc b/chrome/browser/chromeos/login/active_directory_login_browsertest.cc index 4024605..867628f 100644 --- a/chrome/browser/chromeos/login/active_directory_login_browsertest.cc +++ b/chrome/browser/chromeos/login/active_directory_login_browsertest.cc
@@ -5,318 +5,75 @@ #include <string> #include "base/base_paths.h" -#include "base/command_line.h" #include "base/environment.h" -#include "base/location.h" #include "base/path_service.h" -#include "base/run_loop.h" -#include "base/single_thread_task_runner.h" #include "base/strings/string_util.h" #include "base/strings/utf_string_conversions.h" -#include "base/threading/thread_task_runner_handle.h" #include "chrome/browser/chrome_notification_types.h" #include "chrome/browser/chromeos/authpolicy/kerberos_files_handler.h" -#include "chrome/browser/chromeos/login/active_directory_test_helper.h" -#include "chrome/browser/chromeos/login/login_manager_test.h" -#include "chrome/browser/chromeos/login/login_shelf_test_helper.h" -#include "chrome/browser/chromeos/login/startup_utils.h" -#include "chrome/browser/chromeos/login/test/js_checker.h" -#include "chrome/browser/chromeos/login/test/oobe_screen_waiter.h" -#include "chrome/browser/chromeos/login/ui/login_display_host.h" +#include "chrome/browser/chromeos/login/test/active_directory_login_mixin.h" +#include "chrome/browser/chromeos/login/test/oobe_base_test.h" #include "chrome/browser/chromeos/login/wizard_controller.h" -#include "chrome/browser/ui/webui/chromeos/login/signin_screen_handler.h" -#include "chrome/grit/generated_resources.h" -#include "chrome/test/base/in_process_browser_test.h" -#include "chrome/test/base/interactive_test_utils.h" -#include "chromeos/constants/chromeos_switches.h" #include "chromeos/dbus/auth_policy/fake_auth_policy_client.h" -#include "chromeos/dbus/dbus_thread_manager.h" -#include "chromeos/tpm/stub_install_attributes.h" -#include "components/account_id/account_id.h" #include "components/user_manager/user_names.h" #include "content/public/common/network_service_util.h" #include "content/public/common/service_manager_connection.h" #include "content/public/common/service_names.mojom.h" -#include "content/public/test/browser_test_utils.h" #include "content/public/test/test_utils.h" #include "mojo/public/cpp/bindings/sync_call_restrictions.h" #include "services/network/public/mojom/network_service_test.mojom.h" #include "services/service_manager/public/cpp/connector.h" -#include "ui/base/l10n/l10n_util.h" -#include "ui/gfx/geometry/test/rect_test_util.h" - -using ::gfx::test::RectContains; namespace chromeos { + namespace { -const char kPassword[] = "password"; - -constexpr char kGaiaSigninId[] = "signin-frame-dialog"; -constexpr char kAdOfflineAuthId[] = "offline-ad-auth"; - constexpr char kTestActiveDirectoryUser[] = "test-user"; constexpr char kTestUserRealm[] = "user.realm"; -constexpr char kAdMachineInput[] = "machineNameInput"; -constexpr char kAdMoreOptionsButton[] = "moreOptionsBtn"; -constexpr char kAdUserInput[] = "userInput"; -constexpr char kAdPasswordInput[] = "passwordInput"; -constexpr char kAdCredsButton[] = "nextButton"; -constexpr char kAdAutocompleteRealm[] = "$.userInput.querySelector('span')"; - -constexpr char kAdPasswordChangeId[] = "active-directory-password-change"; -constexpr char kAdAnimatedPages[] = "animatedPages"; -constexpr char kAdOldPasswordInput[] = "oldPassword"; -constexpr char kAdNewPassword1Input[] = "newPassword1"; -constexpr char kAdNewPassword2Input[] = "newPassword2"; -constexpr char kAdPasswordChangeFormId[] = "inputForm"; -constexpr char kFormButtonId[] = "button"; +constexpr char kPassword[] = "password"; constexpr char kNewPassword[] = "new_password"; constexpr char kDifferentNewPassword[] = "different_new_password"; -constexpr char kNavigationId[] = "navigation"; -constexpr char kCloseButtonId[] = "closeButton"; +void AssertNetworkServiceEnvEquals(const std::string& name, + const std::string& expected_value) { + std::string value; + if (content::IsOutOfProcessNetworkService()) { + network::mojom::NetworkServiceTestPtr network_service_test; + content::ServiceManagerConnection::GetForProcess() + ->GetConnector() + ->BindInterface(content::mojom::kNetworkServiceName, + &network_service_test); + mojo::ScopedAllowSyncCallForTesting allow_sync_call; + network_service_test->GetEnvironmentVariableValue(name, &value); + } else { + // If the network service is running in-process, we can read the + // environment variable directly. + base::Environment::Create()->GetVar(name, &value); + } + EXPECT_EQ(value, expected_value); +} -class ActiveDirectoryLoginTest : public LoginManagerTest { +class ActiveDirectoryLoginTest : public OobeBaseTest { public: ActiveDirectoryLoginTest() - : LoginManagerTest(true, true), - // Using the same realm as supervised user domain. Should be treated as - // normal realm. + : OobeBaseTest(), + // Using the same realm as supervised user domain. Should be treated + // as normal realm. test_realm_(user_manager::kSupervisedUserDomain), - test_user_(kTestActiveDirectoryUser + ("@" + test_realm_)), - install_attributes_( - chromeos::StubInstallAttributes::CreateActiveDirectoryManaged( - test_realm_, - "device_id")) {} + test_user_(kTestActiveDirectoryUser + ("@" + test_realm_)) {} ~ActiveDirectoryLoginTest() override = default; - void SetUpInProcessBrowserTestFixture() override { - LoginManagerTest::SetUpInProcessBrowserTestFixture(); - - // This is called before ChromeBrowserMain initializes the fake dbus - // clients, and DisableOperationDelayForTesting() needs to be called before - // other ChromeBrowserMain initialization occurs. - AuthPolicyClient::InitializeFake(); - FakeAuthPolicyClient::Get()->DisableOperationDelayForTesting(); - } - - void SetUpCommandLine(base::CommandLine* command_line) override { - LoginManagerTest::SetUpCommandLine(command_line); - command_line->AppendSwitch(switches::kOobeSkipPostLogin); - } - - void SetUpOnMainThread() override { - // Set the threshold to a max value to disable the offline message screen - // on slow configurations like MSAN, where it otherwise triggers on every - // run. - LoginDisplayHost::default_host() - ->GetOobeUI() - ->signin_screen_handler() - ->SetOfflineTimeoutForTesting(base::TimeDelta::Max()); - LoginManagerTest::SetUpOnMainThread(); - } - - void TriggerPasswordChangeScreen() { - OobeScreenWaiter screen_waiter( - OobeScreen::SCREEN_ACTIVE_DIRECTORY_PASSWORD_CHANGE); - - fake_auth_policy_client()->set_auth_error( - authpolicy::ERROR_PASSWORD_EXPIRED); - SubmitActiveDirectoryCredentials(test_user_, kPassword); - screen_waiter.Wait(); - TestAdPasswordChangeError(std::string()); - } - - void ClosePasswordChangeScreen() { - test::OobeJS().TapOnPath( - {kAdPasswordChangeId, kNavigationId, kCloseButtonId}); - } - - void ExpectValid(const std::string& parent_id, - const std::string& child_id, - bool valid) { - std::string js = - test::GetOobeElementPath({parent_id, child_id}) + ".invalid"; - if (valid) - test::OobeJS().ExpectFalse(js); - else - test::OobeJS().ExpectTrue(js); - } - - // Checks if Active Directory login is visible. - void TestLoginVisible() { - OobeScreenWaiter screen_waiter(OobeScreen::SCREEN_GAIA_SIGNIN); - screen_waiter.Wait(); - // Checks if Gaia signin is hidden. - test::OobeJS().ExpectHidden(kGaiaSigninId); - - // Checks if Active Directory signin is visible. - test::OobeJS().ExpectVisible(kAdOfflineAuthId); - test::OobeJS().ExpectHiddenPath({kAdOfflineAuthId, kAdMachineInput}); - test::OobeJS().ExpectHiddenPath({kAdOfflineAuthId, kAdMoreOptionsButton}); - test::OobeJS().ExpectVisiblePath({kAdOfflineAuthId, kAdUserInput}); - test::OobeJS().ExpectVisiblePath({kAdOfflineAuthId, kAdPasswordInput}); - - std::string autocomplete_domain_ui; - base::TrimString( - test::OobeJS().GetString( - JSElement(kAdOfflineAuthId, kAdAutocompleteRealm) + ".innerText"), - base::kWhitespaceASCII, &autocomplete_domain_ui); - // Checks if realm is set to autocomplete username. - EXPECT_EQ(autocomplete_realm_, autocomplete_domain_ui); - - EXPECT_TRUE(LoginShelfTestHelper().IsLoginShelfShown()); - } - - // Checks if Active Directory password change screen is shown. - void TestPasswordChangeVisible() { - // Checks if Gaia signin is hidden. - test::OobeJS().ExpectHidden(kGaiaSigninId); - // Checks if Active Directory signin is visible. - test::OobeJS().ExpectVisible(kAdPasswordChangeId); - test::OobeJS().ExpectTrue( - test::GetOobeElementPath({kAdPasswordChangeId, kAdAnimatedPages}) + - ".selected == 0"); - test::OobeJS().ExpectVisiblePath( - {kAdPasswordChangeId, kNavigationId, kCloseButtonId}); - } - - // Checks if user input is marked as invalid. - void TestUserError() { - TestLoginVisible(); - ExpectValid(kAdOfflineAuthId, kAdUserInput, false); - } - - void SetUserInput(const std::string& value) { - test::OobeJS().TypeIntoPath(value, {kAdOfflineAuthId, kAdUserInput}); - } - - void TestUserInput(const std::string& value) { - test::OobeJS().ExpectEQ( - test::GetOobeElementPath({kAdOfflineAuthId, kAdUserInput}) + ".value", - value); - } - - // Checks if password input is marked as invalid. - void TestPasswordError() { - TestLoginVisible(); - ExpectValid(kAdOfflineAuthId, kAdPasswordInput, false); - } - - // Checks that machine, password and user inputs are valid. - void TestNoError() { - TestLoginVisible(); - ExpectValid(kAdOfflineAuthId, kAdMachineInput, true); - ExpectValid(kAdOfflineAuthId, kAdUserInput, true); - ExpectValid(kAdOfflineAuthId, kAdPasswordInput, true); - } - - // Checks if autocomplete domain is visible for the user input. - void TestDomainVisible() { - test::OobeJS().ExpectTrue( - "!" + JSElement(kAdOfflineAuthId, kAdAutocompleteRealm) + ".hidden"); - } - - // Checks if autocomplete domain is hidden for the user input. - void TestDomainHidden() { - test::OobeJS().ExpectTrue( - JSElement(kAdOfflineAuthId, kAdAutocompleteRealm) + ".hidden"); - } - - // Checks if Active Directory password change screen is shown. Also checks if - // |invalid_element| is invalidated and all the other elements are valid. - void TestAdPasswordChangeError(const std::string& invalid_element) { - TestPasswordChangeVisible(); - for (const char* element : - {kAdOldPasswordInput, kAdNewPassword1Input, kAdNewPassword2Input}) { - std::string js_assertion = - test::GetOobeElementPath({kAdPasswordChangeId, element}) + - ".isInvalid"; - if (element != invalid_element) - js_assertion = "!" + js_assertion; - test::OobeJS().ExpectTrue(js_assertion); - } - } - - // Sets username and password for the Active Directory login and submits it. - void SubmitActiveDirectoryCredentials(const std::string& username, - const std::string& password) { - test::OobeJS().TypeIntoPath(username, {kAdOfflineAuthId, kAdUserInput}); - test::OobeJS().TypeIntoPath(password, {kAdOfflineAuthId, kAdPasswordInput}); - test::OobeJS().TapOnPath({kAdOfflineAuthId, kAdCredsButton}); - } - - // Sets username and password for the Active Directory login and submits it. - void SubmitActiveDirectoryPasswordChangeCredentials( - const std::string& old_password, - const std::string& new_password1, - const std::string& new_password2) { - test::OobeJS().TypeIntoPath(old_password, - {kAdPasswordChangeId, kAdOldPasswordInput}); - test::OobeJS().TypeIntoPath(new_password1, - {kAdPasswordChangeId, kAdNewPassword1Input}); - test::OobeJS().TypeIntoPath(new_password2, - {kAdPasswordChangeId, kAdNewPassword2Input}); - test::OobeJS().TapOnPath( - {kAdPasswordChangeId, kAdPasswordChangeFormId, kFormButtonId}); - } - - void SetupActiveDirectoryJSNotifications() { - test::OobeJS().Evaluate( - "var testInvalidateAd = login.GaiaSigninScreen.invalidateAd;" - "login.GaiaSigninScreen.invalidateAd = function(user, errorState) {" - " testInvalidateAd(user, errorState);" - " window.domAutomationController.send('ShowAuthError');" - "}"); - } - - void WaitForMessage(content::DOMMessageQueue* message_queue, - const std::string& expected_message) { - std::string message; - do { - ASSERT_TRUE(message_queue->WaitForMessage(&message)); - } while (message != expected_message); - } - - void AssertNetworkServiceEnvEquals(const std::string& name, - const std::string& expected_value) { - std::string value; - if (content::IsOutOfProcessNetworkService()) { - network::mojom::NetworkServiceTestPtr network_service_test; - content::ServiceManagerConnection::GetForProcess() - ->GetConnector() - ->BindInterface(content::mojom::kNetworkServiceName, - &network_service_test); - mojo::ScopedAllowSyncCallForTesting allow_sync_call; - network_service_test->GetEnvironmentVariableValue(name, &value); - } else { - // If the network service is running in-process, we can read the - // environment variable directly. - base::Environment::Create()->GetVar(name, &value); - } - EXPECT_EQ(value, expected_value); - } - protected: - // Returns string representing element with id=|element_id| inside Active - // Directory login element. - std::string JSElement(const std::string& parent_id, - const std::string& selector) { - return "document.querySelector('#" + parent_id + "')." + selector; - } FakeAuthPolicyClient* fake_auth_policy_client() { return FakeAuthPolicyClient::Get(); } const std::string test_realm_; const std::string test_user_; - std::string autocomplete_realm_; + ActiveDirectoryLoginMixin ad_login_{&mixin_host_, test_realm_}; private: - chromeos::ScopedStubInstallAttributes install_attributes_; - DISALLOW_COPY_AND_ASSIGN(ActiveDirectoryLoginTest); }; @@ -331,42 +88,40 @@ ->set_login_screen_domain_auto_complete(kTestUserRealm); fake_auth_policy_client()->set_device_policy(device_settings); autocomplete_realm_ = "@" + std::string(kTestUserRealm); + ad_login_.set_autocomplete_realm(autocomplete_realm_); } + std::string autocomplete_realm_; + private: DISALLOW_COPY_AND_ASSIGN(ActiveDirectoryLoginAutocompleteTest); }; + } // namespace -// Declares a PRE_ test that calls StartupUtils::MarkOobeCompleted() and the -// test itself. -#define IN_PROC_BROWSER_TEST_F_WITH_PRE(class_name, test_name) \ - IN_PROC_BROWSER_TEST_F(class_name, PRE_##test_name) { \ - StartupUtils::MarkOobeCompleted(); \ - } \ - IN_PROC_BROWSER_TEST_F(class_name, test_name) - // Test successful Active Directory login. -IN_PROC_BROWSER_TEST_F_WITH_PRE(ActiveDirectoryLoginTest, LoginSuccess) { +IN_PROC_BROWSER_TEST_F(ActiveDirectoryLoginTest, LoginSuccess) { + OobeBaseTest::WaitForSigninScreen(); ASSERT_TRUE(InstallAttributes::Get()->IsActiveDirectoryManaged()); - TestNoError(); - TestDomainHidden(); + ad_login_.TestNoError(); + ad_login_.TestDomainHidden(); content::WindowedNotificationObserver session_start_waiter( chrome::NOTIFICATION_SESSION_STARTED, content::NotificationService::AllSources()); - SubmitActiveDirectoryCredentials(test_user_, kPassword); + ad_login_.SubmitActiveDirectoryCredentials(test_user_, kPassword); session_start_waiter.Wait(); } // Tests that the Kerberos SSO environment variables are set correctly after // an Active Directory log in. -IN_PROC_BROWSER_TEST_F_WITH_PRE(ActiveDirectoryLoginTest, KerberosVarsCopied) { - TestNoError(); - TestDomainHidden(); +IN_PROC_BROWSER_TEST_F(ActiveDirectoryLoginTest, KerberosVarsCopied) { + OobeBaseTest::WaitForSigninScreen(); + ad_login_.TestNoError(); + ad_login_.TestDomainHidden(); content::WindowedNotificationObserver session_start_waiter( chrome::NOTIFICATION_SESSION_STARTED, content::NotificationService::AllSources()); - SubmitActiveDirectoryCredentials(test_user_, kPassword); + ad_login_.SubmitActiveDirectoryCredentials(test_user_, kPassword); session_start_waiter.Wait(); base::FilePath dir; @@ -380,166 +135,164 @@ } // Test different UI errors for Active Directory login. -IN_PROC_BROWSER_TEST_F_WITH_PRE(ActiveDirectoryLoginTest, LoginErrors) { +IN_PROC_BROWSER_TEST_F(ActiveDirectoryLoginTest, LoginErrors) { + OobeBaseTest::WaitForSigninScreen(); ASSERT_TRUE(InstallAttributes::Get()->IsActiveDirectoryManaged()); - SetupActiveDirectoryJSNotifications(); - TestNoError(); - TestDomainHidden(); + ad_login_.TestNoError(); + ad_login_.TestDomainHidden(); - content::DOMMessageQueue message_queue; + ad_login_.SubmitActiveDirectoryCredentials("", ""); + ad_login_.TestUserError(); + ad_login_.TestDomainHidden(); - SubmitActiveDirectoryCredentials("", ""); - TestUserError(); - TestDomainHidden(); + ad_login_.SubmitActiveDirectoryCredentials(test_user_, ""); + ad_login_.TestPasswordError(); + ad_login_.TestDomainHidden(); - SubmitActiveDirectoryCredentials(test_user_, ""); - TestPasswordError(); - TestDomainHidden(); - - SubmitActiveDirectoryCredentials(std::string(kTestActiveDirectoryUser) + "@", - kPassword); - WaitForMessage(&message_queue, "\"ShowAuthError\""); - TestUserError(); - TestDomainHidden(); + ad_login_.SubmitActiveDirectoryCredentials( + std::string(kTestActiveDirectoryUser) + "@", kPassword); + ad_login_.WaitForAuthError(); + ad_login_.TestUserError(); + ad_login_.TestDomainHidden(); fake_auth_policy_client()->set_auth_error(authpolicy::ERROR_BAD_USER_NAME); - SubmitActiveDirectoryCredentials(test_user_, kPassword); - WaitForMessage(&message_queue, "\"ShowAuthError\""); - TestUserError(); - TestDomainHidden(); + ad_login_.SubmitActiveDirectoryCredentials(test_user_, kPassword); + ad_login_.WaitForAuthError(); + ad_login_.TestUserError(); + ad_login_.TestDomainHidden(); fake_auth_policy_client()->set_auth_error(authpolicy::ERROR_BAD_PASSWORD); - SubmitActiveDirectoryCredentials(test_user_, kPassword); - WaitForMessage(&message_queue, "\"ShowAuthError\""); - TestPasswordError(); - TestDomainHidden(); + ad_login_.SubmitActiveDirectoryCredentials(test_user_, kPassword); + ad_login_.WaitForAuthError(); + ad_login_.TestPasswordError(); + ad_login_.TestDomainHidden(); fake_auth_policy_client()->set_auth_error(authpolicy::ERROR_UNKNOWN); - SubmitActiveDirectoryCredentials(test_user_, kPassword); - WaitForMessage(&message_queue, "\"ShowAuthError\""); + ad_login_.SubmitActiveDirectoryCredentials(test_user_, kPassword); + ad_login_.WaitForAuthError(); // Inputs are not invalidated for the unknown error. - TestNoError(); - TestDomainHidden(); + ad_login_.TestNoError(); + ad_login_.TestDomainHidden(); } // Test successful Active Directory login from the password change screen. -IN_PROC_BROWSER_TEST_F_WITH_PRE(ActiveDirectoryLoginTest, - PasswordChange_LoginSuccess) { +IN_PROC_BROWSER_TEST_F(ActiveDirectoryLoginTest, PasswordChange_LoginSuccess) { + OobeBaseTest::WaitForSigninScreen(); ASSERT_TRUE(InstallAttributes::Get()->IsActiveDirectoryManaged()); - TestLoginVisible(); - TestDomainHidden(); + ad_login_.TestLoginVisible(); + ad_login_.TestDomainHidden(); - TriggerPasswordChangeScreen(); + ad_login_.TriggerPasswordChangeScreen(); // Password accepted by AuthPolicyClient. fake_auth_policy_client()->set_auth_error(authpolicy::ERROR_NONE); content::WindowedNotificationObserver session_start_waiter( chrome::NOTIFICATION_SESSION_STARTED, content::NotificationService::AllSources()); - SubmitActiveDirectoryPasswordChangeCredentials(kPassword, kNewPassword, - kNewPassword); + ad_login_.SubmitActiveDirectoryPasswordChangeCredentials( + kPassword, kNewPassword, kNewPassword); session_start_waiter.Wait(); } // Test different UI errors for Active Directory password change screen. -IN_PROC_BROWSER_TEST_F_WITH_PRE(ActiveDirectoryLoginTest, - PasswordChange_UIErrors) { +IN_PROC_BROWSER_TEST_F(ActiveDirectoryLoginTest, PasswordChange_UIErrors) { + OobeBaseTest::WaitForSigninScreen(); ASSERT_TRUE(InstallAttributes::Get()->IsActiveDirectoryManaged()); - TestLoginVisible(); - TestDomainHidden(); + ad_login_.TestLoginVisible(); + ad_login_.TestDomainHidden(); - TriggerPasswordChangeScreen(); + ad_login_.TriggerPasswordChangeScreen(); // Password rejected by UX. // Empty passwords. - SubmitActiveDirectoryPasswordChangeCredentials("", "", ""); - TestAdPasswordChangeError(kAdOldPasswordInput); + ad_login_.SubmitActiveDirectoryPasswordChangeCredentials("", "", ""); + ad_login_.TestPasswordChangeOldPasswordError(); // Empty new password. - SubmitActiveDirectoryPasswordChangeCredentials(kPassword, "", ""); - TestAdPasswordChangeError(kAdNewPassword1Input); + ad_login_.SubmitActiveDirectoryPasswordChangeCredentials(kPassword, "", ""); + ad_login_.TestPasswordChangeNewPasswordError(); // Empty confirmation of the new password. - SubmitActiveDirectoryPasswordChangeCredentials(kPassword, kNewPassword, ""); - TestAdPasswordChangeError(kAdNewPassword2Input); + ad_login_.SubmitActiveDirectoryPasswordChangeCredentials(kPassword, + kNewPassword, ""); + ad_login_.TestPasswordChangeConfirmNewPasswordError(); // Confirmation of password is different from new password. - SubmitActiveDirectoryPasswordChangeCredentials(kPassword, kNewPassword, - kDifferentNewPassword); - TestAdPasswordChangeError(kAdNewPassword2Input); + ad_login_.SubmitActiveDirectoryPasswordChangeCredentials( + kPassword, kNewPassword, kDifferentNewPassword); + ad_login_.TestPasswordChangeConfirmNewPasswordError(); // Password rejected by AuthPolicyClient. fake_auth_policy_client()->set_auth_error(authpolicy::ERROR_BAD_PASSWORD); - SubmitActiveDirectoryPasswordChangeCredentials(kPassword, kNewPassword, - kNewPassword); - TestAdPasswordChangeError(kAdOldPasswordInput); + ad_login_.SubmitActiveDirectoryPasswordChangeCredentials( + kPassword, kNewPassword, kNewPassword); + ad_login_.TestPasswordChangeOldPasswordError(); } // Test reopening Active Directory password change screen clears errors. -IN_PROC_BROWSER_TEST_F_WITH_PRE(ActiveDirectoryLoginTest, - PasswordChange_ReopenClearErrors) { +IN_PROC_BROWSER_TEST_F(ActiveDirectoryLoginTest, + PasswordChange_ReopenClearErrors) { + OobeBaseTest::WaitForSigninScreen(); ASSERT_TRUE(InstallAttributes::Get()->IsActiveDirectoryManaged()); - TestLoginVisible(); - TestDomainHidden(); + ad_login_.TestLoginVisible(); + ad_login_.TestDomainHidden(); - TriggerPasswordChangeScreen(); + ad_login_.TriggerPasswordChangeScreen(); // Empty new password. - SubmitActiveDirectoryPasswordChangeCredentials("", "", ""); - TestAdPasswordChangeError(kAdOldPasswordInput); + ad_login_.SubmitActiveDirectoryPasswordChangeCredentials("", "", ""); + ad_login_.TestPasswordChangeOldPasswordError(); - ClosePasswordChangeScreen(); - TestLoginVisible(); - TriggerPasswordChangeScreen(); + ad_login_.ClosePasswordChangeScreen(); + ad_login_.TestLoginVisible(); + ad_login_.TriggerPasswordChangeScreen(); + ad_login_.TestPasswordChangeNoErrors(); } // Tests that autocomplete works. Submits username without domain. -IN_PROC_BROWSER_TEST_F_WITH_PRE(ActiveDirectoryLoginAutocompleteTest, - LoginSuccess) { +IN_PROC_BROWSER_TEST_F(ActiveDirectoryLoginAutocompleteTest, LoginSuccess) { + OobeBaseTest::WaitForSigninScreen(); ASSERT_TRUE(InstallAttributes::Get()->IsActiveDirectoryManaged()); - TestNoError(); - TestDomainVisible(); + ad_login_.TestNoError(); + ad_login_.TestDomainVisible(); content::WindowedNotificationObserver session_start_waiter( chrome::NOTIFICATION_SESSION_STARTED, content::NotificationService::AllSources()); - SubmitActiveDirectoryCredentials(kTestActiveDirectoryUser, kPassword); + ad_login_.SubmitActiveDirectoryCredentials(kTestActiveDirectoryUser, + kPassword); session_start_waiter.Wait(); } // Tests that user could override autocomplete domain. -IN_PROC_BROWSER_TEST_F_WITH_PRE(ActiveDirectoryLoginAutocompleteTest, - TestAutocomplete) { +IN_PROC_BROWSER_TEST_F(ActiveDirectoryLoginAutocompleteTest, TestAutocomplete) { + OobeBaseTest::WaitForSigninScreen(); ASSERT_TRUE(InstallAttributes::Get()->IsActiveDirectoryManaged()); - SetupActiveDirectoryJSNotifications(); - TestLoginVisible(); - TestDomainVisible(); + ad_login_.TestLoginVisible(); + ad_login_.TestDomainVisible(); fake_auth_policy_client()->set_auth_error(authpolicy::ERROR_BAD_PASSWORD); - content::DOMMessageQueue message_queue; // Submit with a different domain. - SetUserInput(test_user_); - TestDomainHidden(); - TestUserInput(test_user_); - SubmitActiveDirectoryCredentials(test_user_, "password"); - WaitForMessage(&message_queue, "\"ShowAuthError\""); - TestLoginVisible(); - TestDomainHidden(); - TestUserInput(test_user_); + ad_login_.SetUserInput(test_user_); + ad_login_.TestDomainHidden(); + ad_login_.TestUserInput(test_user_); + ad_login_.SubmitActiveDirectoryCredentials(test_user_, "password"); + ad_login_.WaitForAuthError(); + ad_login_.TestLoginVisible(); + ad_login_.TestDomainHidden(); + ad_login_.TestUserInput(test_user_); // Set userinput with the autocomplete domain. JS will remove the autocomplete // domain. - SetUserInput(kTestActiveDirectoryUser + autocomplete_realm_); - TestDomainVisible(); - TestUserInput(kTestActiveDirectoryUser); - SubmitActiveDirectoryCredentials( + ad_login_.SetUserInput(kTestActiveDirectoryUser + autocomplete_realm_); + ad_login_.TestDomainVisible(); + ad_login_.TestUserInput(kTestActiveDirectoryUser); + ad_login_.SubmitActiveDirectoryCredentials( kTestActiveDirectoryUser + autocomplete_realm_, "password"); - WaitForMessage(&message_queue, "\"ShowAuthError\""); - TestLoginVisible(); - TestDomainVisible(); - TestUserInput(kTestActiveDirectoryUser); + ad_login_.WaitForAuthError(); + ad_login_.TestLoginVisible(); + ad_login_.TestDomainVisible(); + ad_login_.TestUserInput(kTestActiveDirectoryUser); } -#undef IN_PROC_BROWSER_TEST_F_WITH_PRE - } // namespace chromeos
diff --git a/chrome/browser/chromeos/login/existing_user_controller.cc b/chrome/browser/chromeos/login/existing_user_controller.cc index 2b01df5c..1817e8c 100644 --- a/chrome/browser/chromeos/login/existing_user_controller.cc +++ b/chrome/browser/chromeos/login/existing_user_controller.cc
@@ -1068,21 +1068,6 @@ if (auth_status_consumer_) auth_status_consumer_->OnPasswordChangeDetected(); - // If the password change happens after an online auth, do a TokenHandle check - // to find out whether the user password is really changed or not. - // TODO(xiyuan): Remove channel restriction. See http://crbug.com/585530 - if (chrome::GetChannel() <= version_info::Channel::DEV && - auth_mode() == LoginPerformer::AUTH_MODE_EXTENSION) { - token_handle_util_.reset(new TokenHandleUtil); - if (token_handle_util_->HasToken(last_login_attempt_account_id_)) { - token_handle_util_->CheckToken( - last_login_attempt_account_id_, - base::Bind(&ExistingUserController::OnTokenHandleChecked, - weak_factory_.GetWeakPtr())); - return; - } - } - ShowPasswordChangedDialog(); } @@ -1820,24 +1805,6 @@ PerformLogin(user_context, LoginPerformer::AUTH_MODE_EXTENSION); } -void ExistingUserController::OnTokenHandleChecked( - const AccountId&, - TokenHandleUtil::TokenHandleStatus token_handle_status) { - // If TokenHandle is invalid or unknown, continue with regular password - // changed flow. - if (token_handle_status != TokenHandleUtil::VALID) { - VLOG(1) << "Checked TokenHandle status=" << token_handle_status; - ShowPasswordChangedDialog(); - return; - } - - // Otherwise, show the unrecoverable cryptohome error UI and ask user's - // permission to collect a feedback. - RecordPasswordChangeFlow(LOGIN_PASSWORD_CHANGE_FLOW_CRYPTOHOME_FAILURE); - VLOG(1) << "Show unrecoverable cryptohome error dialog."; - GetLoginDisplay()->ShowUnrecoverableCrypthomeErrorDialog(); -} - void ExistingUserController::ClearRecordedNames() { display_email_.clear(); display_name_.clear();
diff --git a/chrome/browser/chromeos/login/existing_user_controller.h b/chrome/browser/chromeos/login/existing_user_controller.h index 8f84c10..499ffa08 100644 --- a/chrome/browser/chromeos/login/existing_user_controller.h +++ b/chrome/browser/chromeos/login/existing_user_controller.h
@@ -22,7 +22,6 @@ #include "chrome/browser/chromeos/app_mode/kiosk_app_manager.h" #include "chrome/browser/chromeos/login/screens/encryption_migration_mode.h" #include "chrome/browser/chromeos/login/session/user_session_manager.h" -#include "chrome/browser/chromeos/login/signin/token_handle_util.h" #include "chrome/browser/chromeos/login/ui/login_display.h" #include "chrome/browser/chromeos/policy/minimum_version_policy_handler.h" #include "chrome/browser/chromeos/policy/pre_signin_policy_fetcher.h" @@ -295,11 +294,6 @@ // Callback invoked when |oauth2_token_initializer_| has finished. void OnOAuth2TokensFetched(bool success, const UserContext& user_context); - // Callback invoked when |token_handle_util_| finishes token check. - void OnTokenHandleChecked( - const AccountId&, - TokenHandleUtil::TokenHandleStatus token_handle_status); - // Called on completition of a pre-signin policy fetch, which is performed to // check if there is a user policy governing migration action. void OnPolicyFetchResult( @@ -416,8 +410,6 @@ std::unique_ptr<OAuth2TokenInitializer> oauth2_token_initializer_; - std::unique_ptr<TokenHandleUtil> token_handle_util_; - std::unique_ptr<policy::PreSigninPolicyFetcher> pre_signin_policy_fetcher_; // Used to wait for cloud policy store load during public session login, if
diff --git a/chrome/browser/chromeos/login/lock/screen_locker_browsertest.cc b/chrome/browser/chromeos/login/lock/screen_locker_browsertest.cc index 2369a05..a080c88 100644 --- a/chrome/browser/chromeos/login/lock/screen_locker_browsertest.cc +++ b/chrome/browser/chromeos/login/lock/screen_locker_browsertest.cc
@@ -70,8 +70,10 @@ ui::ScopedAnimationDurationScaleMode::ZERO_DURATION); } + void TearDown() override { quick_unlock::EnabledForTesting(false); } + void EnrollFingerprint() { - quick_unlock::EnableForTesting(); + quick_unlock::EnabledForTesting(true); FakeBiodClient::Get()->StartEnrollSession( "test-user", std::string(),
diff --git a/chrome/browser/chromeos/login/mixin_based_in_process_browser_test.cc b/chrome/browser/chromeos/login/mixin_based_in_process_browser_test.cc index e181424..e3b35ecb 100644 --- a/chrome/browser/chromeos/login/mixin_based_in_process_browser_test.cc +++ b/chrome/browser/chromeos/login/mixin_based_in_process_browser_test.cc
@@ -27,6 +27,9 @@ void InProcessBrowserTestMixin::SetUpInProcessBrowserTestFixture() {} +void InProcessBrowserTestMixin::CreatedBrowserMainParts( + content::BrowserMainParts* browser_main_parts) {} + void InProcessBrowserTestMixin::SetUpOnMainThread() {} void InProcessBrowserTestMixin::TearDownOnMainThread() {} @@ -61,6 +64,12 @@ mixin->SetUpInProcessBrowserTestFixture(); } +void InProcessBrowserTestMixinHost::CreatedBrowserMainParts( + content::BrowserMainParts* browser_main_parts) { + for (InProcessBrowserTestMixin* mixin : mixins_) + mixin->CreatedBrowserMainParts(browser_main_parts); +} + void InProcessBrowserTestMixinHost::SetUpOnMainThread() { for (InProcessBrowserTestMixin* mixin : mixins_) mixin->SetUpOnMainThread(); @@ -107,6 +116,12 @@ InProcessBrowserTest::SetUpInProcessBrowserTestFixture(); } +void MixinBasedInProcessBrowserTest::CreatedBrowserMainParts( + content::BrowserMainParts* browser_main_parts) { + mixin_host_.CreatedBrowserMainParts(browser_main_parts); + InProcessBrowserTest::CreatedBrowserMainParts(browser_main_parts); +} + void MixinBasedInProcessBrowserTest::SetUpOnMainThread() { mixin_host_.SetUpOnMainThread(); InProcessBrowserTest::SetUpOnMainThread();
diff --git a/chrome/browser/chromeos/login/mixin_based_in_process_browser_test.h b/chrome/browser/chromeos/login/mixin_based_in_process_browser_test.h index f3089439..0ed02b9 100644 --- a/chrome/browser/chromeos/login/mixin_based_in_process_browser_test.h +++ b/chrome/browser/chromeos/login/mixin_based_in_process_browser_test.h
@@ -72,6 +72,7 @@ // SetUpCommandLine // SetUpDefaultCommandLine // SetUpInProcessBrowserTestFixture + // CreatedBrowserMainParts // SetUpOnMainThread // TearDownOnMainThread // TearDownInProcessBrowserTestFixture @@ -83,6 +84,8 @@ virtual void SetUpCommandLine(base::CommandLine* command_line); virtual void SetUpDefaultCommandLine(base::CommandLine* command_line); virtual void SetUpInProcessBrowserTestFixture(); + virtual void CreatedBrowserMainParts( + content::BrowserMainParts* browser_main_parts); virtual void SetUpOnMainThread(); virtual void TearDownOnMainThread(); virtual void TearDownInProcessBrowserTestFixture(); @@ -102,6 +105,7 @@ void SetUpCommandLine(base::CommandLine* command_line); void SetUpDefaultCommandLine(base::CommandLine* command_line); void SetUpInProcessBrowserTestFixture(); + void CreatedBrowserMainParts(content::BrowserMainParts* browser_main_parts); void SetUpOnMainThread(); void TearDownOnMainThread(); void TearDownInProcessBrowserTestFixture(); @@ -129,6 +133,8 @@ void SetUpCommandLine(base::CommandLine* command_line) override; void SetUpDefaultCommandLine(base::CommandLine* command_line) override; void SetUpInProcessBrowserTestFixture() override; + void CreatedBrowserMainParts( + content::BrowserMainParts* browser_main_parts) override; void SetUpOnMainThread() override; void TearDownOnMainThread() override; void TearDownInProcessBrowserTestFixture() override;
diff --git a/chrome/browser/chromeos/login/oobe_interactive_ui_test.cc b/chrome/browser/chromeos/login/oobe_interactive_ui_test.cc index c54dc78..84eec27 100644 --- a/chrome/browser/chromeos/login/oobe_interactive_ui_test.cc +++ b/chrome/browser/chromeos/login/oobe_interactive_ui_test.cc
@@ -80,6 +80,7 @@ } void TearDown() override { + quick_unlock::EnabledForTesting(false); OobeBaseTest::TearDown(); params_.reset(); } @@ -95,7 +96,7 @@ OobeBaseTest::SetUpInProcessBrowserTestFixture(); if (params_->is_quick_unlock_enabled) - quick_unlock::EnableForTesting(); + quick_unlock::EnabledForTesting(true); } void TearDownOnMainThread() override {
diff --git a/chrome/browser/chromeos/login/oobe_screen.cc b/chrome/browser/chromeos/login/oobe_screen.cc index 7d03f63..60e0753 100644 --- a/chrome/browser/chromeos/login/oobe_screen.cc +++ b/chrome/browser/chromeos/login/oobe_screen.cc
@@ -43,7 +43,6 @@ "confirm-password", // SCREEN_CONFIRM_PASSWORD "fatal-error", // SCREEN_FATAL_ERROR "device-disabled", // SCREEN_DEVICE_DISABLED - "unrecoverable-cryptohome-error", // SCREEN_UNRECOVERABLE_CRYPTOHOME_ERROR "userBoard", // SCREEN_USER_SELECTION "ad-password-change", // SCREEN_ACTIVE_DIRECTORY_PASSWORD_CHANGE "encryption-migration", // SCREEN_ENCRYPTION_MIGRATION
diff --git a/chrome/browser/chromeos/login/oobe_screen.h b/chrome/browser/chromeos/login/oobe_screen.h index 3beb845..c2e626f 100644 --- a/chrome/browser/chromeos/login/oobe_screen.h +++ b/chrome/browser/chromeos/login/oobe_screen.h
@@ -40,7 +40,6 @@ SCREEN_CONFIRM_PASSWORD, SCREEN_FATAL_ERROR, SCREEN_DEVICE_DISABLED, - SCREEN_UNRECOVERABLE_CRYPTOHOME_ERROR, SCREEN_USER_SELECTION, SCREEN_ACTIVE_DIRECTORY_PASSWORD_CHANGE, SCREEN_ENCRYPTION_MIGRATION,
diff --git a/chrome/browser/chromeos/login/password_change_browsertest.cc b/chrome/browser/chromeos/login/password_change_browsertest.cc new file mode 100644 index 0000000..070d500 --- /dev/null +++ b/chrome/browser/chromeos/login/password_change_browsertest.cc
@@ -0,0 +1,360 @@ +// Copyright 2019 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 <memory> +#include <string> +#include <utility> + +#include "base/bind.h" +#include "base/callback.h" +#include "base/macros.h" +#include "base/run_loop.h" +#include "base/scoped_observer.h" +#include "chrome/browser/chromeos/login/existing_user_controller.h" +#include "chrome/browser/chromeos/login/login_manager_test.h" +#include "chrome/browser/chromeos/login/session/user_session_manager.h" +#include "chrome/browser/chromeos/login/session/user_session_manager_test_api.h" +#include "chrome/browser/chromeos/login/signin_specifics.h" +#include "chrome/browser/chromeos/login/startup_utils.h" +#include "chrome/browser/chromeos/login/test/js_checker.h" +#include "chrome/browser/chromeos/login/test/oobe_screen_waiter.h" +#include "chrome/browser/chromeos/login/ui/login_display_host.h" +#include "chromeos/login/auth/stub_authenticator.h" +#include "chromeos/login/auth/stub_authenticator_builder.h" +#include "chromeos/login/auth/user_context.h" +#include "components/account_id/account_id.h" +#include "components/session_manager/core/session_manager.h" +#include "components/session_manager/core/session_manager_observer.h" +#include "components/user_manager/user_manager.h" +#include "content/public/browser/web_contents.h" +#include "ui/aura/window.h" +#include "ui/aura/window_observer.h" +#include "ui/gfx/native_widget_types.h" + +namespace chromeos { + +namespace { + +constexpr char kTestUser[] = "test-user1@gmail.com"; +constexpr char kTestUserGaiaId[] = "test-user1@gmail.com"; +constexpr char kPassword[] = "test user password"; + +// Waits for the window that hosts OOBE UI changes visibility to target value. +// When waiting for the OOBE UI window to be hidden, it handles the window +// getting destroyed. Window getting destroyed while waiting for the window +// to become visible will stop the waiter, but will cause a test failure. +class OobeUiWindowVisibilityWaiter : public aura::WindowObserver { + public: + explicit OobeUiWindowVisibilityWaiter(bool target_visibilty) + : target_visibility_(target_visibilty) {} + ~OobeUiWindowVisibilityWaiter() override = default; + + void Wait() { + aura::Window* window = GetWindow(); + if (!window && !target_visibility_) + return; + + DCHECK(window); + if (target_visibility_ == window->IsVisible()) + return; + + base::RunLoop run_loop; + wait_stop_closure_ = run_loop.QuitClosure(); + window_observer_.Add(window); + run_loop.Run(); + } + + // aura::WindowObserver: + void OnWindowVisibilityChanged(aura::Window* window, bool visible) override { + if (visible != target_visibility_) + return; + window_observer_.RemoveAll(); + std::move(wait_stop_closure_).Run(); + } + + void OnWindowDestroyed(aura::Window* window) override { + EXPECT_FALSE(target_visibility_); + window_observer_.RemoveAll(); + std::move(wait_stop_closure_).Run(); + } + + private: + aura::Window* GetWindow() { + LoginDisplayHost* host = LoginDisplayHost::default_host(); + if (!host || !host->GetOobeWebContents()) + return nullptr; + return host->GetOobeWebContents()->GetTopLevelNativeWindow(); + } + + const bool target_visibility_; + base::OnceClosure wait_stop_closure_; + ScopedObserver<aura::Window, OobeUiWindowVisibilityWaiter> window_observer_{ + this}; +}; + +// Used to wait for session manager to become active. +class SessionStartWaiter : public session_manager::SessionManagerObserver { + public: + SessionStartWaiter() = default; + ~SessionStartWaiter() override = default; + + void Wait() { + if (!session_manager::SessionManager::Get()->IsUserSessionBlocked()) + return; + session_observer_.Add(session_manager::SessionManager::Get()); + + base::RunLoop run_loop; + session_active_callback_ = run_loop.QuitClosure(); + run_loop.Run(); + + session_observer_.RemoveAll(); + } + + // session_manager::SessionManagerObserver: + void OnSessionStateChanged() override { + if (!session_manager::SessionManager::Get()->IsUserSessionBlocked() && + session_active_callback_) { + std::move(session_active_callback_).Run(); + } + } + + private: + base::OnceClosure session_active_callback_; + ScopedObserver<session_manager::SessionManager, SessionStartWaiter> + session_observer_{this}; + + DISALLOW_COPY_AND_ASSIGN(SessionStartWaiter); +}; + +} // namespace + +class PasswordChangeTest : public LoginManagerTest { + public: + PasswordChangeTest() + : LoginManagerTest(false, false), + test_account_id_( + AccountId::FromUserEmailGaiaId(kTestUser, kTestUserGaiaId)) { + set_force_webui_login(false); + } + ~PasswordChangeTest() override = default; + + UserContext GetTestUserContext() { + const user_manager::User* user = + user_manager::UserManager::Get()->FindUser(test_account_id_); + UserContext user_context(*user); + user_context.SetKey(Key(kPassword)); + user_context.SetUserIDHash(test_account_id_.GetUserEmail()); + return user_context; + } + + // Sets up UserSessionManager to use stub authenticator that reports a + // password change, and attempts login. + // Password changed OOBE dialog is expected to show up after calling this. + void SetUpStubAuthentcatorAndAttemptLogin(const std::string& old_password) { + UserContext user_context = GetTestUserContext(); + + auto authenticator_builder = + std::make_unique<StubAuthenticatorBuilder>(user_context); + authenticator_builder->SetUpPasswordChange( + old_password, + base::BindRepeating(&PasswordChangeTest::HandleDataRecoveryStatusChange, + base::Unretained(this))); + test::UserSessionManagerTestApi(UserSessionManager::GetInstance()) + .InjectAuthenticatorBuilder(std::move(authenticator_builder)); + + ExistingUserController::current_controller()->SetDisplayEmail( + test_account_id_.GetUserEmail()); + ExistingUserController::current_controller()->Login(user_context, + SigninSpecifics()); + } + + protected: + AccountId test_account_id_; + StubAuthenticator::DataRecoveryStatus data_recovery_status_ = + StubAuthenticator::DataRecoveryStatus::kNone; + + private: + void HandleDataRecoveryStatusChange( + StubAuthenticator::DataRecoveryStatus status) { + EXPECT_EQ(StubAuthenticator::DataRecoveryStatus::kNone, + data_recovery_status_); + data_recovery_status_ = status; + } +}; + +IN_PROC_BROWSER_TEST_F(PasswordChangeTest, PRE_MigrateOldCryptohome) { + RegisterUser(test_account_id_); + StartupUtils::MarkOobeCompleted(); +} + +IN_PROC_BROWSER_TEST_F(PasswordChangeTest, MigrateOldCryptohome) { + SetUpStubAuthentcatorAndAttemptLogin("old user password"); + OobeScreenWaiter(OobeScreen::SCREEN_PASSWORD_CHANGED).Wait(); + OobeUiWindowVisibilityWaiter(true).Wait(); + test::OobeJS().CreateVisibilityWaiter( + true, {"gaia-password-changed", "oldPasswordCard"}); + + // Fill out and submit the old password passed to the stub authenticator. + test::OobeJS().TypeIntoPath("old user password", + {"gaia-password-changed", "oldPasswordInput"}); + test::OobeJS().TapOnPath( + {"gaia-password-changed", "oldPasswordInputForm", "button"}); + + // User session should start, and whole OOBE screen is expected to be hidden, + OobeUiWindowVisibilityWaiter(false).Wait(); + EXPECT_EQ(StubAuthenticator::DataRecoveryStatus::kRecovered, + data_recovery_status_); + + SessionStartWaiter().Wait(); +} + +IN_PROC_BROWSER_TEST_F(PasswordChangeTest, PRE_RetryOnWrongPassword) { + RegisterUser(test_account_id_); + StartupUtils::MarkOobeCompleted(); +} + +IN_PROC_BROWSER_TEST_F(PasswordChangeTest, RetryOnWrongPassword) { + SetUpStubAuthentcatorAndAttemptLogin("old user password"); + OobeScreenWaiter(OobeScreen::SCREEN_PASSWORD_CHANGED).Wait(); + OobeUiWindowVisibilityWaiter(true).Wait(); + test::OobeJS().CreateVisibilityWaiter( + true, {"gaia-password-changed", "oldPasswordCard"}); + + // Fill out and submit the old password passed to the stub authenticator. + test::OobeJS().TypeIntoPath("incorrect old user password", + {"gaia-password-changed", "oldPasswordInput"}); + test::OobeJS().TapOnPath( + {"gaia-password-changed", "oldPasswordInputForm", "button"}); + + // Expect the UI to report failure. + test::OobeJS() + .CreateWaiter(test::GetOobeElementPath( + {"gaia-password-changed", "oldPasswordInput"}) + + ".isInvalid") + ->Wait(); + test::OobeJS().ExpectEnabledPath( + {"gaia-password-changed", "oldPasswordCard"}); + + EXPECT_EQ(StubAuthenticator::DataRecoveryStatus::kRecoveryFailed, + data_recovery_status_); + + data_recovery_status_ = StubAuthenticator::DataRecoveryStatus::kNone; + + // Submit the correct password. + test::OobeJS().TypeIntoPath("old user password", + {"gaia-password-changed", "oldPasswordInput"}); + test::OobeJS().TapOnPath( + {"gaia-password-changed", "oldPasswordInputForm", "button"}); + + // User session should start, and whole OOBE screen is expected to be hidden, + OobeUiWindowVisibilityWaiter(false).Wait(); + EXPECT_EQ(StubAuthenticator::DataRecoveryStatus::kRecovered, + data_recovery_status_); + + SessionStartWaiter().Wait(); +} + +IN_PROC_BROWSER_TEST_F(PasswordChangeTest, PRE_SkipDataRecovery) { + RegisterUser(test_account_id_); + StartupUtils::MarkOobeCompleted(); +} + +IN_PROC_BROWSER_TEST_F(PasswordChangeTest, SkipDataRecovery) { + SetUpStubAuthentcatorAndAttemptLogin("old user password"); + OobeScreenWaiter(OobeScreen::SCREEN_PASSWORD_CHANGED).Wait(); + OobeUiWindowVisibilityWaiter(true).Wait(); + test::OobeJS().CreateVisibilityWaiter( + true, {"gaia-password-changed", "oldPasswordCard"}); + + // Click forgot password link. + test::OobeJS().TapOnPath({"gaia-password-changed", "forgot-password-link"}); + + test::OobeJS().CreateVisibilityWaiter( + false, {"gaia-password-changed", "oldPasswordCard"}); + + test::OobeJS().ExpectVisiblePath({"gaia-password-changed", "try-again-link"}); + test::OobeJS().ExpectVisiblePath( + {"gaia-password-changed", "proceedAnywayBtn"}); + + // Click "Proceed anyway". + test::OobeJS().TapOnPath({"gaia-password-changed", "proceedAnywayBtn"}); + + // User session should start, and whole OOBE screen is expected to be hidden, + OobeUiWindowVisibilityWaiter(false).Wait(); + EXPECT_EQ(StubAuthenticator::DataRecoveryStatus::kResynced, + data_recovery_status_); + + SessionStartWaiter().Wait(); +} + +IN_PROC_BROWSER_TEST_F(PasswordChangeTest, PRE_TryAgainAfterForgetLinkClick) { + RegisterUser(test_account_id_); + StartupUtils::MarkOobeCompleted(); +} + +IN_PROC_BROWSER_TEST_F(PasswordChangeTest, TryAgainAfterForgetLinkClick) { + SetUpStubAuthentcatorAndAttemptLogin("old user password"); + OobeScreenWaiter(OobeScreen::SCREEN_PASSWORD_CHANGED).Wait(); + OobeUiWindowVisibilityWaiter(true).Wait(); + test::OobeJS().CreateVisibilityWaiter( + true, {"gaia-password-changed", "oldPasswordCard"}); + + // Click forgot password link. + test::OobeJS().TapOnPath({"gaia-password-changed", "forgot-password-link"}); + + test::OobeJS().CreateVisibilityWaiter( + false, {"gaia-password-changed", "oldPasswordCard"}); + + test::OobeJS().ExpectVisiblePath({"gaia-password-changed", "try-again-link"}); + test::OobeJS().ExpectVisiblePath( + {"gaia-password-changed", "proceedAnywayBtn"}); + + // Go back to old password input by clicking Try Again. + test::OobeJS().TapOnPath({"gaia-password-changed", "try-again-link"}); + + test::OobeJS().CreateVisibilityWaiter( + true, {"gaia-password-changed", "oldPasswordCard"}); + + // Enter and submit the correct password. + test::OobeJS().TypeIntoPath("old user password", + {"gaia-password-changed", "oldPasswordInput"}); + test::OobeJS().TapOnPath( + {"gaia-password-changed", "oldPasswordInputForm", "button"}); + + // User session should start, and whole OOBE screen is expected to be hidden, + OobeUiWindowVisibilityWaiter(false).Wait(); + EXPECT_EQ(StubAuthenticator::DataRecoveryStatus::kRecovered, + data_recovery_status_); + + SessionStartWaiter().Wait(); +} + +IN_PROC_BROWSER_TEST_F(PasswordChangeTest, PRE_ClosePasswordChangedDialog) { + RegisterUser(test_account_id_); + StartupUtils::MarkOobeCompleted(); +} + +IN_PROC_BROWSER_TEST_F(PasswordChangeTest, ClosePasswordChangedDialog) { + SetUpStubAuthentcatorAndAttemptLogin("old user password"); + OobeScreenWaiter(OobeScreen::SCREEN_PASSWORD_CHANGED).Wait(); + test::OobeJS().CreateVisibilityWaiter( + true, {"gaia-password-changed", "oldPasswordCard"}); + + test::OobeJS().TypeIntoPath("old user password", + {"gaia-password-changed", "oldPasswordInput"}); + // Click the close button. + test::OobeJS().TapOnPath( + {"gaia-password-changed", "navigation", "closeButton"}); + + OobeUiWindowVisibilityWaiter(false).Wait(); + EXPECT_EQ(StubAuthenticator::DataRecoveryStatus::kNone, + data_recovery_status_); + + ExistingUserController::current_controller()->Login(GetTestUserContext(), + SigninSpecifics()); + OobeUiWindowVisibilityWaiter(true).Wait(); + OobeScreenWaiter(OobeScreen::SCREEN_PASSWORD_CHANGED).Wait(); +} + +} // namespace chromeos
diff --git a/chrome/browser/chromeos/login/quick_unlock/fingerprint_storage_unittest.cc b/chrome/browser/chromeos/login/quick_unlock/fingerprint_storage_unittest.cc index 9023448f..2bce1e34 100644 --- a/chrome/browser/chromeos/login/quick_unlock/fingerprint_storage_unittest.cc +++ b/chrome/browser/chromeos/login/quick_unlock/fingerprint_storage_unittest.cc
@@ -23,7 +23,9 @@ ~FingerprintStorageUnitTest() override {} // testing::Test: - void SetUp() override { quick_unlock::EnableForTesting(); } + void SetUp() override { quick_unlock::EnabledForTesting(true); } + + void TearDown() override { quick_unlock::EnabledForTesting(false); } void SetRecords(int records_number) { profile_->GetPrefs()->SetInteger(prefs::kQuickUnlockFingerprintRecord,
diff --git a/chrome/browser/chromeos/login/quick_unlock/pin_storage_prefs_unittest.cc b/chrome/browser/chromeos/login/quick_unlock/pin_storage_prefs_unittest.cc index 33b6273..c4951789 100644 --- a/chrome/browser/chromeos/login/quick_unlock/pin_storage_prefs_unittest.cc +++ b/chrome/browser/chromeos/login/quick_unlock/pin_storage_prefs_unittest.cc
@@ -22,7 +22,9 @@ ~PinStoragePrefsUnitTest() override = default; // testing::Test: - void SetUp() override { quick_unlock::EnableForTesting(); } + void SetUp() override { quick_unlock::EnabledForTesting(true); } + + void TearDown() override { quick_unlock::EnabledForTesting(false); } quick_unlock::PinStoragePrefs* PinStoragePrefs() const { return quick_unlock::QuickUnlockFactory::GetForProfile(profile_.get())
diff --git a/chrome/browser/chromeos/login/quick_unlock/quick_unlock_storage_unittest.cc b/chrome/browser/chromeos/login/quick_unlock/quick_unlock_storage_unittest.cc index 31fafb0..fa9a8d8 100644 --- a/chrome/browser/chromeos/login/quick_unlock/quick_unlock_storage_unittest.cc +++ b/chrome/browser/chromeos/login/quick_unlock/quick_unlock_storage_unittest.cc
@@ -43,7 +43,8 @@ ~QuickUnlockStorageUnitTest() override {} // testing::Test: - void SetUp() override { quick_unlock::EnableForTesting(); } + void SetUp() override { quick_unlock::EnabledForTesting(true); } + void TearDown() override { quick_unlock::EnabledForTesting(false); } void ExpireAuthToken() { quick_unlock::QuickUnlockFactory::GetForProfile(profile_.get())
diff --git a/chrome/browser/chromeos/login/quick_unlock/quick_unlock_utils.cc b/chrome/browser/chromeos/login/quick_unlock/quick_unlock_utils.cc index 5c3bea51..7794988 100644 --- a/chrome/browser/chromeos/login/quick_unlock/quick_unlock_utils.cc +++ b/chrome/browser/chromeos/login/quick_unlock/quick_unlock_utils.cc
@@ -151,8 +151,8 @@ return base::FeatureList::IsEnabled(features::kQuickUnlockFingerprint); } -void EnableForTesting() { - enable_for_testing_ = true; +void EnabledForTesting(bool state) { + enable_for_testing_ = state; } bool IsEnabledForTesting() {
diff --git a/chrome/browser/chromeos/login/quick_unlock/quick_unlock_utils.h b/chrome/browser/chromeos/login/quick_unlock/quick_unlock_utils.h index d4c15ba..ffa95296 100644 --- a/chrome/browser/chromeos/login/quick_unlock/quick_unlock_utils.h +++ b/chrome/browser/chromeos/login/quick_unlock/quick_unlock_utils.h
@@ -48,8 +48,8 @@ // by cros_config. bool IsFingerprintReaderOnKeyboard(); -// Forcibly enable all quick-unlock modes for testing. -void EnableForTesting(); +// Enable or Disable quick-unlock modes for testing +void EnabledForTesting(bool state); // Returns true if EnableForTesting() was previously called. bool IsEnabledForTesting();
diff --git a/chrome/browser/chromeos/login/reset_browsertest.cc b/chrome/browser/chromeos/login/reset_browsertest.cc index c7a387e4..aafeb36 100644 --- a/chrome/browser/chromeos/login/reset_browsertest.cc +++ b/chrome/browser/chromeos/login/reset_browsertest.cc
@@ -6,12 +6,13 @@ #include "base/command_line.h" #include "chrome/browser/browser_process.h" -#include "chrome/browser/chromeos/login/login_manager_test.h" #include "chrome/browser/chromeos/login/login_wizard.h" +#include "chrome/browser/chromeos/login/mixin_based_in_process_browser_test.h" #include "chrome/browser/chromeos/login/oobe_screen.h" #include "chrome/browser/chromeos/login/screens/reset_screen.h" #include "chrome/browser/chromeos/login/startup_utils.h" #include "chrome/browser/chromeos/login/test/js_checker.h" +#include "chrome/browser/chromeos/login/test/login_manager_mixin.h" #include "chrome/browser/chromeos/login/test/oobe_screen_exit_waiter.h" #include "chrome/browser/chromeos/login/test/oobe_screen_waiter.h" #include "chrome/browser/chromeos/login/ui/login_display_host.h" @@ -37,11 +38,9 @@ } // namespace -class ResetTest : public LoginManagerTest { +class ResetTest : public MixinBasedInProcessBrowserTest { public: - ResetTest() : LoginManagerTest(false, false /* should_initialize_webui */) { - set_force_webui_login(false); - } + ResetTest() = default; ~ResetTest() override = default; // LoginManagerTest overrides: @@ -52,12 +51,7 @@ dbus_setter->SetUpdateEngineClient( std::unique_ptr<UpdateEngineClient>(update_engine_client_)); - LoginManagerTest::SetUpInProcessBrowserTestFixture(); - } - - void RegisterSomeUser() { - RegisterUser(AccountId::FromUserEmailGaiaId(kTestUser1, kTestUser1GaiaId)); - StartupUtils::MarkOobeCompleted(); + MixinBasedInProcessBrowserTest::SetUpInProcessBrowserTestFixture(); } void InvokeResetScreen() { @@ -100,6 +94,9 @@ FakeUpdateEngineClient* update_engine_client_ = nullptr; private: + LoginManagerMixin login_manager_mixin_{ + &mixin_host_, + {AccountId::FromUserEmailGaiaId(kTestUser1, kTestUser1GaiaId)}}; DISALLOW_COPY_AND_ASSIGN(ResetTest); }; @@ -179,10 +176,6 @@ tpm_firmware_update_checker_callback_; }; -IN_PROC_BROWSER_TEST_F(ResetTest, PRE_ShowAndCancel) { - RegisterSomeUser(); -} - IN_PROC_BROWSER_TEST_F(ResetTest, ShowAndCancel) { InvokeResetScreen(); test::OobeJS().ExpectVisible("reset"); @@ -191,10 +184,6 @@ test::OobeJS().CreateVisibilityWaiter(false, {"reset"}); } -IN_PROC_BROWSER_TEST_F(ResetTest, PRE_RestartBeforePowerwash) { - RegisterSomeUser(); -} - IN_PROC_BROWSER_TEST_F(ResetTest, RestartBeforePowerwash) { PrefService* prefs = g_browser_process->local_state(); @@ -211,7 +200,6 @@ IN_PROC_BROWSER_TEST_F(ResetFirstAfterBootTest, PRE_ViewsLogic) { PrefService* prefs = g_browser_process->local_state(); prefs->SetBoolean(prefs::kFactoryResetRequested, true); - RegisterSomeUser(); update_engine_client_->set_can_rollback_check_result(false); } @@ -256,7 +244,6 @@ IN_PROC_BROWSER_TEST_F(ResetFirstAfterBootTest, PRE_ShowAfterBootIfRequested) { PrefService* prefs = g_browser_process->local_state(); prefs->SetBoolean(prefs::kFactoryResetRequested, true); - RegisterSomeUser(); } IN_PROC_BROWSER_TEST_F(ResetFirstAfterBootTest, ShowAfterBootIfRequested) { @@ -269,7 +256,6 @@ IN_PROC_BROWSER_TEST_F(ResetFirstAfterBootTest, PRE_RollbackUnavailable) { PrefService* prefs = g_browser_process->local_state(); prefs->SetBoolean(prefs::kFactoryResetRequested, true); - RegisterSomeUser(); } IN_PROC_BROWSER_TEST_F(ResetFirstAfterBootTest, RollbackUnavailable) { @@ -302,7 +288,6 @@ PRE_RollbackAvailable) { PrefService* prefs = g_browser_process->local_state(); prefs->SetBoolean(prefs::kFactoryResetRequested, true); - RegisterSomeUser(); } IN_PROC_BROWSER_TEST_F(ResetFirstAfterBootTestWithRollback, RollbackAvailable) { @@ -351,7 +336,6 @@ PRE_ErrorOnRollbackRequested) { PrefService* prefs = g_browser_process->local_state(); prefs->SetBoolean(prefs::kFactoryResetRequested, true); - RegisterSomeUser(); } IN_PROC_BROWSER_TEST_F(ResetFirstAfterBootTestWithRollback, @@ -378,7 +362,6 @@ PRE_RevertAfterCancel) { PrefService* prefs = g_browser_process->local_state(); prefs->SetBoolean(prefs::kFactoryResetRequested, true); - RegisterSomeUser(); } IN_PROC_BROWSER_TEST_F(ResetFirstAfterBootTestWithRollback, RevertAfterCancel) { @@ -408,11 +391,6 @@ } IN_PROC_BROWSER_TEST_F(ResetTestWithTpmFirmwareUpdate, - PRE_PRE_ResetFromSigninWithFirmwareUpdate) { - RegisterSomeUser(); -} - -IN_PROC_BROWSER_TEST_F(ResetTestWithTpmFirmwareUpdate, PRE_ResetFromSigninWithFirmwareUpdate) { InvokeResetScreen(); test::OobeJS().ExpectHiddenPath({"oobe-reset-md", "tpmFirmwareUpdate"}); @@ -456,7 +434,6 @@ IN_PROC_BROWSER_TEST_F(ResetTestWithTpmFirmwareUpdate, PRE_TpmFirmwareUpdateAvailableButNotSelected) { - RegisterSomeUser(); PrefService* prefs = g_browser_process->local_state(); prefs->SetBoolean(prefs::kFactoryResetRequested, true); } @@ -483,7 +460,6 @@ IN_PROC_BROWSER_TEST_F(ResetTestWithTpmFirmwareUpdate, PRE_ResetWithTpmCleanUp) { - RegisterSomeUser(); PrefService* prefs = g_browser_process->local_state(); prefs->SetBoolean(prefs::kFactoryResetRequested, true); prefs->SetInteger(prefs::kFactoryResetTPMFirmwareUpdateMode, @@ -517,7 +493,6 @@ IN_PROC_BROWSER_TEST_F(ResetTestWithTpmFirmwareUpdate, PRE_ResetWithTpmUpdatePreservingDeviceState) { - RegisterSomeUser(); PrefService* prefs = g_browser_process->local_state(); prefs->SetBoolean(prefs::kFactoryResetRequested, true); prefs->SetInteger( @@ -555,7 +530,6 @@ IN_PROC_BROWSER_TEST_F(ResetTestWithTpmFirmwareUpdate, PRE_TpmFirmwareUpdateRequestedBeforeShowNotEditable) { - RegisterSomeUser(); PrefService* prefs = g_browser_process->local_state(); prefs->SetBoolean(prefs::kFactoryResetRequested, true); prefs->SetInteger(prefs::kFactoryResetTPMFirmwareUpdateMode, @@ -599,7 +573,6 @@ IN_PROC_BROWSER_TEST_F(ResetTestWithTpmFirmwareUpdate, PRE_AvailableTpmUpdateModesChangeDuringRequest) { - RegisterSomeUser(); PrefService* prefs = g_browser_process->local_state(); prefs->SetBoolean(prefs::kFactoryResetRequested, true); prefs->SetInteger(prefs::kFactoryResetTPMFirmwareUpdateMode,
diff --git a/chrome/browser/chromeos/login/screens/fingerprint_setup_browsertest.cc b/chrome/browser/chromeos/login/screens/fingerprint_setup_browsertest.cc new file mode 100644 index 0000000..c40c48f --- /dev/null +++ b/chrome/browser/chromeos/login/screens/fingerprint_setup_browsertest.cc
@@ -0,0 +1,208 @@ +// Copyright 2019 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 "chrome/browser/chromeos/login/login_wizard.h" +#include "chrome/browser/chromeos/login/quick_unlock/quick_unlock_utils.h" +#include "chrome/browser/chromeos/login/screens/fingerprint_setup_screen.h" +#include "chrome/browser/chromeos/login/test/js_checker.h" +#include "chrome/browser/chromeos/login/test/oobe_base_test.h" +#include "chrome/browser/chromeos/login/test/oobe_screen_waiter.h" +#include "chrome/browser/chromeos/login/ui/login_display_host.h" +#include "chrome/browser/ui/webui/chromeos/login/oobe_ui.h" +#include "chromeos/dbus/biod/fake_biod_client.h" + +namespace chromeos { + +namespace { + +constexpr char kTestFingerprintDataString[] = "testFinger"; + +int kMaxAllowedFingerprints = 3; + +chromeos::OobeUI* GetOobeUI() { + auto* host = chromeos::LoginDisplayHost::default_host(); + return host ? host->GetOobeUI() : nullptr; +} + +} // namespace + +class FingerprintSetupTest : public InProcessBrowserTest { + public: + FingerprintSetupTest() = default; + ~FingerprintSetupTest() override = default; + + void SetUpOnMainThread() override { + ShowLoginWizard(OobeScreen::SCREEN_TEST_NO_WINDOW); + + fingerprint_setup_screen_ = std::make_unique<FingerprintSetupScreen>( + GetOobeUI()->GetFingerprintSetupScreenView(), + base::BindRepeating(&FingerprintSetupTest::OnFingerprintSetupScreenExit, + base::Unretained(this))); + + InProcessBrowserTest::SetUpOnMainThread(); + } + + void TearDownOnMainThread() override { + fingerprint_setup_screen_.reset(); + + InProcessBrowserTest::TearDownOnMainThread(); + } + + void WaitForScreenExit() { + if (screen_exit_) + return; + base::RunLoop run_loop; + screen_exit_callback_ = run_loop.QuitClosure(); + run_loop.Run(); + } + + void OnFingerprintSetupScreenExit() { + screen_exit_ = true; + if (screen_exit_callback_) { + std::move(screen_exit_callback_).Run(); + } + } + + void EnrollFingerprint(int percent_complete) { + base::RunLoop().RunUntilIdle(); + FakeBiodClient::Get()->SendEnrollScanDone( + kTestFingerprintDataString, biod::SCAN_RESULT_SUCCESS, + percent_complete == 100 /* is_complete */, percent_complete); + base::RunLoop().RunUntilIdle(); + } + + void CheckCompletedEnroll() { + test::OobeJS().ExpectVisiblePath({"fingerprint-setup-impl", "arc"}); + test::OobeJS() + .CreateVisibilityWaiter( + true, {"fingerprint-setup-impl", "fingerprintEnrollDone"}) + ->Wait(); + test::OobeJS().ExpectHiddenPath( + {"fingerprint-setup-impl", "skipFingerprintEnroll"}); + test::OobeJS().ExpectVisiblePath( + {"fingerprint-setup-impl", "arc", "checkmarkAnimation"}); + test::OobeJS().ExpectVisiblePath( + {"fingerprint-setup-impl", "fingerprintAddAnother"}); + } + + std::unique_ptr<FingerprintSetupScreen> fingerprint_setup_screen_; + + private: + bool screen_exit_ = false; + + base::OnceClosure screen_exit_callback_; +}; + +IN_PROC_BROWSER_TEST_F(FingerprintSetupTest, FingerprintEnrollHalf) { + quick_unlock::EnabledForTesting(true); + fingerprint_setup_screen_->Show(); + OobeScreenWaiter(OobeScreen::SCREEN_FINGERPRINT_SETUP).Wait(); + + EnrollFingerprint(50); + test::OobeJS().ExpectVisiblePath({"fingerprint-setup-impl", "arc"}); + test::OobeJS().ExpectVisiblePath( + {"fingerprint-setup-impl", "skipFingerprintEnroll"}); + test::OobeJS().ExpectHiddenPath( + {"fingerprint-setup-impl", "fingerprintAddAnother"}); + test::OobeJS().ExpectHiddenPath( + {"fingerprint-setup-impl", "fingerprintEnrollDone"}); + + test::OobeJS().TapOnPath({"fingerprint-setup-impl", "skipFingerprintEnroll"}); + + WaitForScreenExit(); +} + +IN_PROC_BROWSER_TEST_F(FingerprintSetupTest, FingerprintEnrollFull) { + quick_unlock::EnabledForTesting(true); + fingerprint_setup_screen_->Show(); + + OobeScreenWaiter(OobeScreen::SCREEN_FINGERPRINT_SETUP).Wait(); + EnrollFingerprint(100); + CheckCompletedEnroll(); + + test::OobeJS().TapOnPath({"fingerprint-setup-impl", "fingerprintEnrollDone"}); + + WaitForScreenExit(); +} + +IN_PROC_BROWSER_TEST_F(FingerprintSetupTest, FingerprintEnrollLimit) { + quick_unlock::EnabledForTesting(true); + fingerprint_setup_screen_->Show(); + OobeScreenWaiter(OobeScreen::SCREEN_FINGERPRINT_SETUP).Wait(); + + for (int i = 0; i < kMaxAllowedFingerprints - 1; i++) { + EnrollFingerprint(100); + CheckCompletedEnroll(); + test::OobeJS().TapOnPath( + {"fingerprint-setup-impl", "fingerprintAddAnother", "textButton"}); + } + + EnrollFingerprint(100); + test::OobeJS().ExpectHiddenPath( + {"fingerprint-setup-impl", "fingerprintAddAnother"}); + test::OobeJS().TapOnPath({"fingerprint-setup-impl", "fingerprintEnrollDone"}); + + WaitForScreenExit(); +} + +IN_PROC_BROWSER_TEST_F(FingerprintSetupTest, FingerprintDisabled) { + quick_unlock::EnabledForTesting(false); + fingerprint_setup_screen_->Show(); + + WaitForScreenExit(); +} + +IN_PROC_BROWSER_TEST_F(FingerprintSetupTest, FingerprintSetupScreenElements) { + quick_unlock::EnabledForTesting(true); + fingerprint_setup_screen_->Show(); + OobeScreenWaiter(OobeScreen::SCREEN_FINGERPRINT_SETUP).Wait(); + + test::OobeJS().CreateVisibilityWaiter(true, {"fingerprint-setup"})->Wait(); + test::OobeJS().ExpectVisible("fingerprint-setup-impl"); + + test::OobeJS().ExpectVisiblePath( + {"fingerprint-setup-impl", "setupFingerprint"}); +} + +IN_PROC_BROWSER_TEST_F(FingerprintSetupTest, FingerprintSetupCancel) { + quick_unlock::EnabledForTesting(true); + fingerprint_setup_screen_->Show(); + OobeScreenWaiter(OobeScreen::SCREEN_FINGERPRINT_SETUP).Wait(); + test::OobeJS().TapOnPath({"fingerprint-setup-impl", "skipFingerprintSetup"}); + WaitForScreenExit(); +} + +IN_PROC_BROWSER_TEST_F(FingerprintSetupTest, FingerprintSetupNext) { + quick_unlock::EnabledForTesting(true); + fingerprint_setup_screen_->Show(); + OobeScreenWaiter(OobeScreen::SCREEN_FINGERPRINT_SETUP).Wait(); + + test::OobeJS().CreateVisibilityWaiter(true, {"fingerprint-setup"})->Wait(); + test::OobeJS().TapOnPath( + {"fingerprint-setup-impl", "showSensorLocationButton"}); + test::OobeJS() + .CreateVisibilityWaiter(true, {"fingerprint-setup-impl", "placeFinger"}) + ->Wait(); + test::OobeJS().ExpectHiddenPath( + {"fingerprint-setup-impl", "setupFingerprint"}); +} + +IN_PROC_BROWSER_TEST_F(FingerprintSetupTest, FingerprintSetupLater) { + quick_unlock::EnabledForTesting(true); + fingerprint_setup_screen_->Show(); + OobeScreenWaiter(OobeScreen::SCREEN_FINGERPRINT_SETUP).Wait(); + + test::OobeJS().CreateVisibilityWaiter(true, {"fingerprint-setup"})->Wait(); + test::OobeJS().TapOnPath( + {"fingerprint-setup-impl", "showSensorLocationButton"}); + test::OobeJS() + .CreateVisibilityWaiter( + true, {"fingerprint-setup-impl", "setupFingerprintLater"}) + ->Wait(); + test::OobeJS().TapOnPath({"fingerprint-setup-impl", "setupFingerprintLater"}); + + WaitForScreenExit(); +} + +} // namespace chromeos
diff --git a/chrome/browser/chromeos/login/screens/fingerprint_setup_screen.cc b/chrome/browser/chromeos/login/screens/fingerprint_setup_screen.cc index 2a4587c..9c40de71 100644 --- a/chrome/browser/chromeos/login/screens/fingerprint_setup_screen.cc +++ b/chrome/browser/chromeos/login/screens/fingerprint_setup_screen.cc
@@ -3,8 +3,9 @@ // found in the LICENSE file. #include "chrome/browser/chromeos/login/screens/fingerprint_setup_screen.h" - +#include "chrome/browser/chromeos/login/quick_unlock/quick_unlock_utils.h" #include "chrome/browser/chromeos/login/users/chrome_user_manager_util.h" +#include "chrome/browser/profiles/profile_manager.h" namespace chromeos { namespace { @@ -28,7 +29,9 @@ } void FingerprintSetupScreen::Show() { - if (chrome_user_manager_util::IsPublicSessionOrEphemeralLogin()) { + if (!chromeos::quick_unlock::IsFingerprintEnabled( + ProfileManager::GetActiveUserProfile()) || + chrome_user_manager_util::IsPublicSessionOrEphemeralLogin()) { exit_callback_.Run(); return; }
diff --git a/chrome/browser/chromeos/login/session/user_session_manager.cc b/chrome/browser/chromeos/login/session/user_session_manager.cc index ec87b0d..c9bb2ea 100644 --- a/chrome/browser/chromeos/login/session/user_session_manager.cc +++ b/chrome/browser/chromeos/login/session/user_session_manager.cc
@@ -117,7 +117,7 @@ #include "chromeos/dbus/cryptohome/tpm_util.h" #include "chromeos/dbus/dbus_thread_manager.h" #include "chromeos/dbus/session_manager/session_manager_client.h" -#include "chromeos/login/auth/stub_authenticator.h" +#include "chromeos/login/auth/stub_authenticator_builder.h" #include "chromeos/network/network_cert_loader.h" #include "chromeos/network/portal_detector/network_portal_detector.h" #include "chromeos/network/portal_detector/network_portal_detector_strategy.h" @@ -588,9 +588,8 @@ } if (authenticator_.get() == NULL) { - if (injected_user_context_) { - authenticator_ = - new StubAuthenticator(consumer, *injected_user_context_.get()); + if (injected_authenticator_builder_) { + authenticator_ = injected_authenticator_builder_->Create(consumer); } else { authenticator_ = new ChromeCryptohomeAuthenticator(consumer); } @@ -2227,9 +2226,9 @@ default_ime_states_.erase(profile); } -void UserSessionManager::InjectStubUserContext( - const UserContext& user_context) { - injected_user_context_.reset(new UserContext(user_context)); +void UserSessionManager::InjectAuthenticatorBuilder( + std::unique_ptr<StubAuthenticatorBuilder> builder) { + injected_authenticator_builder_ = std::move(builder); authenticator_ = NULL; }
diff --git a/chrome/browser/chromeos/login/session/user_session_manager.h b/chrome/browser/chromeos/login/session/user_session_manager.h index ba445358..60d83ce 100644 --- a/chrome/browser/chromeos/login/session/user_session_manager.h +++ b/chrome/browser/chromeos/login/session/user_session_manager.h
@@ -61,6 +61,7 @@ class EasyUnlockKeyManager; class InputEventsBlocker; class LoginDisplayHost; +class StubAuthenticatorBuilder; class UserSessionManagerDelegate { public: @@ -479,10 +480,8 @@ void CreateTokenUtilIfMissing(); // Test API methods. - - // Injects |user_context| that will be used to create StubAuthenticator - // instance when CreateAuthenticator() is called. - void InjectStubUserContext(const UserContext& user_context); + void InjectAuthenticatorBuilder( + std::unique_ptr<StubAuthenticatorBuilder> builer); // Controls whether browser instance should be launched after sign in // (used in tests). @@ -526,8 +525,7 @@ scoped_refptr<Authenticator> authenticator_; StartSessionType start_session_type_; - // Injected user context for stub authenticator. - std::unique_ptr<UserContext> injected_user_context_; + std::unique_ptr<StubAuthenticatorBuilder> injected_authenticator_builder_; // True if the authentication context's cookie jar contains authentication // cookies from the authentication extension login flow.
diff --git a/chrome/browser/chromeos/login/session/user_session_manager_test_api.cc b/chrome/browser/chromeos/login/session/user_session_manager_test_api.cc index e25708aa..fb7e977c 100644 --- a/chrome/browser/chromeos/login/session/user_session_manager_test_api.cc +++ b/chrome/browser/chromeos/login/session/user_session_manager_test_api.cc
@@ -4,6 +4,8 @@ #include "chrome/browser/chromeos/login/session/user_session_manager_test_api.h" +#include "chromeos/login/auth/stub_authenticator_builder.h" + namespace chromeos { namespace test { @@ -13,7 +15,13 @@ void UserSessionManagerTestApi::InjectStubUserContext( const UserContext& user_context) { - session_manager_->InjectStubUserContext(user_context); + session_manager_->InjectAuthenticatorBuilder( + std::make_unique<StubAuthenticatorBuilder>(user_context)); +} + +void UserSessionManagerTestApi::InjectAuthenticatorBuilder( + std::unique_ptr<StubAuthenticatorBuilder> builder) { + session_manager_->InjectAuthenticatorBuilder(std::move(builder)); } void UserSessionManagerTestApi::SetShouldLaunchBrowserInTests(
diff --git a/chrome/browser/chromeos/login/session/user_session_manager_test_api.h b/chrome/browser/chromeos/login/session/user_session_manager_test_api.h index bbe43d7..9faeea4b 100644 --- a/chrome/browser/chromeos/login/session/user_session_manager_test_api.h +++ b/chrome/browser/chromeos/login/session/user_session_manager_test_api.h
@@ -5,6 +5,8 @@ #ifndef CHROME_BROWSER_CHROMEOS_LOGIN_SESSION_USER_SESSION_MANAGER_TEST_API_H_ #define CHROME_BROWSER_CHROMEOS_LOGIN_SESSION_USER_SESSION_MANAGER_TEST_API_H_ +#include <memory> + #include "base/macros.h" #include "chrome/browser/chromeos/login/session/user_session_manager.h" @@ -18,8 +20,12 @@ // Injects |user_context| that will be used to create StubAuthenticator // instance when UserSessionManager::CreateAuthenticator() is called. + // DEPRECATED: Use InjectStubAuthenticatorBuilder instead. void InjectStubUserContext(const UserContext& user_context); + void InjectAuthenticatorBuilder( + std::unique_ptr<StubAuthenticatorBuilder> builder); + // Controls whether browser instance should be launched after sign in // (used in tests). void SetShouldLaunchBrowserInTests(bool should_launch_browser);
diff --git a/chrome/browser/chromeos/login/test/active_directory_login_mixin.cc b/chrome/browser/chromeos/login/test/active_directory_login_mixin.cc new file mode 100644 index 0000000..a10b488 --- /dev/null +++ b/chrome/browser/chromeos/login/test/active_directory_login_mixin.cc
@@ -0,0 +1,252 @@ +// Copyright 2019 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 "chrome/browser/chromeos/login/test/active_directory_login_mixin.h" + +#include "chrome/browser/chromeos/login/login_shelf_test_helper.h" +#include "chrome/browser/chromeos/login/test/js_checker.h" +#include "chrome/browser/chromeos/login/test/oobe_screen_waiter.h" +#include "chrome/browser/chromeos/login/ui/login_display_host.h" +#include "chrome/browser/ui/webui/chromeos/login/oobe_ui.h" +#include "chrome/browser/ui/webui/chromeos/login/signin_screen_handler.h" +#include "chromeos/dbus/auth_policy/fake_auth_policy_client.h" +#include "chromeos/tpm/stub_install_attributes.h" +#include "content/public/test/browser_test_utils.h" + +namespace chromeos { + +namespace { + +constexpr char kGaiaSigninId[] = "signin-frame-dialog"; +constexpr char kAdOfflineAuthId[] = "offline-ad-auth"; + +constexpr char kAdMachineInput[] = "machineNameInput"; +constexpr char kAdMoreOptionsButton[] = "moreOptionsBtn"; +constexpr char kAdUserInput[] = "userInput"; +constexpr char kAdPasswordInput[] = "passwordInput"; +constexpr char kAdCredsButton[] = "nextButton"; +constexpr char kAdAutocompleteRealm[] = "$.userInput.querySelector('span')"; + +constexpr char kPasswordChangeId[] = "active-directory-password-change"; +constexpr char kAdAnimatedPages[] = "animatedPages"; +constexpr char kAdOldPasswordInput[] = "oldPassword"; +constexpr char kAdNewPassword1Input[] = "newPassword1"; +constexpr char kAdNewPassword2Input[] = "newPassword2"; +constexpr char kPasswordChangeFormId[] = "inputForm"; +constexpr char kFormButtonId[] = "button"; + +constexpr char kNavigationId[] = "navigation"; +constexpr char kCloseButtonId[] = "closeButton"; + +} // namespace + +ActiveDirectoryLoginMixin::ActiveDirectoryLoginMixin( + InProcessBrowserTestMixinHost* host, + const std::string& realm) + : InProcessBrowserTestMixin(host), + install_attributes_( + chromeos::StubInstallAttributes::CreateActiveDirectoryManaged( + realm, + "device_id")) {} + +void ActiveDirectoryLoginMixin::SetUpInProcessBrowserTestFixture() { + AuthPolicyClient::InitializeFake(); + FakeAuthPolicyClient::Get()->DisableOperationDelayForTesting(); +} + +void ActiveDirectoryLoginMixin::SetUpOnMainThread() { + // Set the threshold to a max value to disable the offline message screen on + // slow configurations like MSAN, where it otherwise triggers on every run. + LoginDisplayHost::default_host() + ->GetOobeUI() + ->signin_screen_handler() + ->SetOfflineTimeoutForTesting(base::TimeDelta::Max()); + + message_queue_ = std::make_unique<content::DOMMessageQueue>(); + SetupActiveDirectoryJSNotifications(); +} + +void ActiveDirectoryLoginMixin::TriggerPasswordChangeScreen() { + OobeScreenWaiter screen_waiter( + OobeScreen::SCREEN_ACTIVE_DIRECTORY_PASSWORD_CHANGE); + + FakeAuthPolicyClient::Get()->set_auth_error( + authpolicy::ERROR_PASSWORD_EXPIRED); + SubmitActiveDirectoryCredentials("any_user@any_realm", "any_password"); + screen_waiter.Wait(); + TestPasswordChangeError(std::string()); +} + +void ActiveDirectoryLoginMixin::ClosePasswordChangeScreen() { + test::OobeJS().TapOnPath({kPasswordChangeId, kNavigationId, kCloseButtonId}); +} + +void ActiveDirectoryLoginMixin::ExpectValid(const std::string& parent_id, + const std::string& child_id, + bool valid) { + std::string js = test::GetOobeElementPath({parent_id, child_id}) + ".invalid"; + if (valid) + test::OobeJS().ExpectFalse(js); + else + test::OobeJS().ExpectTrue(js); +} + +// Checks if Active Directory login is visible. +void ActiveDirectoryLoginMixin::TestLoginVisible() { + OobeScreenWaiter screen_waiter(OobeScreen::SCREEN_GAIA_SIGNIN); + screen_waiter.Wait(); + // Checks if Gaia signin is hidden. + test::OobeJS().ExpectHidden(kGaiaSigninId); + + // Checks if Active Directory signin is visible. + test::OobeJS().ExpectVisible(kAdOfflineAuthId); + test::OobeJS().ExpectHiddenPath({kAdOfflineAuthId, kAdMachineInput}); + test::OobeJS().ExpectHiddenPath({kAdOfflineAuthId, kAdMoreOptionsButton}); + test::OobeJS().ExpectVisiblePath({kAdOfflineAuthId, kAdUserInput}); + test::OobeJS().ExpectVisiblePath({kAdOfflineAuthId, kAdPasswordInput}); + + test::OobeJS().ExpectEQ( + JSElement(kAdOfflineAuthId, kAdAutocompleteRealm) + ".innerText.trim()", + autocomplete_realm_); + + EXPECT_TRUE(LoginShelfTestHelper().IsLoginShelfShown()); +} + +// Checks if Active Directory password change screen is shown. +void ActiveDirectoryLoginMixin::TestPasswordChangeVisible() { + // Checks if Gaia signin is hidden. + test::OobeJS().ExpectHidden(kGaiaSigninId); + // Checks if Active Directory signin is visible. + test::OobeJS().ExpectVisible(kPasswordChangeId); + test::OobeJS().ExpectTrue( + test::GetOobeElementPath({kPasswordChangeId, kAdAnimatedPages}) + + ".selected == 0"); + test::OobeJS().ExpectVisiblePath( + {kPasswordChangeId, kNavigationId, kCloseButtonId}); +} + +// Checks if user input is marked as invalid. +void ActiveDirectoryLoginMixin::TestUserError() { + TestLoginVisible(); + ExpectValid(kAdOfflineAuthId, kAdUserInput, false); +} + +void ActiveDirectoryLoginMixin::SetUserInput(const std::string& value) { + test::OobeJS().TypeIntoPath(value, {kAdOfflineAuthId, kAdUserInput}); +} + +void ActiveDirectoryLoginMixin::TestUserInput(const std::string& value) { + test::OobeJS().ExpectEQ( + test::GetOobeElementPath({kAdOfflineAuthId, kAdUserInput}) + ".value", + value); +} + +// Checks if password input is marked as invalid. +void ActiveDirectoryLoginMixin::TestPasswordError() { + TestLoginVisible(); + ExpectValid(kAdOfflineAuthId, kAdPasswordInput, false); +} + +// Checks that machine, password and user inputs are valid. +void ActiveDirectoryLoginMixin::TestNoError() { + TestLoginVisible(); + ExpectValid(kAdOfflineAuthId, kAdMachineInput, true); + ExpectValid(kAdOfflineAuthId, kAdUserInput, true); + ExpectValid(kAdOfflineAuthId, kAdPasswordInput, true); +} + +// Checks if autocomplete domain is visible for the user input. +void ActiveDirectoryLoginMixin::TestDomainVisible() { + test::OobeJS().ExpectTrue( + "!" + JSElement(kAdOfflineAuthId, kAdAutocompleteRealm) + ".hidden"); +} + +// Checks if autocomplete domain is hidden for the user input. +void ActiveDirectoryLoginMixin::TestDomainHidden() { + test::OobeJS().ExpectTrue(JSElement(kAdOfflineAuthId, kAdAutocompleteRealm) + + ".hidden"); +} + +void ActiveDirectoryLoginMixin::TestPasswordChangeNoErrors() { + TestPasswordChangeError(""); +} + +void ActiveDirectoryLoginMixin::TestPasswordChangeOldPasswordError() { + TestPasswordChangeError(kAdOldPasswordInput); +} + +void ActiveDirectoryLoginMixin::TestPasswordChangeNewPasswordError() { + TestPasswordChangeError(kAdNewPassword1Input); +} + +void ActiveDirectoryLoginMixin::TestPasswordChangeConfirmNewPasswordError() { + TestPasswordChangeError(kAdNewPassword2Input); +} + +// Checks if Active Directory password change screen is shown. Also checks if +// |invalid_element| is invalidated and all the other elements are valid. +void ActiveDirectoryLoginMixin::TestPasswordChangeError( + const std::string& invalid_element) { + TestPasswordChangeVisible(); + for (const char* element : + {kAdOldPasswordInput, kAdNewPassword1Input, kAdNewPassword2Input}) { + std::string js_assertion = + test::GetOobeElementPath({kPasswordChangeId, element}) + ".isInvalid"; + if (element != invalid_element) + js_assertion = "!" + js_assertion; + test::OobeJS().ExpectTrue(js_assertion); + } +} + +// Sets username and password for the Active Directory login and submits it. +void ActiveDirectoryLoginMixin::SubmitActiveDirectoryCredentials( + const std::string& username, + const std::string& password) { + test::OobeJS().TypeIntoPath(username, {kAdOfflineAuthId, kAdUserInput}); + test::OobeJS().TypeIntoPath(password, {kAdOfflineAuthId, kAdPasswordInput}); + test::OobeJS().TapOnPath({kAdOfflineAuthId, kAdCredsButton}); +} + +// Sets username and password for the Active Directory login and submits it. +void ActiveDirectoryLoginMixin::SubmitActiveDirectoryPasswordChangeCredentials( + const std::string& old_password, + const std::string& new_password1, + const std::string& new_password2) { + test::OobeJS().TypeIntoPath(old_password, + {kPasswordChangeId, kAdOldPasswordInput}); + test::OobeJS().TypeIntoPath(new_password1, + {kPasswordChangeId, kAdNewPassword1Input}); + test::OobeJS().TypeIntoPath(new_password2, + {kPasswordChangeId, kAdNewPassword2Input}); + test::OobeJS().TapOnPath( + {kPasswordChangeId, kPasswordChangeFormId, kFormButtonId}); +} + +void ActiveDirectoryLoginMixin::SetupActiveDirectoryJSNotifications() { + test::OobeJS().Evaluate( + "var testInvalidateAd = login.GaiaSigninScreen.invalidateAd;" + "login.GaiaSigninScreen.invalidateAd = function(user, errorState) {" + " testInvalidateAd(user, errorState);" + " window.domAutomationController.send('ShowAuthError');" + "}"); +} + +void ActiveDirectoryLoginMixin::WaitForAuthError() { + const std::string& expected_message = "\"ShowAuthError\""; + std::string message; + do { + ASSERT_TRUE(message_queue_->WaitForMessage(&message)); + } while (message != expected_message); +} + +// Returns string representing element with id=|element_id| inside Active +// Directory login element. +std::string ActiveDirectoryLoginMixin::JSElement(const std::string& parent_id, + const std::string& selector) { + return "document.querySelector('#" + parent_id + "')." + selector; +} + +ActiveDirectoryLoginMixin::~ActiveDirectoryLoginMixin() = default; + +} // namespace chromeos
diff --git a/chrome/browser/chromeos/login/test/active_directory_login_mixin.h b/chrome/browser/chromeos/login/test/active_directory_login_mixin.h new file mode 100644 index 0000000..10a4f5b7 --- /dev/null +++ b/chrome/browser/chromeos/login/test/active_directory_login_mixin.h
@@ -0,0 +1,97 @@ +// Copyright 2019 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 CHROME_BROWSER_CHROMEOS_LOGIN_TEST_ACTIVE_DIRECTORY_LOGIN_MIXIN_H_ +#define CHROME_BROWSER_CHROMEOS_LOGIN_TEST_ACTIVE_DIRECTORY_LOGIN_MIXIN_H_ + +#include <memory> + +#include "base/macros.h" +#include "base/values.h" +#include "chrome/browser/chromeos/login/mixin_based_in_process_browser_test.h" +#include "chrome/browser/chromeos/policy/server_backed_state_keys_broker.h" +#include "chrome/browser/policy/test/local_policy_test_server.h" +#include "chromeos/tpm/stub_install_attributes.h" +#include "components/policy/proto/chrome_device_policy.pb.h" +#include "components/policy/proto/device_management_backend.pb.h" +#include "content/public/test/browser_test_utils.h" + +namespace chromeos { + +// Handles interaction with Active Directory login screen and Active Directory +// password change screen. +class ActiveDirectoryLoginMixin : public InProcessBrowserTestMixin { + public: + explicit ActiveDirectoryLoginMixin(InProcessBrowserTestMixinHost* host, + const std::string& realm); + ~ActiveDirectoryLoginMixin() override; + + // InProcessBrowserTestMixin: + void SetUpInProcessBrowserTestFixture() override; + void SetUpOnMainThread() override; + + void set_autocomplete_realm(const std::string& autocomplete_realm) { + autocomplete_realm_ = autocomplete_realm; + } + + // Checks if Active Directory login is visible. + void TestLoginVisible(); + // Checks if Active Directory password change screen is shown. + void TestPasswordChangeVisible(); + // Checks if user input is marked as invalid. + void TestUserError(); + void SetUserInput(const std::string& value); + void TestUserInput(const std::string& value); + // Checks if password input is marked as invalid. + void TestPasswordError(); + // Checks that machine, password and user inputs are valid. + void TestNoError(); + // Checks if autocomplete domain is visible for the user input. + void TestDomainVisible(); + // Checks if autocomplete domain is hidden for the user input. + void TestDomainHidden(); + + void TriggerPasswordChangeScreen(); + void ClosePasswordChangeScreen(); + // Checks if Active Directory password change screen is shown. Also checks if + // |invalid_element| is invalidated and all the other elements are valid. + void TestPasswordChangeNoErrors(); + void TestPasswordChangeOldPasswordError(); + void TestPasswordChangeNewPasswordError(); + void TestPasswordChangeConfirmNewPasswordError(); + + // Sets username and password for the Active Directory login and submits it. + void SubmitActiveDirectoryCredentials(const std::string& username, + const std::string& password); + + // Sets username and password for the Active Directory login and submits it. + void SubmitActiveDirectoryPasswordChangeCredentials( + const std::string& old_password, + const std::string& new_password1, + const std::string& new_password2); + + // Waits when Active Directory screen been invalidated from inside Chrome. + void WaitForAuthError(); + + private: + void SetupActiveDirectoryJSNotifications(); + void TestPasswordChangeError(const std::string& invalid_element); + void ExpectValid(const std::string& parent_id, + const std::string& child_id, + bool valid); + // Returns string representing element with id=|element_id| inside Active + // Directory login element. + std::string JSElement(const std::string& parent_id, + const std::string& selector); + + std::string autocomplete_realm_; + std::unique_ptr<content::DOMMessageQueue> message_queue_; + chromeos::ScopedStubInstallAttributes install_attributes_; + + DISALLOW_COPY_AND_ASSIGN(ActiveDirectoryLoginMixin); +}; + +} // namespace chromeos + +#endif // CHROME_BROWSER_CHROMEOS_LOGIN_TEST_ACTIVE_DIRECTORY_LOGIN_MIXIN_H_
diff --git a/chrome/browser/chromeos/login/test/login_manager_mixin.cc b/chrome/browser/chromeos/login/test/login_manager_mixin.cc new file mode 100644 index 0000000..4c8b8a652 --- /dev/null +++ b/chrome/browser/chromeos/login/test/login_manager_mixin.cc
@@ -0,0 +1,97 @@ +// Copyright 2019 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 "chrome/browser/chromeos/login/test/login_manager_mixin.h" + +#include <memory> +#include <string> + +#include "base/command_line.h" +#include "chrome/browser/browser_process.h" +#include "chrome/browser/chrome_browser_main.h" +#include "chrome/browser/chrome_browser_main_extra_parts.h" +#include "chrome/browser/chromeos/login/session/user_session_manager.h" +#include "chrome/browser/chromeos/login/session/user_session_manager_test_api.h" +#include "chrome/browser/chromeos/login/startup_utils.h" +#include "chromeos/constants/chromeos_switches.h" +#include "components/prefs/pref_service.h" +#include "components/prefs/scoped_user_pref_update.h" +#include "components/user_manager/fake_user_manager.h" +#include "components/user_manager/known_user.h" +#include "components/user_manager/scoped_user_manager.h" + +namespace chromeos { + +namespace { + +// Chrome main extra part used for login manager tests to set up initially +// registered users. The main part injects itself into browser startup after +// local state has been set up, but before the user manager instance is created. +// It adds list of "known" users to the local state, so user manager can load +// them during its initialization. +// When users are registered, it marks OOBE as complete. +class TestUserRegistrationMainExtra : public ChromeBrowserMainExtraParts { + public: + explicit TestUserRegistrationMainExtra(const std::vector<AccountId>& users) + : users_(users) {} + ~TestUserRegistrationMainExtra() override = default; + + // ChromeBrowserMainExtraParts: + void PostEarlyInitialization() override { + // SaveKnownUser depends on UserManager to get the local state that has to + // be updated, and do ephemeral user checks. + // Given that user manager does not exist yet (by design), create a + // temporary fake user manager instance. + { + auto user_manager = std::make_unique<user_manager::FakeUserManager>(); + user_manager->set_local_state(g_browser_process->local_state()); + user_manager::ScopedUserManager scoper(std::move(user_manager)); + for (const auto& account_id : users_) { + ListPrefUpdate users_pref(g_browser_process->local_state(), + "LoggedInUsers"); + users_pref->AppendIfNotPresent( + std::make_unique<base::Value>(account_id.GetUserEmail())); + + user_manager::known_user::UpdateId(account_id); + } + } + + StartupUtils::MarkOobeCompleted(); + } + + private: + const std::vector<AccountId> users_; + + DISALLOW_COPY_AND_ASSIGN(TestUserRegistrationMainExtra); +}; + +} // namespace + +LoginManagerMixin::LoginManagerMixin( + InProcessBrowserTestMixinHost* host, + const std::vector<AccountId>& initial_users) + : InProcessBrowserTestMixin(host), initial_users_(initial_users) {} + +LoginManagerMixin::~LoginManagerMixin() = default; + +void LoginManagerMixin::SetUpCommandLine(base::CommandLine* command_line) { + command_line->AppendSwitch(chromeos::switches::kLoginManager); + command_line->AppendSwitch(chromeos::switches::kForceLoginManagerInTests); +} + +void LoginManagerMixin::CreatedBrowserMainParts( + content::BrowserMainParts* browser_main_parts) { + // |browser_main_parts| take ownership of TestUserRegistrationMainExtra. + static_cast<ChromeBrowserMainParts*>(browser_main_parts) + ->AddParts(new TestUserRegistrationMainExtra(initial_users_)); +} + +void LoginManagerMixin::SetUpOnMainThread() { + test::UserSessionManagerTestApi session_manager_test_api( + UserSessionManager::GetInstance()); + session_manager_test_api.SetShouldLaunchBrowserInTests(false); + session_manager_test_api.SetShouldObtainTokenHandleInTests(false); +} + +} // namespace chromeos
diff --git a/chrome/browser/chromeos/login/test/login_manager_mixin.h b/chrome/browser/chromeos/login/test/login_manager_mixin.h new file mode 100644 index 0000000..35c50b8 --- /dev/null +++ b/chrome/browser/chromeos/login/test/login_manager_mixin.h
@@ -0,0 +1,41 @@ +// Copyright 2019 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 CHROME_BROWSER_CHROMEOS_LOGIN_TEST_LOGIN_MANAGER_MIXIN_H_ +#define CHROME_BROWSER_CHROMEOS_LOGIN_TEST_LOGIN_MANAGER_MIXIN_H_ + +#include <vector> + +#include "base/macros.h" +#include "chrome/browser/chromeos/login/mixin_based_in_process_browser_test.h" +#include "components/account_id/account_id.h" + +namespace chromeos { + +// Mixin browser tests can use for setting up test login manager environment. +// It sets up command line so test starts on the login screen UI, and +// initializes user manager with a list of pre-registered users. +// The mixin will mark the OOBE flow as complete during test setup, so it's not +// suitable for OOBE tests. +class LoginManagerMixin : public InProcessBrowserTestMixin { + public: + LoginManagerMixin(InProcessBrowserTestMixinHost* host, + const std::vector<AccountId>& initial_users); + ~LoginManagerMixin() override; + + // InProcessBrowserTestMixin: + void SetUpCommandLine(base::CommandLine* command_line) override; + void CreatedBrowserMainParts( + content::BrowserMainParts* browser_main_parts) override; + void SetUpOnMainThread() override; + + private: + const std::vector<AccountId> initial_users_; + + DISALLOW_COPY_AND_ASSIGN(LoginManagerMixin); +}; + +} // namespace chromeos + +#endif // CHROME_BROWSER_CHROMEOS_LOGIN_TEST_LOGIN_MANAGER_MIXIN_H_
diff --git a/chrome/browser/chromeos/login/ui/login_display.h b/chrome/browser/chromeos/login/ui/login_display.h index 11defe7..1fbaf37d 100644 --- a/chrome/browser/chromeos/login/ui/login_display.h +++ b/chrome/browser/chromeos/login/ui/login_display.h
@@ -121,9 +121,6 @@ // signin but whitelist check fails. virtual void ShowWhitelistCheckFailedError() = 0; - // Show unrecoverable cryptohome error dialog. - virtual void ShowUnrecoverableCrypthomeErrorDialog() = 0; - Delegate* delegate() { return delegate_; } void set_delegate(Delegate* delegate) { delegate_ = delegate; }
diff --git a/chrome/browser/chromeos/login/ui/login_display_host_mojo.cc b/chrome/browser/chromeos/login/ui/login_display_host_mojo.cc index eb4de186..a8930a91 100644 --- a/chrome/browser/chromeos/login/ui/login_display_host_mojo.cc +++ b/chrome/browser/chromeos/login/ui/login_display_host_mojo.cc
@@ -91,12 +91,6 @@ dialog_->Show(); } -void LoginDisplayHostMojo::ShowUnrecoverableCrypthomeErrorDialog() { - DCHECK(GetOobeUI()); - GetOobeUI()->signin_screen_handler()->ShowUnrecoverableCrypthomeErrorDialog(); - dialog_->Show(); -} - void LoginDisplayHostMojo::ShowErrorScreen(LoginDisplay::SigninError error_id) { DCHECK(GetOobeUI()); GetOobeUI()->signin_screen_handler()->ShowErrorScreen(error_id); @@ -454,6 +448,12 @@ } } +void LoginDisplayHostMojo::OnPasswordChangeDetected() {} + +void LoginDisplayHostMojo::OnOldEncryptionDetected( + const UserContext& user_context, + bool has_incomplete_migration) {} + void LoginDisplayHostMojo::LoadOobeDialog() { if (dialog_) return;
diff --git a/chrome/browser/chromeos/login/ui/login_display_host_mojo.h b/chrome/browser/chromeos/login/ui/login_display_host_mojo.h index 6219862..a8626ea 100644 --- a/chrome/browser/chromeos/login/ui/login_display_host_mojo.h +++ b/chrome/browser/chromeos/login/ui/login_display_host_mojo.h
@@ -50,9 +50,6 @@ // signin but whitelist check fails. void ShowWhitelistCheckFailedError(); - // Show unrecoverable cryptohome error dialog. - void ShowUnrecoverableCrypthomeErrorDialog(); - // Displays detailed error screen for error with ID |error_id|. void ShowErrorScreen(LoginDisplay::SigninError error_id); @@ -119,6 +116,9 @@ // AuthStatusConsumer: void OnAuthFailure(const AuthFailure& error) override; void OnAuthSuccess(const UserContext& user_context) override; + void OnPasswordChangeDetected() override; + void OnOldEncryptionDetected(const UserContext& user_context, + bool has_incomplete_migration) override; private: void LoadOobeDialog();
diff --git a/chrome/browser/chromeos/login/ui/login_display_mojo.cc b/chrome/browser/chromeos/login/ui/login_display_mojo.cc index 501fe8d..887f23f 100644 --- a/chrome/browser/chromeos/login/ui/login_display_mojo.cc +++ b/chrome/browser/chromeos/login/ui/login_display_mojo.cc
@@ -169,10 +169,6 @@ host_->ShowWhitelistCheckFailedError(); } -void LoginDisplayMojo::ShowUnrecoverableCrypthomeErrorDialog() { - host_->ShowUnrecoverableCrypthomeErrorDialog(); -} - void LoginDisplayMojo::Login(const UserContext& user_context, const SigninSpecifics& specifics) { if (delegate_)
diff --git a/chrome/browser/chromeos/login/ui/login_display_mojo.h b/chrome/browser/chromeos/login/ui/login_display_mojo.h index 835277b..1b2969d7 100644 --- a/chrome/browser/chromeos/login/ui/login_display_mojo.h +++ b/chrome/browser/chromeos/login/ui/login_display_mojo.h
@@ -45,7 +45,6 @@ const std::string& email) override; void ShowSigninUI(const std::string& email) override; void ShowWhitelistCheckFailedError() override; - void ShowUnrecoverableCrypthomeErrorDialog() override; // SigninScreenHandlerDelegate: void Login(const UserContext& user_context,
diff --git a/chrome/browser/chromeos/login/ui/login_display_webui.cc b/chrome/browser/chromeos/login/ui/login_display_webui.cc index 1126bb3..bba65bf9 100644 --- a/chrome/browser/chromeos/login/ui/login_display_webui.cc +++ b/chrome/browser/chromeos/login/ui/login_display_webui.cc
@@ -178,11 +178,6 @@ webui_handler_->ShowWhitelistCheckFailedError(); } -void LoginDisplayWebUI::ShowUnrecoverableCrypthomeErrorDialog() { - if (webui_handler_) - webui_handler_->ShowUnrecoverableCrypthomeErrorDialog(); -} - // LoginDisplayWebUI, NativeWindowDelegate implementation: --------------------- gfx::NativeWindow LoginDisplayWebUI::GetNativeWindow() const { return parent_window();
diff --git a/chrome/browser/chromeos/login/ui/login_display_webui.h b/chrome/browser/chromeos/login/ui/login_display_webui.h index 89ad383..8198e19 100644 --- a/chrome/browser/chromeos/login/ui/login_display_webui.h +++ b/chrome/browser/chromeos/login/ui/login_display_webui.h
@@ -47,7 +47,6 @@ const std::string& email) override; void ShowSigninUI(const std::string& email) override; void ShowWhitelistCheckFailedError() override; - void ShowUnrecoverableCrypthomeErrorDialog() override; // NativeWindowDelegate implementation: gfx::NativeWindow GetNativeWindow() const override;
diff --git a/chrome/browser/chromeos/login/ui/mock_login_display.h b/chrome/browser/chromeos/login/ui/mock_login_display.h index b79e86e..6943da1 100644 --- a/chrome/browser/chromeos/login/ui/mock_login_display.h +++ b/chrome/browser/chromeos/login/ui/mock_login_display.h
@@ -26,7 +26,6 @@ MOCK_METHOD2(ShowPasswordChangedDialog, void(bool, const std::string&)); MOCK_METHOD1(ShowSigninUI, void(const std::string&)); MOCK_METHOD0(ShowWhitelistCheckFailedError, void(void)); - MOCK_METHOD0(ShowUnrecoverableCrypthomeErrorDialog, void()); private: DISALLOW_COPY_AND_ASSIGN(MockLoginDisplay);
diff --git a/chrome/browser/chromeos/login/wizard_controller.cc b/chrome/browser/chromeos/login/wizard_controller.cc index 568bbe44..d8d37f51 100644 --- a/chrome/browser/chromeos/login/wizard_controller.cc +++ b/chrome/browser/chromeos/login/wizard_controller.cc
@@ -980,12 +980,7 @@ } void WizardController::OnSyncConsentFinished() { - if (chromeos::quick_unlock::IsFingerprintEnabled( - ProfileManager::GetActiveUserProfile())) { - ShowFingerprintSetupScreen(); - } else { - ShowDiscoverScreen(); - } + ShowFingerprintSetupScreen(); } void WizardController::OnFingerprintSetupScreenExit() {
diff --git a/chrome/browser/chromeos/login/wizard_controller_browsertest.cc b/chrome/browser/chromeos/login/wizard_controller_browsertest.cc index 826816c..4de98d8 100644 --- a/chrome/browser/chromeos/login/wizard_controller_browsertest.cc +++ b/chrome/browser/chromeos/login/wizard_controller_browsertest.cc
@@ -2879,8 +2879,6 @@ // TODO(alemate): Add tests for Discover UI. -// TODO(xiaoyinh): Add tests for Fingerprint Setup UI. - // TODO(alemate): Add tests for Marketing Opt-In. // TODO(khorimoto): Add tests for MultiDevice Setup UI.
diff --git a/chrome/browser/chromeos/policy/device_cloud_policy_initializer.cc b/chrome/browser/chromeos/policy/device_cloud_policy_initializer.cc index e6ce8211..4f295c1d 100644 --- a/chrome/browser/chromeos/policy/device_cloud_policy_initializer.cc +++ b/chrome/browser/chromeos/policy/device_cloud_policy_initializer.cc
@@ -17,11 +17,11 @@ #include "chrome/browser/chromeos/attestation/attestation_ca_client.h" #include "chrome/browser/chromeos/policy/device_cloud_policy_manager_chromeos.h" #include "chrome/browser/chromeos/policy/device_cloud_policy_store_chromeos.h" -#include "chrome/browser/chromeos/policy/device_status_collector.h" #include "chrome/browser/chromeos/policy/enrollment_config.h" #include "chrome/browser/chromeos/policy/enrollment_handler_chromeos.h" #include "chrome/browser/chromeos/policy/enrollment_status_chromeos.h" #include "chrome/browser/chromeos/policy/server_backed_device_state.h" +#include "chrome/browser/chromeos/policy/status_collector/device_status_collector.h" #include "chrome/browser/net/system_network_context_manager.h" #include "chrome/common/chrome_content_client.h" #include "chrome/common/pref_names.h"
diff --git a/chrome/browser/chromeos/policy/device_cloud_policy_manager_chromeos.cc b/chrome/browser/chromeos/policy/device_cloud_policy_manager_chromeos.cc index 13b4644..d77b6698 100644 --- a/chrome/browser/chromeos/policy/device_cloud_policy_manager_chromeos.cc +++ b/chrome/browser/chromeos/policy/device_cloud_policy_manager_chromeos.cc
@@ -26,10 +26,10 @@ #include "chrome/browser/chromeos/login/enrollment/auto_enrollment_controller.h" #include "chrome/browser/chromeos/login/startup_utils.h" #include "chrome/browser/chromeos/policy/device_cloud_policy_store_chromeos.h" -#include "chrome/browser/chromeos/policy/device_status_collector.h" #include "chrome/browser/chromeos/policy/heartbeat_scheduler.h" #include "chrome/browser/chromeos/policy/remote_commands/device_commands_factory_chromeos.h" #include "chrome/browser/chromeos/policy/server_backed_state_keys_broker.h" +#include "chrome/browser/chromeos/policy/status_collector/device_status_collector.h" #include "chrome/browser/chromeos/policy/status_uploader.h" #include "chrome/browser/chromeos/policy/system_log_uploader.h" #include "chrome/common/pref_names.h"
diff --git a/chrome/browser/chromeos/policy/device_status_collector.cc b/chrome/browser/chromeos/policy/status_collector/device_status_collector.cc similarity index 95% rename from chrome/browser/chromeos/policy/device_status_collector.cc rename to chrome/browser/chromeos/policy/status_collector/device_status_collector.cc index f025bd41..b600316 100644 --- a/chrome/browser/chromeos/policy/device_status_collector.cc +++ b/chrome/browser/chromeos/policy/status_collector/device_status_collector.cc
@@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "chrome/browser/chromeos/policy/device_status_collector.h" +#include "chrome/browser/chromeos/policy/status_collector/device_status_collector.h" #include <stddef.h> #include <stdint.h> @@ -315,31 +315,6 @@ std::move(callback))); } -// Returns the DeviceLocalAccount associated with the current kiosk session. -// Returns null if there is no active kiosk session, or if that kiosk -// session has been removed from policy since the session started, in which -// case we won't report its status). -std::unique_ptr<policy::DeviceLocalAccount> GetCurrentKioskDeviceLocalAccount( - chromeos::CrosSettings* settings) { - if (!user_manager::UserManager::Get()->IsLoggedInAsKioskApp() && - !user_manager::UserManager::Get()->IsLoggedInAsArcKioskApp()) { - return std::unique_ptr<policy::DeviceLocalAccount>(); - } - const user_manager::User* const user = - user_manager::UserManager::Get()->GetActiveUser(); - const std::vector<policy::DeviceLocalAccount> accounts = - policy::GetDeviceLocalAccounts(settings); - - for (const auto& device_local_account : accounts) { - if (AccountId::FromUserEmail(device_local_account.user_id) == - user->GetAccountId()) { - return std::make_unique<policy::DeviceLocalAccount>(device_local_account); - } - } - LOG(WARNING) << "Kiosk app not found in list of device-local accounts"; - return std::unique_ptr<policy::DeviceLocalAccount>(); -} - base::Version GetPlatformVersion() { return base::Version(base::SysInfo::OperatingSystemVersion()); } @@ -396,20 +371,20 @@ public: explicit GetStatusState( const scoped_refptr<base::SequencedTaskRunner> task_runner, - const policy::DeviceStatusCollector::StatusCallback& response) + const policy::StatusCollectorCallback& response) : task_runner_(task_runner), response_(response) {} inline em::DeviceStatusReportRequest* device_status() { - return device_status_.get(); + return response_params_.device_status.get(); } inline em::SessionStatusReportRequest* session_status() { - return session_status_.get(); + return response_params_.session_status.get(); } - inline void ResetDeviceStatus() { device_status_.reset(); } + inline void ResetDeviceStatus() { response_params_.device_status.reset(); } - inline void ResetSessionStatus() { session_status_.reset(); } + inline void ResetSessionStatus() { response_params_.session_status.reset(); } // Queues an async callback to query disk volume information. void SampleVolumeInfo(const policy::DeviceStatusCollector::VolumeInfoFetcher& @@ -473,27 +448,26 @@ // not called. ~GetStatusState() { task_runner_->PostTask( - FROM_HERE, base::BindOnce(response_, base::Passed(&device_status_), - base::Passed(&session_status_))); + FROM_HERE, base::BindOnce(response_, base::Passed(&response_params_))); } void OnVolumeInfoReceived(const std::vector<em::VolumeInfo>& volume_info) { - device_status_->clear_volume_infos(); + response_params_.device_status->clear_volume_infos(); for (const em::VolumeInfo& info : volume_info) - *device_status_->add_volume_infos() = info; + *response_params_.device_status->add_volume_infos() = info; } void OnCPUTempInfoReceived( const std::vector<em::CPUTempInfo>& cpu_temp_info) { // Only one of OnProbeDataReceived and OnCPUTempInfoReceived should be // called. - DCHECK(device_status_->cpu_temp_infos_size() == 0); + DCHECK(response_params_.device_status->cpu_temp_infos_size() == 0); DLOG_IF(WARNING, cpu_temp_info.empty()) << "Unable to read CPU temp information."; base::Time timestamp = base::Time::Now(); for (const em::CPUTempInfo& info : cpu_temp_info) { - auto* new_info = device_status_->add_cpu_temp_infos(); + auto* new_info = response_params_.device_status->add_cpu_temp_infos(); *new_info = info; new_info->set_timestamp(timestamp.ToJavaTime()); } @@ -502,7 +476,7 @@ void OnAndroidInfoReceived(const std::string& status, const std::string& droid_guard_info) { em::AndroidStatus* const android_status = - session_status_->mutable_android_status(); + response_params_.session_status->mutable_android_status(); android_status->set_status_payload(status); android_status->set_droid_guard_info(droid_guard_info); } @@ -511,7 +485,7 @@ // Make sure we edit the state on the right thread. DCHECK_CURRENTLY_ON(content::BrowserThread::UI); em::TpmStatusInfo* const tpm_status_proto = - device_status_->mutable_tpm_status_info(); + response_params_.device_status->mutable_tpm_status_info(); tpm_status_proto->set_enabled(tpm_status_struct.enabled); tpm_status_proto->set_owned(tpm_status_struct.owned); @@ -540,13 +514,13 @@ // Only one of OnProbeDataReceived and OnCPUTempInfoReceived should be // called. - DCHECK(device_status_->cpu_temp_infos_size() == 0); + DCHECK(response_params_.device_status->cpu_temp_infos_size() == 0); // Store CPU measurement samples. for (const std::unique_ptr<SampledData>& sample_data : samples) { for (auto it = sample_data->cpu_samples.begin(); it != sample_data->cpu_samples.end(); it++) { - auto* new_info = device_status_->add_cpu_temp_infos(); + auto* new_info = response_params_.device_status->add_cpu_temp_infos(); *new_info = it->second; } } @@ -559,7 +533,7 @@ } if (probe_result.value().battery_size() > 0) { em::PowerStatus* const power_status = - device_status_->mutable_power_status(); + response_params_.device_status->mutable_power_status(); for (const auto& battery : probe_result.value().battery()) { em::BatteryInfo* const battery_info = power_status->add_batteries(); battery_info->set_serial(battery.values().serial_number()); @@ -583,7 +557,7 @@ } if (probe_result.value().storage_size() > 0) { em::StorageStatus* const storage_status = - device_status_->mutable_storage_status(); + response_params_.device_status->mutable_storage_status(); for (const auto& storage : probe_result.value().storage()) { em::DiskInfo* const disk_info = storage_status->add_disks(); disk_info->set_serial(base::NumberToString(storage.values().serial())); @@ -597,11 +571,8 @@ } const scoped_refptr<base::SequencedTaskRunner> task_runner_; - policy::DeviceStatusCollector::StatusCallback response_; - std::unique_ptr<em::DeviceStatusReportRequest> device_status_ = - std::make_unique<em::DeviceStatusReportRequest>(); - std::unique_ptr<em::SessionStatusReportRequest> session_status_ = - std::make_unique<em::SessionStatusReportRequest>(); + policy::StatusCollectorCallback response_; + StatusCollectorParams response_params_; }; // Handles storing activity time periods needed for reporting. Provides @@ -1353,28 +1324,6 @@ last_active_check_ = now; } -std::unique_ptr<DeviceLocalAccount> -DeviceStatusCollector::GetAutoLaunchedKioskSessionInfo() { - std::unique_ptr<DeviceLocalAccount> account = - GetCurrentKioskDeviceLocalAccount(cros_settings_); - if (account) { - chromeos::KioskAppManager::App current_app; - bool regular_app_auto_launched_with_zero_delay = - chromeos::KioskAppManager::Get()->GetApp(account->kiosk_app_id, - ¤t_app) && - current_app.was_auto_launched_with_zero_delay; - bool arc_app_auto_launched_with_zero_delay = - chromeos::ArcKioskAppManager::Get() - ->current_app_was_auto_launched_with_zero_delay(); - if (regular_app_auto_launched_with_zero_delay || - arc_app_auto_launched_with_zero_delay) { - return account; - } - } - // No auto-launched kiosk session active. - return std::unique_ptr<DeviceLocalAccount>(); -} - void DeviceStatusCollector::SampleResourceUsage() { // Results must be written in the creation thread since that's where they // are read from in the Get*StatusAsync methods. @@ -1787,8 +1736,9 @@ // Don't write any network state if we aren't in a kiosk or public session. if (!GetAutoLaunchedKioskSessionInfo() && - !user_manager::UserManager::Get()->IsLoggedInAsPublicAccount()) + !user_manager::UserManager::Get()->IsLoggedInAsPublicAccount()) { return anything_reported; + } // Walk the various networks and store their state in the status report. chromeos::NetworkStateHandler::NetworkStateList state_list; @@ -1996,8 +1946,8 @@ return true; } -void DeviceStatusCollector::GetDeviceAndSessionStatusAsync( - const StatusCallback& response) { +void DeviceStatusCollector::GetStatusAsync( + const StatusCollectorCallback& response) { // Must be on creation thread since some stats are written to in that thread // and accessing them from another thread would lead to race conditions. DCHECK(thread_checker_.CalledOnValidThread()); @@ -2200,12 +2150,27 @@ return extension->VersionString(); } +// TODO(crbug.com/827386): move public API methods above private ones after +// common methods are extracted. void DeviceStatusCollector::OnSubmittedSuccessfully() { activity_storage_->TrimActivityPeriods(last_reported_day_, duration_for_last_reported_day_, std::numeric_limits<int64_t>::max()); } +bool DeviceStatusCollector::ShouldReportActivityTimes() const { + return report_activity_times_; +} +bool DeviceStatusCollector::ShouldReportNetworkInterfaces() const { + return report_network_interfaces_; +} +bool DeviceStatusCollector::ShouldReportUsers() const { + return report_users_; +} +bool DeviceStatusCollector::ShouldReportHardwareStatus() const { + return report_hardware_status_; +} + void DeviceStatusCollector::OnOSVersion(const std::string& version) { os_version_ = version; }
diff --git a/chrome/browser/chromeos/policy/device_status_collector.h b/chrome/browser/chromeos/policy/status_collector/device_status_collector.h similarity index 91% rename from chrome/browser/chromeos/policy/device_status_collector.h rename to chrome/browser/chromeos/policy/status_collector/device_status_collector.h index ce15dd7e..be309f03 100644 --- a/chrome/browser/chromeos/policy/device_status_collector.h +++ b/chrome/browser/chromeos/policy/status_collector/device_status_collector.h
@@ -2,8 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef CHROME_BROWSER_CHROMEOS_POLICY_DEVICE_STATUS_COLLECTOR_H_ -#define CHROME_BROWSER_CHROMEOS_POLICY_DEVICE_STATUS_COLLECTOR_H_ +#ifndef CHROME_BROWSER_CHROMEOS_POLICY_STATUS_COLLECTOR_DEVICE_STATUS_COLLECTOR_H_ +#define CHROME_BROWSER_CHROMEOS_POLICY_STATUS_COLLECTOR_DEVICE_STATUS_COLLECTOR_H_ #include <stdint.h> @@ -25,6 +25,7 @@ #include "base/time/time.h" #include "base/timer/timer.h" #include "chrome/browser/chromeos/child_accounts/usage_time_state_notifier.h" +#include "chrome/browser/chromeos/policy/status_collector/status_collector.h" #include "chrome/browser/chromeos/settings/cros_settings.h" #include "chromeos/dbus/cryptohome/cryptohome_client.h" #include "chromeos/dbus/power/power_manager_client.h" @@ -41,7 +42,7 @@ namespace system { class StatisticsProvider; } -} +} // namespace chromeos namespace cryptohome { struct TpmStatusInfo; @@ -112,13 +113,14 @@ }; // Collects and summarizes the status of an enterprised-managed ChromeOS device. -class DeviceStatusCollector : public session_manager::SessionManagerObserver, +class DeviceStatusCollector : public StatusCollector, + public session_manager::SessionManagerObserver, public chromeos::UsageTimeStateNotifier::Observer, public chromeos::PowerManagerClient::Observer { public: - using VolumeInfoFetcher = base::Callback< - std::vector<enterprise_management::VolumeInfo>( - const std::vector<std::string>& mount_points)>; + using VolumeInfoFetcher = + base::Callback<std::vector<enterprise_management::VolumeInfo>( + const std::vector<std::string>& mount_points)>; // Reads the first CPU line from /proc/stat. Returns an empty string if // the cpu data could not be read. Broken out into a callback to enable @@ -157,12 +159,6 @@ // Gets the ProbeResult/sampled data and passes it to ProbeDataReceiver. using ProbeDataFetcher = base::RepeatingCallback<void(ProbeDataReceiver)>; - // Called in the UI thread after the device and session status have been - // collected asynchronously in GetDeviceAndSessionStatusAsync. Null pointers - // indicate errors or that device or session status reporting is disabled. - using StatusCallback = base::Callback<void( - std::unique_ptr<enterprise_management::DeviceStatusReportRequest>, - std::unique_ptr<enterprise_management::SessionStatusReportRequest>)>; // Constructor. Callers can inject their own *Fetcher callbacks, e.g. for unit // testing. A null callback can be passed for any *Fetcher parameter, to use // the default implementation. These callbacks are always executed on Blocking @@ -182,29 +178,17 @@ bool is_enterprise_reporting); ~DeviceStatusCollector() override; - // Gathers device and session status information and calls the passed response - // callback. Null pointers passed into the response indicate errors or that - // device or session status reporting is disabled. - virtual void GetDeviceAndSessionStatusAsync(const StatusCallback& response); - - // Called after the status information has successfully been submitted to - // the server. - virtual void OnSubmittedSuccessfully(); + // StatusCollector: + void GetStatusAsync(const StatusCollectorCallback& response) override; + void OnSubmittedSuccessfully() override; + bool ShouldReportActivityTimes() const override; + bool ShouldReportNetworkInterfaces() const override; + bool ShouldReportUsers() const override; + bool ShouldReportHardwareStatus() const override; static void RegisterPrefs(PrefRegistrySimple* registry); static void RegisterProfilePrefs(PrefRegistrySimple* registry); - // Returns the DeviceLocalAccount associated with the currently active - // kiosk session, if the session was auto-launched with zero delay - // (this enables functionality such as network reporting). - // Virtual to allow mocking. - virtual std::unique_ptr<DeviceLocalAccount> GetAutoLaunchedKioskSessionInfo(); - - bool report_activity_times() const { return report_activity_times_; } - bool report_network_interfaces() const { return report_network_interfaces_; } - bool report_users() const { return report_users_; } - bool report_hardware_status() const { return report_hardware_status_; } - // How often, in seconds, to poll to see if the user is idle. static const unsigned int kIdlePollIntervalSeconds = 30; @@ -520,4 +504,4 @@ } // namespace policy -#endif // CHROME_BROWSER_CHROMEOS_POLICY_DEVICE_STATUS_COLLECTOR_H_ +#endif // CHROME_BROWSER_CHROMEOS_POLICY_STATUS_COLLECTOR_DEVICE_STATUS_COLLECTOR_H_
diff --git a/chrome/browser/chromeos/policy/device_status_collector_browsertest.cc b/chrome/browser/chromeos/policy/status_collector/device_status_collector_browsertest.cc similarity index 99% rename from chrome/browser/chromeos/policy/device_status_collector_browsertest.cc rename to chrome/browser/chromeos/policy/status_collector/device_status_collector_browsertest.cc index f2854eb..fb2fa40 100644 --- a/chrome/browser/chromeos/policy/device_status_collector_browsertest.cc +++ b/chrome/browser/chromeos/policy/status_collector/device_status_collector_browsertest.cc
@@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "chrome/browser/chromeos/policy/device_status_collector.h" +#include "chrome/browser/chromeos/policy/status_collector/device_status_collector.h" #include <stddef.h> #include <stdint.h> @@ -178,8 +178,8 @@ kiosk_account_ = std::move(account); } - std::unique_ptr<policy::DeviceLocalAccount> GetAutoLaunchedKioskSessionInfo() - override { + std::unique_ptr<policy::DeviceLocalAccount> + GetAutoLaunchedKioskSessionInfo() override { if (kiosk_account_) return std::make_unique<policy::DeviceLocalAccount>(*kiosk_account_); return std::unique_ptr<policy::DeviceLocalAccount>(); @@ -513,20 +513,18 @@ session_status_.Clear(); got_session_status_ = false; run_loop_.reset(new base::RunLoop()); - status_collector_->GetDeviceAndSessionStatusAsync(base::BindRepeating( + status_collector_->GetStatusAsync(base::BindRepeating( &DeviceStatusCollectorTest::OnStatusReceived, base::Unretained(this))); run_loop_->Run(); run_loop_.reset(); } - void OnStatusReceived( - std::unique_ptr<em::DeviceStatusReportRequest> device_status, - std::unique_ptr<em::SessionStatusReportRequest> session_status) { - if (device_status) - device_status_ = *device_status; - got_session_status_ = session_status != nullptr; + void OnStatusReceived(StatusCollectorParams callback_params) { + if (callback_params.device_status) + device_status_ = *callback_params.device_status; + got_session_status_ = callback_params.session_status != nullptr; if (got_session_status_) - session_status_ = *session_status; + session_status_ = *callback_params.session_status; EXPECT_TRUE(run_loop_); run_loop_->Quit(); }
diff --git a/chrome/browser/chromeos/policy/status_collector/status_collector.cc b/chrome/browser/chromeos/policy/status_collector/status_collector.cc new file mode 100644 index 0000000..a0aa92a0 --- /dev/null +++ b/chrome/browser/chromeos/policy/status_collector/status_collector.cc
@@ -0,0 +1,80 @@ +// Copyright 2019 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 "chrome/browser/chromeos/policy/status_collector/status_collector.h" + +#include "chrome/browser/chromeos/app_mode/arc/arc_kiosk_app_manager.h" +#include "chrome/browser/chromeos/app_mode/kiosk_app_manager.h" +#include "chrome/browser/chromeos/policy/device_local_account.h" +#include "components/user_manager/user_manager.h" + +namespace policy { +namespace { + +namespace ent_mgmt = ::enterprise_management; + +// Returns the DeviceLocalAccount associated with the current kiosk session. +// Returns nullptr if there is no active kiosk session, or if that kiosk +// session has been removed from policy since the session started, in which +// case we won't report its status). +std::unique_ptr<DeviceLocalAccount> GetCurrentKioskDeviceLocalAccount( + chromeos::CrosSettings* settings) { + if (!user_manager::UserManager::Get()->IsLoggedInAsKioskApp() && + !user_manager::UserManager::Get()->IsLoggedInAsArcKioskApp()) { + return nullptr; + } + const user_manager::User* const user = + user_manager::UserManager::Get()->GetActiveUser(); + const std::vector<DeviceLocalAccount> accounts = + GetDeviceLocalAccounts(settings); + + for (const auto& device_local_account : accounts) { + if (AccountId::FromUserEmail(device_local_account.user_id) == + user->GetAccountId()) { + return std::make_unique<DeviceLocalAccount>(device_local_account); + } + } + LOG(WARNING) << "Kiosk app not found in list of device-local accounts"; + return nullptr; +} + +} // namespace + +StatusCollectorParams::StatusCollectorParams() { + device_status = std::make_unique<ent_mgmt::DeviceStatusReportRequest>(); + session_status = std::make_unique<ent_mgmt::SessionStatusReportRequest>(); + child_status = std::make_unique<ent_mgmt::ChildStatusReportRequest>(); +} +StatusCollectorParams::~StatusCollectorParams() = default; + +// Move only. +StatusCollectorParams::StatusCollectorParams(StatusCollectorParams&&) = default; +StatusCollectorParams& StatusCollectorParams::operator=( + StatusCollectorParams&&) = default; + +std::unique_ptr<DeviceLocalAccount> +StatusCollector::GetAutoLaunchedKioskSessionInfo() { + std::unique_ptr<DeviceLocalAccount> account = + GetCurrentKioskDeviceLocalAccount(chromeos::CrosSettings::Get()); + if (!account) { + // No auto-launched kiosk session active. + return nullptr; + } + + chromeos::KioskAppManager::App current_app; + bool regular_app_auto_launched_with_zero_delay = + chromeos::KioskAppManager::Get()->GetApp(account->kiosk_app_id, + ¤t_app) && + current_app.was_auto_launched_with_zero_delay; + bool arc_app_auto_launched_with_zero_delay = + chromeos::ArcKioskAppManager::Get() + ->current_app_was_auto_launched_with_zero_delay(); + + return regular_app_auto_launched_with_zero_delay || + arc_app_auto_launched_with_zero_delay + ? std::move(account) + : nullptr; +} + +} // namespace policy
diff --git a/chrome/browser/chromeos/policy/status_collector/status_collector.h b/chrome/browser/chromeos/policy/status_collector/status_collector.h new file mode 100644 index 0000000..8110556f --- /dev/null +++ b/chrome/browser/chromeos/policy/status_collector/status_collector.h
@@ -0,0 +1,79 @@ +// Copyright 2019 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 CHROME_BROWSER_CHROMEOS_POLICY_STATUS_COLLECTOR_STATUS_COLLECTOR_H_ +#define CHROME_BROWSER_CHROMEOS_POLICY_STATUS_COLLECTOR_STATUS_COLLECTOR_H_ + +#include <memory> + +#include "base/callback.h" +#include "components/policy/proto/device_management_backend.pb.h" + +namespace policy { + +struct DeviceLocalAccount; + +// Groups parameters that are necessary either directly in the +// |StatusCollectorCallback| or in async methods that work as callbacks and +// expect these exact same parameters. Absence of values indicates errors or +// that status reporting is disabled. +// +// Notice that the fields are used in different contexts, depending on the type +// of user: +// - Enterprise users: |device_status| and |session_status|. +// - Child users: +// - During the migration away from DeviceStatusCollector: +// |device_status|, |session_status|, |child_status|. +// - After migration: only |child_status|. +struct StatusCollectorParams { + StatusCollectorParams(); + ~StatusCollectorParams(); + + // Move only. + StatusCollectorParams(StatusCollectorParams&&); + StatusCollectorParams& operator=(StatusCollectorParams&&); + + std::unique_ptr<enterprise_management::DeviceStatusReportRequest> + device_status; + std::unique_ptr<enterprise_management::SessionStatusReportRequest> + session_status; + std::unique_ptr<enterprise_management::ChildStatusReportRequest> child_status; +}; + +// Called in the UI thread after the statuses have been collected +// asynchronously. +using StatusCollectorCallback = + base::RepeatingCallback<void(StatusCollectorParams)>; + +// Defines the API for a status collector. +class StatusCollector { + public: + StatusCollector() = default; + virtual ~StatusCollector() = default; + + // Gathers status information and calls the passed response callback. + virtual void GetStatusAsync(const StatusCollectorCallback& callback) = 0; + + // Called after the status information has successfully been submitted to + // the server. + virtual void OnSubmittedSuccessfully() = 0; + + // Methods used to decide whether a specific categories of data should be + // included in the reports or not. See: + // https://cs.chromium.org/search/?q=AddDeviceReportingInfo + virtual bool ShouldReportActivityTimes() const = 0; + virtual bool ShouldReportNetworkInterfaces() const = 0; + virtual bool ShouldReportUsers() const = 0; + virtual bool ShouldReportHardwareStatus() const = 0; + + // Returns the DeviceLocalAccount associated with the currently active kiosk + // session, if the session was auto-launched with zero delay (this enables + // functionality such as network reporting). If it isn't a kiosk session, + // nullptr is returned. + virtual std::unique_ptr<DeviceLocalAccount> GetAutoLaunchedKioskSessionInfo(); +}; + +} // namespace policy + +#endif // CHROME_BROWSER_CHROMEOS_POLICY_STATUS_COLLECTOR_STATUS_COLLECTOR_H_
diff --git a/chrome/browser/chromeos/policy/status_uploader.cc b/chrome/browser/chromeos/policy/status_uploader.cc index 3770773f..363b609 100644 --- a/chrome/browser/chromeos/policy/status_uploader.cc +++ b/chrome/browser/chromeos/policy/status_uploader.cc
@@ -15,7 +15,7 @@ #include "base/syslog_logging.h" #include "base/system/sys_info.h" #include "chrome/browser/chromeos/policy/device_local_account.h" -#include "chrome/browser/chromeos/policy/device_status_collector.h" +#include "chrome/browser/chromeos/policy/status_collector/status_collector.h" #include "chromeos/settings/cros_settings_names.h" #include "chromeos/settings/cros_settings_provider.h" #include "components/policy/core/common/cloud/cloud_policy_client.h" @@ -41,7 +41,7 @@ StatusUploader::StatusUploader( CloudPolicyClient* client, - std::unique_ptr<DeviceStatusCollector> collector, + std::unique_ptr<StatusCollector> collector, const scoped_refptr<base::SequencedTaskRunner>& task_runner, base::TimeDelta default_upload_frequency) : client_(client), @@ -188,16 +188,15 @@ void StatusUploader::UploadStatus() { status_upload_in_progress_ = true; // Gather status in the background. - collector_->GetDeviceAndSessionStatusAsync(base::Bind( - &StatusUploader::OnStatusReceived, weak_factory_.GetWeakPtr())); + collector_->GetStatusAsync(base::Bind(&StatusUploader::OnStatusReceived, + weak_factory_.GetWeakPtr())); } -void StatusUploader::OnStatusReceived( - std::unique_ptr<em::DeviceStatusReportRequest> device_status, - std::unique_ptr<em::SessionStatusReportRequest> session_status) { - bool have_device_status = device_status != nullptr; - bool have_session_status = session_status != nullptr; - if (!have_device_status && !have_session_status) { +void StatusUploader::OnStatusReceived(StatusCollectorParams callback_params) { + bool has_device_status = callback_params.device_status != nullptr; + bool has_session_status = callback_params.session_status != nullptr; + bool has_child_status = callback_params.child_status != nullptr; + if (!has_device_status && !has_session_status && !has_child_status) { SYSLOG(INFO) << "Skipping status upload because no data to upload"; // Don't have any status to upload - just set our timer for next time. last_upload_ = base::Time::NowFromSystemTime(); @@ -215,9 +214,11 @@ return; } - SYSLOG(INFO) << "Starting status upload: have_device_status = " - << have_device_status; - client_->UploadDeviceStatus(device_status.get(), session_status.get(), + SYSLOG(INFO) << "Starting status upload: has_device_status = " + << has_device_status; + client_->UploadDeviceStatus(callback_params.device_status.get(), + callback_params.session_status.get(), + callback_params.child_status.get(), base::Bind(&StatusUploader::OnUploadCompleted, weak_factory_.GetWeakPtr())); }
diff --git a/chrome/browser/chromeos/policy/status_uploader.h b/chrome/browser/chromeos/policy/status_uploader.h index 56f6e68e..9463260 100644 --- a/chrome/browser/chromeos/policy/status_uploader.h +++ b/chrome/browser/chromeos/policy/status_uploader.h
@@ -7,11 +7,13 @@ #include <memory> +#include "base/bind.h" #include "base/cancelable_callback.h" #include "base/macros.h" #include "base/memory/ref_counted.h" #include "base/memory/weak_ptr.h" #include "base/time/time.h" +#include "chrome/browser/chromeos/policy/device_local_account.h" #include "chrome/browser/chromeos/settings/cros_settings.h" #include "chrome/browser/media/webrtc/media_capture_devices_dispatcher.h" #include "components/policy/core/common/cloud/cloud_policy_constants.h" @@ -24,17 +26,18 @@ namespace policy { class CloudPolicyClient; -class DeviceStatusCollector; +class StatusCollector; +struct StatusCollectorParams; // Class responsible for periodically uploading device status from the -// passed DeviceStatusCollector. +// passed StatusCollector. class StatusUploader : public MediaCaptureDevicesDispatcher::Observer { public: // Constructor. |client| must be registered and must stay // valid and registered through the lifetime of this StatusUploader // object. StatusUploader(CloudPolicyClient* client, - std::unique_ptr<DeviceStatusCollector> collector, + std::unique_ptr<StatusCollector> collector, const scoped_refptr<base::SequencedTaskRunner>& task_runner, base::TimeDelta default_upload_frequency); @@ -59,21 +62,15 @@ // Returns false if there is already an ongoing status report. bool ScheduleNextStatusUploadImmediately(); - const DeviceStatusCollector* device_status_collector() const { - return collector_.get(); - } + StatusCollector* status_collector() const { return collector_.get(); } private: // Callback invoked periodically to upload the device status from the - // DeviceStatusCollector. + // StatusCollector. void UploadStatus(); - // Called asynchronously by DeviceStatusCollector when status arrives - void OnStatusReceived( - std::unique_ptr<enterprise_management::DeviceStatusReportRequest> - device_status, - std::unique_ptr<enterprise_management::SessionStatusReportRequest> - session_status); + // Called asynchronously by StatusCollector when status arrives. + void OnStatusReceived(StatusCollectorParams callback_params); // Invoked once a status upload has completed. void OnUploadCompleted(bool success); @@ -90,8 +87,8 @@ // CloudPolicyClient used to issue requests to the server. CloudPolicyClient* client_; - // DeviceStatusCollector that provides status for uploading. - std::unique_ptr<DeviceStatusCollector> collector_; + // StatusCollector that provides status for uploading. + std::unique_ptr<StatusCollector> collector_; // TaskRunner used for scheduling upload tasks. const scoped_refptr<base::SequencedTaskRunner> task_runner_;
diff --git a/chrome/browser/chromeos/policy/status_uploader_unittest.cc b/chrome/browser/chromeos/policy/status_uploader_unittest.cc index 4956928..a1bcf519 100644 --- a/chrome/browser/chromeos/policy/status_uploader_unittest.cc +++ b/chrome/browser/chromeos/policy/status_uploader_unittest.cc
@@ -13,7 +13,7 @@ #include "base/test/test_simple_task_runner.h" #include "base/time/time.h" #include "chrome/browser/chromeos/policy/device_local_account.h" -#include "chrome/browser/chromeos/policy/device_status_collector.h" +#include "chrome/browser/chromeos/policy/status_collector/device_status_collector.h" #include "chrome/browser/chromeos/settings/scoped_testing_cros_settings.h" #include "chrome/browser/chromeos/settings/stub_cros_settings_provider.h" #include "chromeos/dbus/dbus_thread_manager.h" @@ -64,8 +64,7 @@ base::TimeDelta(), /* Day starts at midnight */ true /* is_enterprise_device */) {} - MOCK_METHOD1(GetDeviceAndSessionStatusAsync, - void(const policy::DeviceStatusCollector::StatusCallback&)); + MOCK_METHOD1(GetStatusAsync, void(const policy::StatusCollectorCallback&)); MOCK_METHOD0(OnSubmittedSuccessfully, void()); @@ -112,12 +111,12 @@ // Given a pending task to upload status, runs the task and returns the // callback waiting to get device status / session status. The status upload // task will be blocked until the test code calls that callback. - DeviceStatusCollector::StatusCallback CollectStatusCallback() { + StatusCollectorCallback CollectStatusCallback() { // Running the task should pass a callback into - // GetDeviceAndSessionStatusAsync. We'll grab this callback. + // GetStatusAsync. We'll grab this callback. EXPECT_TRUE(task_runner_->HasPendingTask()); - DeviceStatusCollector::StatusCallback status_callback; - EXPECT_CALL(*collector_ptr_, GetDeviceAndSessionStatusAsync(_)) + StatusCollectorCallback status_callback; + EXPECT_CALL(*collector_ptr_, GetStatusAsync) .WillOnce(SaveArg<0>(&status_callback)); task_runner_->RunPendingTasks(); testing::Mock::VerifyAndClearExpectations(&device_management_service_); @@ -129,22 +128,17 @@ void RunPendingUploadTaskAndCheckNext(const StatusUploader& uploader, base::TimeDelta expected_delay, bool upload_success) { - DeviceStatusCollector::StatusCallback status_callback = - CollectStatusCallback(); + StatusCollectorCallback status_callback = CollectStatusCallback(); // Running the status collected callback should trigger // CloudPolicyClient::UploadDeviceStatus. CloudPolicyClient::StatusCallback callback; - EXPECT_CALL(client_, UploadDeviceStatus(_, _, _)) - .WillOnce(SaveArg<2>(&callback)); + EXPECT_CALL(client_, UploadDeviceStatus).WillOnce(SaveArg<3>(&callback)); // Send some "valid" (read: non-nullptr) device/session data to the // callback in order to simulate valid status data. - std::unique_ptr<em::DeviceStatusReportRequest> device_status = - std::make_unique<em::DeviceStatusReportRequest>(); - std::unique_ptr<em::SessionStatusReportRequest> session_status = - std::make_unique<em::SessionStatusReportRequest>(); - status_callback.Run(std::move(device_status), std::move(session_status)); + StatusCollectorParams status_params; + status_callback.Run(std::move(status_params)); testing::Mock::VerifyAndClearExpectations(&device_management_service_); @@ -180,6 +174,12 @@ EXPECT_GE(next_task, uploader.last_upload() + expected_delay); } + std::unique_ptr<StatusUploader> CreateStatusUploader() { + return std::make_unique<StatusUploader>(&client_, std::move(collector_), + task_runner_, + kDefaultStatusUploadDelay); + } + content::TestBrowserThreadBundle thread_bundle_; scoped_refptr<base::TestSimpleTaskRunner> task_runner_; chromeos::ScopedTestingCrosSettings scoped_testing_cros_settings_; @@ -196,8 +196,7 @@ TEST_F(StatusUploaderTest, BasicTest) { EXPECT_FALSE(task_runner_->HasPendingTask()); - StatusUploader uploader(&client_, std::move(collector_), task_runner_, - kDefaultStatusUploadDelay); + auto uploader = CreateStatusUploader(); EXPECT_EQ(1U, task_runner_->NumPendingTasks()); // On startup, first update should happen in 1 minute. EXPECT_EQ(base::TimeDelta::FromMinutes(1), @@ -210,26 +209,24 @@ scoped_testing_cros_settings_.device_settings()->SetInteger( chromeos::kReportUploadFrequency, new_delay.InMilliseconds()); EXPECT_FALSE(task_runner_->HasPendingTask()); - StatusUploader uploader(&client_, std::move(collector_), task_runner_, - kDefaultStatusUploadDelay); + auto uploader = CreateStatusUploader(); ASSERT_EQ(1U, task_runner_->NumPendingTasks()); // On startup, first update should happen in 1 minute. EXPECT_EQ(base::TimeDelta::FromMinutes(1), task_runner_->NextPendingTaskDelay()); // Second update should use the delay specified in settings. - RunPendingUploadTaskAndCheckNext(uploader, new_delay, + RunPendingUploadTaskAndCheckNext(*uploader, new_delay, true /* upload_success */); } TEST_F(StatusUploaderTest, ResetTimerAfterStatusCollection) { - StatusUploader uploader(&client_, std::move(collector_), task_runner_, - kDefaultStatusUploadDelay); - RunPendingUploadTaskAndCheckNext(uploader, kDefaultStatusUploadDelay, + auto uploader = CreateStatusUploader(); + RunPendingUploadTaskAndCheckNext(*uploader, kDefaultStatusUploadDelay, true /* upload_success */); // Handle this response also, and ensure new task is queued. - RunPendingUploadTaskAndCheckNext(uploader, kDefaultStatusUploadDelay, + RunPendingUploadTaskAndCheckNext(*uploader, kDefaultStatusUploadDelay, true /* upload_success */); // Now that the previous request was satisfied, a task to do the next @@ -238,15 +235,14 @@ } TEST_F(StatusUploaderTest, ResetTimerAfterFailedStatusCollection) { - StatusUploader uploader(&client_, std::move(collector_), task_runner_, - kDefaultStatusUploadDelay); + auto uploader = CreateStatusUploader(); // Running the queued task should pass a callback into - // GetDeviceAndSessionStatusAsync. We'll grab this callback and send nullptrs + // GetStatusAsync. We'll grab this callback and send nullptrs // to it in order to simulate failure to get status. EXPECT_EQ(1U, task_runner_->NumPendingTasks()); - DeviceStatusCollector::StatusCallback status_callback; - EXPECT_CALL(*collector_ptr_, GetDeviceAndSessionStatusAsync(_)) + StatusCollectorCallback status_callback; + EXPECT_CALL(*collector_ptr_, GetStatusAsync) .WillOnce(SaveArg<0>(&status_callback)); task_runner_->RunPendingTasks(); testing::Mock::VerifyAndClearExpectations(&device_management_service_); @@ -254,23 +250,23 @@ // Running the callback should trigger StatusUploader::OnStatusReceived, which // in turn should recognize the failure to get status and queue another status // upload. - std::unique_ptr<em::DeviceStatusReportRequest> invalid_device_status; - std::unique_ptr<em::SessionStatusReportRequest> invalid_session_status; - status_callback.Run(std::move(invalid_device_status), - std::move(invalid_session_status)); + StatusCollectorParams status_params; + status_params.device_status.reset(); + status_params.session_status.reset(); + status_params.child_status.reset(); + status_callback.Run(std::move(status_params)); EXPECT_EQ(1U, task_runner_->NumPendingTasks()); // Check the delay of the queued upload - CheckPendingTaskDelay(uploader, kDefaultStatusUploadDelay, + CheckPendingTaskDelay(*uploader, kDefaultStatusUploadDelay, task_runner_->NextPendingTaskDelay()); } TEST_F(StatusUploaderTest, ResetTimerAfterUploadError) { - StatusUploader uploader(&client_, std::move(collector_), task_runner_, - kDefaultStatusUploadDelay); + auto uploader = CreateStatusUploader(); // Simulate upload error - RunPendingUploadTaskAndCheckNext(uploader, kDefaultStatusUploadDelay, + RunPendingUploadTaskAndCheckNext(*uploader, kDefaultStatusUploadDelay, false /* upload_success */); // Now that the previous request was satisfied, a task to do the next @@ -279,78 +275,69 @@ } TEST_F(StatusUploaderTest, ResetTimerAfterUnregisteredClient) { - StatusUploader uploader(&client_, std::move(collector_), task_runner_, - kDefaultStatusUploadDelay); + auto uploader = CreateStatusUploader(); client_.SetDMToken(""); EXPECT_FALSE(client_.is_registered()); - DeviceStatusCollector::StatusCallback status_callback = - CollectStatusCallback(); + StatusCollectorCallback status_callback = CollectStatusCallback(); // Make sure no status upload is queued up yet (since an upload is in // progress). EXPECT_FALSE(task_runner_->HasPendingTask()); // StatusUploader should not try to upload using an unregistered client - EXPECT_CALL(client_, UploadDeviceStatus(_, _, _)).Times(0); - std::unique_ptr<em::DeviceStatusReportRequest> device_status = - std::make_unique<em::DeviceStatusReportRequest>(); - std::unique_ptr<em::SessionStatusReportRequest> session_status = - std::make_unique<em::SessionStatusReportRequest>(); - status_callback.Run(std::move(device_status), std::move(session_status)); + EXPECT_CALL(client_, UploadDeviceStatus).Times(0); + StatusCollectorParams status_params; + status_callback.Run(std::move(status_params)); // A task to try again should be queued. ASSERT_EQ(1U, task_runner_->NumPendingTasks()); - CheckPendingTaskDelay(uploader, kDefaultStatusUploadDelay, + CheckPendingTaskDelay(*uploader, kDefaultStatusUploadDelay, task_runner_->NextPendingTaskDelay()); } TEST_F(StatusUploaderTest, ChangeFrequency) { - StatusUploader uploader(&client_, std::move(collector_), task_runner_, - kDefaultStatusUploadDelay); + auto uploader = CreateStatusUploader(); // Change the frequency. The new frequency should be reflected in the timing // used for the next callback. const base::TimeDelta new_delay = kDefaultStatusUploadDelay * 2; scoped_testing_cros_settings_.device_settings()->SetInteger( chromeos::kReportUploadFrequency, new_delay.InMilliseconds()); - RunPendingUploadTaskAndCheckNext(uploader, new_delay, + RunPendingUploadTaskAndCheckNext(*uploader, new_delay, true /* upload_success */); } TEST_F(StatusUploaderTest, NoUploadAfterUserInput) { - StatusUploader uploader(&client_, std::move(collector_), task_runner_, - kDefaultStatusUploadDelay); + auto uploader = CreateStatusUploader(); // Should allow data upload before there is user input. - EXPECT_TRUE(uploader.IsSessionDataUploadAllowed()); + EXPECT_TRUE(uploader->IsSessionDataUploadAllowed()); // Now mock user input, and no session data should be allowed. ui::MouseEvent e(ui::ET_MOUSE_PRESSED, gfx::Point(), gfx::Point(), ui::EventTimeForNow(), 0, 0); const ui::PlatformEvent& native_event = &e; ui::UserActivityDetector::Get()->DidProcessEvent(native_event); - EXPECT_FALSE(uploader.IsSessionDataUploadAllowed()); + EXPECT_FALSE(uploader->IsSessionDataUploadAllowed()); } TEST_F(StatusUploaderTest, NoUploadAfterVideoCapture) { - StatusUploader uploader(&client_, std::move(collector_), task_runner_, - kDefaultStatusUploadDelay); + auto uploader = CreateStatusUploader(); // Should allow data upload before there is video capture. - EXPECT_TRUE(uploader.IsSessionDataUploadAllowed()); + EXPECT_TRUE(uploader->IsSessionDataUploadAllowed()); // Now mock video capture, and no session data should be allowed. MediaCaptureDevicesDispatcher::GetInstance()->OnMediaRequestStateChanged( 0, 0, 0, GURL("http://www.google.com"), blink::MEDIA_DEVICE_VIDEO_CAPTURE, content::MEDIA_REQUEST_STATE_OPENING); base::RunLoop().RunUntilIdle(); - EXPECT_FALSE(uploader.IsSessionDataUploadAllowed()); + EXPECT_FALSE(uploader->IsSessionDataUploadAllowed()); } TEST_F(StatusUploaderTest, ScheduleImmediateStatusUpload) { EXPECT_FALSE(task_runner_->HasPendingTask()); - StatusUploader uploader(&client_, std::move(collector_), task_runner_, - kDefaultStatusUploadDelay); + auto uploader = CreateStatusUploader(); EXPECT_EQ(1U, task_runner_->NumPendingTasks()); // On startup, first update should happen in 1 minute. @@ -358,16 +345,15 @@ task_runner_->NextPendingTaskDelay()); // Schedule an immediate status upload. - uploader.ScheduleNextStatusUploadImmediately(); + uploader->ScheduleNextStatusUploadImmediately(); EXPECT_EQ(2U, task_runner_->NumPendingTasks()); - CheckPendingTaskDelay(uploader, base::TimeDelta(), + CheckPendingTaskDelay(*uploader, base::TimeDelta(), task_runner_->FinalPendingTaskDelay()); } TEST_F(StatusUploaderTest, ScheduleImmediateStatusUploadConsecutively) { EXPECT_FALSE(task_runner_->HasPendingTask()); - StatusUploader uploader(&client_, std::move(collector_), task_runner_, - kDefaultStatusUploadDelay); + auto uploader = CreateStatusUploader(); EXPECT_EQ(1U, task_runner_->NumPendingTasks()); // On startup, first update should happen in 1 minute. @@ -375,15 +361,15 @@ task_runner_->NextPendingTaskDelay()); // Schedule an immediate status upload and run it. - uploader.ScheduleNextStatusUploadImmediately(); - RunPendingUploadTaskAndCheckNext(uploader, kDefaultStatusUploadDelay, + uploader->ScheduleNextStatusUploadImmediately(); + RunPendingUploadTaskAndCheckNext(*uploader, kDefaultStatusUploadDelay, true /* upload_success */); // Schedule the next one and check that it was scheduled after // kMinImmediateUploadInterval of the last upload. - uploader.ScheduleNextStatusUploadImmediately(); + uploader->ScheduleNextStatusUploadImmediately(); EXPECT_EQ(2U, task_runner_->NumPendingTasks()); - CheckPendingTaskDelay(uploader, kMinImmediateUploadInterval, + CheckPendingTaskDelay(*uploader, kMinImmediateUploadInterval, task_runner_->FinalPendingTaskDelay()); }
diff --git a/chrome/browser/chromeos/startup_settings_cache.cc b/chrome/browser/chromeos/startup_settings_cache.cc new file mode 100644 index 0000000..bd29d71 --- /dev/null +++ b/chrome/browser/chromeos/startup_settings_cache.cc
@@ -0,0 +1,78 @@ +// Copyright 2019 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 "chrome/browser/chromeos/startup_settings_cache.h" + +#include "base/files/file_path.h" +#include "base/files/file_util.h" +#include "base/json/json_reader.h" +#include "base/json/json_writer.h" +#include "base/logging.h" +#include "base/path_service.h" +#include "base/values.h" +#include "chrome/common/chrome_paths.h" + +namespace chromeos { +namespace startup_settings_cache { +namespace { + +// Name of the cache file on disk. +const char kCacheFilename[] = "startup_settings_cache.json"; + +// JSON dictionary key for application locale (e.g. "ja" or "en_GB"). +const char kAppLocaleKey[] = "app_locale"; + +bool GetCacheFilePath(base::FilePath* path) { + base::FilePath user_data_dir; + if (!base::PathService::Get(chrome::DIR_USER_DATA, &user_data_dir)) + return false; + + *path = user_data_dir.Append(kCacheFilename); + return true; +} + +} // namespace + +std::string ReadAppLocale() { + base::FilePath cache_file; + if (!GetCacheFilePath(&cache_file)) + return std::string(); + + std::string input; + if (!base::ReadFileToString(cache_file, &input)) + return std::string(); + + base::Optional<base::Value> settings = base::JSONReader::Read(input); + if (!settings.has_value()) + return std::string(); + + base::Value* app_locale_setting = settings->FindKey(kAppLocaleKey); + if (!app_locale_setting) + return std::string(); + + std::string app_locale; + app_locale_setting->GetAsString(&app_locale); + // The locale is already an "actual locale", so this does not need to call + // language::ConvertToActualUILocale(). + return app_locale; +} + +void WriteAppLocale(std::string app_locale) { + base::FilePath cache_file; + if (!GetCacheFilePath(&cache_file)) + return; + + base::Value settings(base::Value::Type::DICTIONARY); + settings.SetKey(kAppLocaleKey, base::Value(app_locale)); + + std::string output; + if (!base::JSONWriter::Write(settings, &output)) + return; + + // Ignore errors because we're shutting down and we can't recover. + base::WriteFile(cache_file, output.data(), static_cast<int>(output.size())); +} + +} // namespace startup_settings_cache +} // namespace chromeos
diff --git a/chrome/browser/chromeos/startup_settings_cache.h b/chrome/browser/chromeos/startup_settings_cache.h new file mode 100644 index 0000000..5954337 --- /dev/null +++ b/chrome/browser/chromeos/startup_settings_cache.h
@@ -0,0 +1,32 @@ +// Copyright 2019 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 CHROME_BROWSER_CHROMEOS_STARTUP_SETTINGS_CACHE_H_ +#define CHROME_BROWSER_CHROMEOS_STARTUP_SETTINGS_CACHE_H_ + +#include <string> + +namespace chromeos { +namespace startup_settings_cache { + +// On Chrome OS, the application locale is stored in local state prefs. The +// zygote needs the locale so it can load the correct resource bundle and +// provide localized strings to renderers. However, the zygote forks and engages +// the sandbox before the browser loads local state. +// +// Instead, cache the locale in a separate JSON file and read it on zygote +// startup. The additional disk read on startup is unfortunately, but it's only +// ~20 bytes and this approach performs better than other approaches (passing a +// resource bundle file descriptor to zygote on renderer fork, or pre-load the +// local state file on startup). On coral (dual core Celeron N3350 1.1 GHz) the +// file read takes < 2.5 ms and the write takes < 1 ms. https://crbug.com/510455 +std::string ReadAppLocale(); + +// Writes the locale string to a JSON file on disk. See above. +void WriteAppLocale(std::string app_locale); + +} // namespace startup_settings_cache +} // namespace chromeos + +#endif // CHROME_BROWSER_CHROMEOS_STARTUP_SETTINGS_CACHE_H_
diff --git a/chrome/browser/chromeos/startup_settings_cache_unittest.cc b/chrome/browser/chromeos/startup_settings_cache_unittest.cc new file mode 100644 index 0000000..716491e7 --- /dev/null +++ b/chrome/browser/chromeos/startup_settings_cache_unittest.cc
@@ -0,0 +1,32 @@ +// Copyright 2019 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 "chrome/browser/chromeos/startup_settings_cache.h" + +#include "base/macros.h" +#include "base/path_service.h" +#include "base/test/scoped_path_override.h" +#include "chrome/common/chrome_paths.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace chromeos { + +class StartupSettingsCacheTest : public testing::Test { + protected: + StartupSettingsCacheTest() : user_data_dir_override_(chrome::DIR_USER_DATA) {} + ~StartupSettingsCacheTest() override {} + + private: + // Map DIR_USER_DATA to a temp dir. + base::ScopedPathOverride user_data_dir_override_; + + DISALLOW_COPY_AND_ASSIGN(StartupSettingsCacheTest); +}; + +TEST_F(StartupSettingsCacheTest, RoundTrip) { + startup_settings_cache::WriteAppLocale("foo"); + EXPECT_EQ("foo", startup_settings_cache::ReadAppLocale()); +} + +} // namespace chromeos
diff --git a/chrome/browser/feedback/system_logs/log_sources/crash_ids_source.cc b/chrome/browser/feedback/system_logs/log_sources/crash_ids_source.cc index 32204cdbd..824a1e6 100644 --- a/chrome/browser/feedback/system_logs/log_sources/crash_ids_source.cc +++ b/chrome/browser/feedback/system_logs/log_sources/crash_ids_source.cc
@@ -21,9 +21,12 @@ // The length of the crash ID string. constexpr size_t kCrashIdStringSize = 16; -// We are only interested in crashes that took place within the last hour. +// For recent crashes, which is for all reports, look back one hour. constexpr base::TimeDelta kOneHourTimeDelta = base::TimeDelta::FromHours(1); +// For all crashes, which is for only @google.com reports, look back 120 days. +constexpr base::TimeDelta k120DaysTimeDelta = base::TimeDelta::FromDays(120); + } // namespace CrashIdsSource::CrashIdsSource() @@ -50,13 +53,19 @@ void CrashIdsSource::OnUploadListAvailable() { pending_crash_list_loading_ = false; - // Only get the IDs of crashes that occurred within the last hour (if any). + // We generate two lists of crash IDs. One will be the crashes within the last + // hour, which is included in all feedback reports. The other is all of the + // crash IDs from the past 120 days, which is only included in feedback + // reports sent from @google.com accounts. std::vector<UploadList::UploadInfo> crashes; crash_upload_list_->GetUploads(kMaxCrashesCountToRetrieve, &crashes); const base::Time now = base::Time::Now(); crash_ids_list_.clear(); crash_ids_list_.reserve(kMaxCrashesCountToRetrieve * (kCrashIdStringSize + 2)); + all_crash_ids_list_.clear(); + all_crash_ids_list_.reserve(kMaxCrashesCountToRetrieve * + (kCrashIdStringSize + 2)); // The feedback server expects the crash IDs to be a comma-separated list. for (const auto& crash_info : crashes) { @@ -64,10 +73,15 @@ crash_info.state == UploadList::UploadInfo::State::Uploaded ? crash_info.upload_time : crash_info.capture_time; - if (now - report_time < kOneHourTimeDelta) { + base::TimeDelta time_diff = now - report_time; + if (time_diff < k120DaysTimeDelta) { const std::string& crash_id = crash_info.upload_id; - crash_ids_list_.append(crash_ids_list_.empty() ? crash_id - : ", " + crash_id); + all_crash_ids_list_.append(all_crash_ids_list_.empty() ? crash_id + : ", " + crash_id); + if (time_diff < kOneHourTimeDelta) { + crash_ids_list_.append(crash_ids_list_.empty() ? crash_id + : ", " + crash_id); + } } } @@ -77,9 +91,11 @@ pending_requests_.clear(); } -void CrashIdsSource::RespondWithCrashIds(SysLogsSourceCallback callback) const { +void CrashIdsSource::RespondWithCrashIds(SysLogsSourceCallback callback) { auto response = std::make_unique<SystemLogsResponse>(); (*response)[feedback::FeedbackReport::kCrashReportIdsKey] = crash_ids_list_; + (*response)[feedback::FeedbackReport::kAllCrashReportIdsKey] = + all_crash_ids_list_; // We must respond anyways. std::move(callback).Run(std::move(response));
diff --git a/chrome/browser/feedback/system_logs/log_sources/crash_ids_source.h b/chrome/browser/feedback/system_logs/log_sources/crash_ids_source.h index 8da0956..e8e4166 100644 --- a/chrome/browser/feedback/system_logs/log_sources/crash_ids_source.h +++ b/chrome/browser/feedback/system_logs/log_sources/crash_ids_source.h
@@ -24,12 +24,14 @@ private: void OnUploadListAvailable(); - void RespondWithCrashIds(SysLogsSourceCallback callback) const; + void RespondWithCrashIds(SysLogsSourceCallback callback); scoped_refptr<UploadList> crash_upload_list_; - // A comma-separated list of crash IDs as expected by the server. + // A comma-separated list of crash IDs as expected by the server. The first + // is for the last hour, the second is for the pat 120 days. std::string crash_ids_list_; + std::string all_crash_ids_list_; // Contains any pending fetch requests waiting for the crash upload list to // finish loading.
diff --git a/chrome/browser/flag-metadata.json b/chrome/browser/flag-metadata.json index 6ab8b45..d525b62 100644 --- a/chrome/browser/flag-metadata.json +++ b/chrome/browser/flag-metadata.json
@@ -1749,6 +1749,11 @@ "expiry_milestone": 76 }, { + "name": "enable-tab-engagement-reporting", + "owners": [ "memex-team@google.com" ], + "expiry_milestone": 82 + }, + { "name": "enable-text-fragment-anchor", "owners": [ "bokan", "input-dev" ], "expiry_milestone": 77 @@ -1809,8 +1814,8 @@ "expiry_milestone": 76 }, { - "name": "enable-viz-hit-test-draw-quad", - "owners": [ "riajiang", "rjkroege" ], + "name": "enable-viz-hit-test", + "owners": [ "riajiang", "sunxd", "rjkroege" ], "expiry_milestone": 76 }, {
diff --git a/chrome/browser/flag_descriptions.cc b/chrome/browser/flag_descriptions.cc index d21623d..0eb98eb 100644 --- a/chrome/browser/flag_descriptions.cc +++ b/chrome/browser/flag_descriptions.cc
@@ -800,10 +800,10 @@ "If enabled, the display compositor runs as part of the viz service in the" "GPU process."; -const char kVizHitTestDrawQuadName[] = "Viz Hit-test Draw-quad version"; -const char kVizHitTestDrawQuadDescription[] = +const char kVizHitTestName[] = "Viz Hit-test"; +const char kVizHitTestDescription[] = "If enabled, event targeting uses the new viz-assisted hit-testing logic, " - "with hit-test data computed from the CompositorFrame."; + "with hit-test data computed from the CompositorFrame or the SurfaceLayer."; const char kCompositorThreadedScrollbarScrollingName[] = "Enable compositor threaded scrollbar scrolling"; @@ -1908,6 +1908,10 @@ "keyboard shortcuts and have the events routed directly to the website " "when in fullscreen mode."; +const char kTabEngagementReportingName[] = "Tab Engagement Metrics"; +const char kTabEngagementReportingDescription[] = + "Tracks tab engagement and lifetime metrics."; + const char kTabGridLayoutAndroidName[] = "Tab Grid Layout"; const char kTabGridLayoutAndroidDescription[] = "Allows users to see their tabs in a grid layout in the tab switcher.";
diff --git a/chrome/browser/flag_descriptions.h b/chrome/browser/flag_descriptions.h index ef3b0dc8..7137fbc 100644 --- a/chrome/browser/flag_descriptions.h +++ b/chrome/browser/flag_descriptions.h
@@ -472,8 +472,8 @@ extern const char kVizDisplayCompositorName[]; extern const char kVizDisplayCompositorDescription[]; -extern const char kVizHitTestDrawQuadName[]; -extern const char kVizHitTestDrawQuadDescription[]; +extern const char kVizHitTestName[]; +extern const char kVizHitTestDescription[]; extern const char kMemlogName[]; extern const char kMemlogDescription[]; @@ -1134,6 +1134,9 @@ extern const char kSyncUSSAutofillWalletMetadataName[]; extern const char kSyncUSSAutofillWalletMetadataDescription[]; +extern const char kTabEngagementReportingName[]; +extern const char kTabEngagementReportingDescription[]; + extern const char kTabGridLayoutAndroidName[]; extern const char kTabGridLayoutAndroidDescription[];
diff --git a/chrome/browser/metrics/perf/heap_collector.cc b/chrome/browser/metrics/perf/heap_collector.cc index 72b942db..b0a07b3 100644 --- a/chrome/browser/metrics/perf/heap_collector.cc +++ b/chrome/browser/metrics/perf/heap_collector.cc
@@ -171,7 +171,7 @@ // The devices major / minor values and the inode are not filled by // ParseProcMaps, so write them as zero values. They are not relevant for // symbolization. - std::string row = base::StringPrintf("%08" PRIx64 "-%08" PRIx64 + std::string row = base::StringPrintf("%08" PRIxPTR "-%08" PRIxPTR " %c%c%c%c %08llx 00:00 0 %s\n", region.start, region.end, r, w, x, p, region.offset, region.path.c_str());
diff --git a/chrome/browser/metrics/process_memory_metrics_emitter.cc b/chrome/browser/metrics/process_memory_metrics_emitter.cc index d83d9a78c..9b53109 100644 --- a/chrome/browser/metrics/process_memory_metrics_emitter.cc +++ b/chrome/browser/metrics/process_memory_metrics_emitter.cc
@@ -764,7 +764,7 @@ process_info.launch_time = process_node->launch_time(); base::flat_set<performance_manager::PageNodeImpl*> page_nodes = - process_node->GetAssociatedPageCoordinationUnits(); + process_node->GetAssociatedPageNodes(); for (performance_manager::PageNodeImpl* page_node : page_nodes) { if (page_node->ukm_source_id() == ukm::kInvalidSourceId) continue;
diff --git a/chrome/browser/pdf/pdf_extension_test.cc b/chrome/browser/pdf/pdf_extension_test.cc index f604984..3b8f6ce 100644 --- a/chrome/browser/pdf/pdf_extension_test.cc +++ b/chrome/browser/pdf/pdf_extension_test.cc
@@ -399,9 +399,11 @@ void SetUpCommandLine(base::CommandLine* command_line) override { PDFExtensionTest::SetUpCommandLine(command_line); if (GetParam()) { - feature_list_.InitAndEnableFeature(features::kEnableVizHitTestDrawQuad); + std::map<std::string, std::string> parameters{{"provider", "draw_quad"}}; + feature_list_.InitAndEnableFeatureWithParameters( + features::kEnableVizHitTest, parameters); } else { - feature_list_.InitAndDisableFeature(features::kEnableVizHitTestDrawQuad); + feature_list_.InitAndDisableFeature(features::kEnableVizHitTest); } }
diff --git a/chrome/browser/performance_manager/decorators/frozen_frame_aggregator.cc b/chrome/browser/performance_manager/decorators/frozen_frame_aggregator.cc new file mode 100644 index 0000000..2ba4c093 --- /dev/null +++ b/chrome/browser/performance_manager/decorators/frozen_frame_aggregator.cc
@@ -0,0 +1,223 @@ +// Copyright 2019 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 "chrome/browser/performance_manager/decorators/frozen_frame_aggregator.h" + +#include "chrome/browser/performance_manager/graph/frame_node_impl.h" +#include "chrome/browser/performance_manager/graph/node_attached_data_impl.h" +#include "chrome/browser/performance_manager/graph/page_node_impl.h" +#include "chrome/browser/performance_manager/graph/process_node_impl.h" + +namespace performance_manager { + +using LifecycleState = resource_coordinator::mojom::LifecycleState; + +// Provides FrozenFrameAggregator machinery access to some internals of a +// PageNodeImpl and ProcessNodeImpl. +class FrozenFrameAggregatorAccess { + public: + using StorageType = decltype(PageNodeImpl::frozen_frame_data_); + + static StorageType* GetInternalStorage(PageNodeImpl* page_node) { + return &page_node->frozen_frame_data_; + } + + static StorageType* GetInternalStorage(ProcessNodeImpl* process_node) { + return &process_node->frozen_frame_data_; + } + + static void SetLifecycleState(PageNodeImpl* page_node, + LifecycleState lifecycle_state) { + page_node->SetLifecycleState(lifecycle_state); + } + + static void NotifyAllFramesInProcessFrozen(ProcessNodeImpl* process_node) { + for (auto& observer : process_node->observers()) + observer.OnAllFramesInProcessFrozen(process_node); + } +}; + +namespace { + +// Private implementation of the node attached data. This keeps the complexity +// out of the header file. +class FrozenDataImpl : public FrozenFrameAggregator::Data, + public NodeAttachedDataImpl<FrozenDataImpl> { + public: + using StorageType = FrozenFrameAggregatorAccess::StorageType; + + // This data is tracked persistently for page and process nodes, so uses + // internal node storage. + struct Traits : public NodeAttachedDataInternalOnNodeType<PageNodeImpl>, + public NodeAttachedDataInternalOnNodeType<ProcessNodeImpl> {}; + + FrozenDataImpl() = default; + ~FrozenDataImpl() override = default; + + static StorageType* GetInternalStorage(PageNodeImpl* page_node) { + return FrozenFrameAggregatorAccess::GetInternalStorage(page_node); + } + + static StorageType* GetInternalStorage(ProcessNodeImpl* process_node) { + return FrozenFrameAggregatorAccess::GetInternalStorage(process_node); + } + + // Returns the current "is_frozen" state. A collection of frames is considered + // frozen if its non-empty, and all of the frames are frozen. + bool IsFrozen() const { + return current_frame_count > 0 && frozen_frame_count == current_frame_count; + } + + // Returns the state as an equivalent LifecycleState. + LifecycleState AsLifecycleState() const { + if (IsFrozen()) + return LifecycleState::kFrozen; + return LifecycleState::kRunning; + } + + // Applies a change to frame counts. Returns true if that causes the frozen + // state to change for this object. + bool ChangeFrameCounts(int32_t current_frame_delta, + int32_t frozen_frame_delta) { + DCHECK(current_frame_delta != 0 || frozen_frame_delta != 0); + DCHECK_GE(1, abs(current_frame_delta)); + DCHECK_GE(1, abs(frozen_frame_delta)); + // We should never have (-1, 1) or (1, -1). + DCHECK_NE(-current_frame_delta, frozen_frame_delta); + + // If the deltas are negative, the counts need to be positive. + DCHECK(current_frame_delta >= 0 || current_frame_count > 0); + DCHECK(frozen_frame_delta >= 0 || frozen_frame_count > 0); + + bool was_frozen = IsFrozen(); + current_frame_count += current_frame_delta; + frozen_frame_count += frozen_frame_delta; + + return IsFrozen() != was_frozen; + } + + private: + DISALLOW_COPY_AND_ASSIGN(FrozenDataImpl); +}; + +bool IsFrozen(const FrameNodeImpl* frame_node) { + return frame_node->lifecycle_state() == LifecycleState::kFrozen; +} + +} // namespace + +FrozenFrameAggregator::FrozenFrameAggregator() = default; +FrozenFrameAggregator::~FrozenFrameAggregator() = default; + +bool FrozenFrameAggregator::ShouldObserve(const NodeBase* node) { + // Use the ShouldObserve hook to ensure page and process node attached data + // is initialized. There's no need to observe these nodes beyond that. + switch (node->id().type) { + case resource_coordinator::CoordinationUnitType::kFrame: + return true; + + case resource_coordinator::CoordinationUnitType::kPage: { + auto* page_node = PageNodeImpl::FromNodeBase(node); + // Expect a page to always start in the running state. + DCHECK_EQ(LifecycleState::kRunning, page_node->lifecycle_state()); + FrozenDataImpl::GetOrCreate(page_node); + return false; + } + + case resource_coordinator::CoordinationUnitType::kProcess: { + FrozenDataImpl::GetOrCreate(ProcessNodeImpl::FromNodeBase(node)); + return false; + } + + default: + return false; + } + NOTREACHED(); +} + +void FrozenFrameAggregator::OnNodeAdded(NodeBase* node) { + // We only observe frame nodes. + DCHECK_EQ(resource_coordinator::CoordinationUnitType::kFrame, + node->id().type); + + auto* frame_node = FrameNodeImpl::FromNodeBase(node); + DCHECK(!IsFrozen(frame_node)); // A newly created node can never be frozen. + AddOrRemoveFrame(frame_node, 1); +} + +void FrozenFrameAggregator::OnBeforeNodeRemoved(NodeBase* node) { + if (node->id().type != resource_coordinator::CoordinationUnitType::kFrame) + return; + + auto* frame_node = FrameNodeImpl::FromNodeBase(node); + AddOrRemoveFrame(frame_node, -1); +} + +void FrozenFrameAggregator::OnIsCurrentChanged(FrameNodeImpl* frame_node) { + int32_t current_frame_delta = frame_node->is_current() ? 1 : -1; + int32_t frozen_frame_delta = IsFrozen(frame_node) ? current_frame_delta : 0; + UpdateFrameCounts(frame_node, current_frame_delta, frozen_frame_delta); +} + +void FrozenFrameAggregator::OnLifecycleStateChanged(FrameNodeImpl* frame_node) { + if (!frame_node->is_current()) + return; + int32_t frozen_frame_delta = IsFrozen(frame_node) ? 1 : -1; + UpdateFrameCounts(frame_node, 0, frozen_frame_delta); +} + +void FrozenFrameAggregator::AddOrRemoveFrame(FrameNodeImpl* frame_node, + int32_t delta) { + int32_t current_frame_delta = 0; + int32_t frozen_frame_delta = 0; + if (frame_node->is_current()) { + current_frame_delta = delta; + if (IsFrozen(frame_node)) + frozen_frame_delta = delta; + } + + UpdateFrameCounts(frame_node, current_frame_delta, frozen_frame_delta); +} + +void FrozenFrameAggregator::UpdateFrameCounts(FrameNodeImpl* frame_node, + int32_t current_frame_delta, + int32_t frozen_frame_delta) { + // If a non-current frame is added or removed the deltas can be zero. In this + // case the logic can be aborted early to save some effort. + if (current_frame_delta == 0 && frozen_frame_delta == 0) + return; + + auto* page_node = frame_node->page_node(); + auto* process_node = frame_node->process_node(); + auto* page_data = FrozenDataImpl::Get(page_node); + auto* process_data = FrozenDataImpl::Get(process_node); + + // Set the page lifecycle state based on the state of the frame tree. + if (page_data->ChangeFrameCounts(current_frame_delta, frozen_frame_delta)) { + FrozenFrameAggregatorAccess::SetLifecycleState( + page_node, page_data->AsLifecycleState()); + } + + // Update the process state, and notify when all frames in the tree are + // frozen. + if (process_data->ChangeFrameCounts(current_frame_delta, + frozen_frame_delta) && + process_data->IsFrozen()) { + FrozenFrameAggregatorAccess::NotifyAllFramesInProcessFrozen(process_node); + } +} + +// static +FrozenFrameAggregator::Data* FrozenFrameAggregator::Data::GetForTesting( + PageNodeImpl* page_node) { + return FrozenDataImpl::Get(page_node); +} + +// static +FrozenFrameAggregator::Data* FrozenFrameAggregator::Data::GetForTesting( + ProcessNodeImpl* process_node) { + return FrozenDataImpl::Get(process_node); +} + +} // namespace performance_manager
diff --git a/chrome/browser/performance_manager/decorators/frozen_frame_aggregator.h b/chrome/browser/performance_manager/decorators/frozen_frame_aggregator.h new file mode 100644 index 0000000..84d692e --- /dev/null +++ b/chrome/browser/performance_manager/decorators/frozen_frame_aggregator.h
@@ -0,0 +1,69 @@ +// Copyright 2019 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 CHROME_BROWSER_PERFORMANCE_MANAGER_DECORATORS_FROZEN_FRAME_AGGREGATOR_H_ +#define CHROME_BROWSER_PERFORMANCE_MANAGER_DECORATORS_FROZEN_FRAME_AGGREGATOR_H_ + +#include "chrome/browser/performance_manager/observers/graph_observer.h" + +namespace performance_manager { + +class NodeBase; +class FrameNodeImpl; +class PageNodeImpl; +class ProcessNodeImpl; + +// The FrozenFrameAggregator is responsible for tracking frame frozen states, +// and aggregating this property to the page and process nodes. +class FrozenFrameAggregator : public GraphObserver { + public: + struct Data; + + // TODO(chrisha): Check that the graph is empty when this observer is added! + // https://www.crbug.com/952891 + FrozenFrameAggregator(); + ~FrozenFrameAggregator() override; + + // GraphObserver implementation: + bool ShouldObserve(const NodeBase* node) override; + void OnNodeAdded(NodeBase* node) override; + void OnBeforeNodeRemoved(NodeBase* node) override; + void OnIsCurrentChanged(FrameNodeImpl* frame_node) override; + void OnLifecycleStateChanged(FrameNodeImpl* page_node) override; + + protected: + friend class FrozenFrameAggregatorTest; + + // Used to update counts when adding or removing a |frame_node|. A |delta| of + // -1 indicates a removal, while +1 indicates adding. + void AddOrRemoveFrame(FrameNodeImpl* frame_node, int32_t delta); + + // Updates the frame counts associated with the given |frame_node|. Takes + // care of updating page and process state, as well as firing any needed + // notifications. + void UpdateFrameCounts(FrameNodeImpl* frame_node, + int32_t current_frame_delta, + int32_t frozen_frame_delta); + + private: + DISALLOW_COPY_AND_ASSIGN(FrozenFrameAggregator); +}; + +// This struct is stored internally on page and process nodes using +// InlineNodeAttachedDataStorage. +struct FrozenFrameAggregator::Data { + // The number of current frames associated with a given page/process. + uint32_t current_frame_count = 0; + + // The number of frozen current frames associated with a given page/process. + // This is always <= |current_frame_count|. + uint32_t frozen_frame_count = 0; + + static Data* GetForTesting(PageNodeImpl* page_node); + static Data* GetForTesting(ProcessNodeImpl* process_node); +}; + +} // namespace performance_manager + +#endif // CHROME_BROWSER_PERFORMANCE_MANAGER_DECORATORS_FROZEN_FRAME_AGGREGATOR_H_
diff --git a/chrome/browser/performance_manager/decorators/frozen_frame_aggregator_unittest.cc b/chrome/browser/performance_manager/decorators/frozen_frame_aggregator_unittest.cc new file mode 100644 index 0000000..44efe502 --- /dev/null +++ b/chrome/browser/performance_manager/decorators/frozen_frame_aggregator_unittest.cc
@@ -0,0 +1,279 @@ +// Copyright 2019 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 "chrome/browser/performance_manager/decorators/frozen_frame_aggregator.h" + +#include <memory> + +#include "chrome/browser/performance_manager/graph/frame_node_impl.h" +#include "chrome/browser/performance_manager/graph/graph_test_harness.h" +#include "chrome/browser/performance_manager/graph/page_node_impl.h" +#include "chrome/browser/performance_manager/graph/process_node_impl.h" +#include "chrome/browser/performance_manager/observers/graph_observer.h" +#include "testing/gmock/include/gmock/gmock.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace performance_manager { + +namespace { + +using LifecycleState = PageNodeImpl::LifecycleState; + +class LenientMockGraphObserver : public GraphObserver { + public: + LenientMockGraphObserver() = default; + ~LenientMockGraphObserver() override = default; + + virtual bool ShouldObserve(const NodeBase* node) { return false; } + + MOCK_METHOD1(OnAllFramesInProcessFrozen, void(ProcessNodeImpl*)); + + private: + DISALLOW_COPY_AND_ASSIGN(LenientMockGraphObserver); +}; + +using MockGraphObserver = ::testing::StrictMock<LenientMockGraphObserver>; + +} // namespace + +class FrozenFrameAggregatorTest : public GraphTestHarness { + protected: + FrozenFrameAggregatorTest() = default; + ~FrozenFrameAggregatorTest() override = default; + + void SetUp() override { + ffa_ = std::make_unique<FrozenFrameAggregator>(); + graph()->RegisterObserver(ffa_.get()); + process_node_ = CreateNode<ProcessNodeImpl>(); + page_node_ = CreateNode<PageNodeImpl>(nullptr); + } + + void TearDown() override { graph()->UnregisterObserver(ffa_.get()); } + + template <typename NodeType> + void ExpectData(NodeType* node, + uint32_t current_frame_count, + uint32_t frozen_frame_count) { + auto* data = FrozenFrameAggregator::Data::GetForTesting(node); + EXPECT_TRUE(data); + EXPECT_EQ(current_frame_count, data->current_frame_count); + EXPECT_EQ(frozen_frame_count, data->frozen_frame_count); + } + + void ExpectPageData(uint32_t current_frame_count, + uint32_t frozen_frame_count) { + ExpectData(page_node_.get(), current_frame_count, frozen_frame_count); + } + + void ExpectProcessData(uint32_t current_frame_count, + uint32_t frozen_frame_count) { + ExpectData(process_node_.get(), current_frame_count, frozen_frame_count); + } + + void ExpectRunning() { + EXPECT_EQ(LifecycleState::kRunning, page_node_.get()->lifecycle_state()); + } + + void ExpectFrozen() { + EXPECT_EQ(LifecycleState::kFrozen, page_node_.get()->lifecycle_state()); + } + + TestNodeWrapper<FrameNodeImpl> CreateFrame(FrameNodeImpl* parent_frame_node, + int frame_tree_node_id) { + return CreateNode<FrameNodeImpl>(process_node_.get(), page_node_.get(), + parent_frame_node, frame_tree_node_id); + } + + std::unique_ptr<FrozenFrameAggregator> ffa_; + TestNodeWrapper<ProcessNodeImpl> process_node_; + TestNodeWrapper<PageNodeImpl> page_node_; + + private: + DISALLOW_COPY_AND_ASSIGN(FrozenFrameAggregatorTest); +}; + +TEST_F(FrozenFrameAggregatorTest, ProcessAggregation) { + // Explicitly add the observer to only the process node. + MockGraphObserver obs; + process_node_.get()->AddObserver(&obs); + + ExpectProcessData(0, 0); + + // Add a main frame. + auto f0 = CreateFrame(nullptr, 0); + ExpectProcessData(0, 0); + + // Make the frame current. + f0.get()->SetIsCurrent(true); + ExpectProcessData(1, 0); + + // Make the frame frozen and expect a notification. + EXPECT_CALL(obs, OnAllFramesInProcessFrozen(process_node_.get())); + f0.get()->SetLifecycleState(LifecycleState::kFrozen); + testing::Mock::VerifyAndClear(&obs); + ExpectProcessData(1, 1); + + // Create another process and another page. + auto proc2 = CreateNode<ProcessNodeImpl>(); + auto page2 = CreateNode<PageNodeImpl>(nullptr); + ExpectProcessData(1, 1); + + // Create a child frame for the first page hosted in the second process. + auto f1 = + CreateNode<FrameNodeImpl>(proc2.get(), page_node_.get(), f0.get(), 1); + ExpectProcessData(1, 1); + + // Immediately make it current. + f1.get()->SetIsCurrent(true); + ExpectProcessData(1, 1); + + // Freeze the child frame and expect no change, as its in another process. + f1.get()->SetLifecycleState(LifecycleState::kFrozen); + ExpectProcessData(1, 1); + + // Unfreeze both frames. + f0.get()->SetLifecycleState(LifecycleState::kRunning); + ExpectProcessData(1, 0); + f1.get()->SetLifecycleState(LifecycleState::kRunning); + ExpectProcessData(1, 0); + + // Create a main frame in the second page, but that's in the first process. + auto f2 = + CreateNode<FrameNodeImpl>(process_node_.get(), page2.get(), nullptr, 2); + ExpectProcessData(1, 0); + + // Freeze the main frame in the second page. + f2.get()->SetLifecycleState(LifecycleState::kFrozen); + ExpectProcessData(1, 0); + + // Make the frozen second main frame current. + f2.get()->SetIsCurrent(true); + ExpectProcessData(2, 1); + + // Freeze the child frame of the first page, hosted in the other process. + f1.get()->SetLifecycleState(LifecycleState::kFrozen); + ExpectProcessData(2, 1); + + // Freeze the main frame of the first page. + EXPECT_CALL(obs, OnAllFramesInProcessFrozen(process_node_.get())); + f0.get()->SetLifecycleState(LifecycleState::kFrozen); + testing::Mock::VerifyAndClear(&obs); + ExpectProcessData(2, 2); + + // Destroy the child frame in the other process, and then kill that process. + f1.reset(); + ExpectProcessData(2, 2); + proc2.reset(); + ExpectProcessData(2, 2); + + // Kill the main frame of the second page. + f2.reset(); + ExpectProcessData(1, 1); + + // Kill the main frame of the first page. + f0.reset(); + ExpectProcessData(0, 0); + + process_node_.get()->RemoveObserver(&obs); +} + +TEST_F(FrozenFrameAggregatorTest, PageAggregation) { + ExpectPageData(0, 0); + ExpectRunning(); + + // Add a non-current frame. + auto f0 = CreateFrame(nullptr, 0); + ExpectPageData(0, 0); + ExpectRunning(); + + // Make the frame current. + f0.get()->SetIsCurrent(true); + ExpectPageData(1, 0); + ExpectRunning(); + + // Freeze the frame. + f0.get()->SetLifecycleState(LifecycleState::kFrozen); + ExpectPageData(1, 1); + ExpectFrozen(); + + // Unfreeze the frame. + f0.get()->SetLifecycleState(LifecycleState::kRunning); + ExpectPageData(1, 0); + ExpectRunning(); + + // Add a child frame. + auto f1 = CreateFrame(f0.get(), 1); + ExpectPageData(1, 0); + ExpectRunning(); + + // Make it current as well. + f1.get()->SetIsCurrent(true); + ExpectPageData(2, 0); + ExpectRunning(); + + // Freeze them both. + f1.get()->SetLifecycleState(LifecycleState::kFrozen); + ExpectPageData(2, 1); + ExpectRunning(); + f0.get()->SetLifecycleState(LifecycleState::kFrozen); + ExpectPageData(2, 2); + ExpectFrozen(); + + // Unfreeze them both. + f0.get()->SetLifecycleState(LifecycleState::kRunning); + ExpectPageData(2, 1); + ExpectRunning(); + f1.get()->SetLifecycleState(LifecycleState::kRunning); + ExpectPageData(2, 0); + ExpectRunning(); + + // Create a third frame. + auto f1a = CreateFrame(f0.get(), 1); + ExpectPageData(2, 0); + ExpectRunning(); + + // Swap the f1 and f1a. + f1.get()->SetIsCurrent(false); + ExpectPageData(1, 0); + ExpectRunning(); + f1a.get()->SetIsCurrent(true); + ExpectPageData(2, 0); + ExpectRunning(); + + // Freeze the original frame and swap it back. + f1.get()->SetLifecycleState(LifecycleState::kFrozen); + f1a.get()->SetIsCurrent(false); + ExpectPageData(1, 0); + ExpectRunning(); + f1.get()->SetIsCurrent(true); + ExpectPageData(2, 1); + ExpectRunning(); + + // Freeze the non-current frame and expect nothing to change. + f1a.get()->SetLifecycleState(LifecycleState::kFrozen); + ExpectPageData(2, 1); + ExpectRunning(); + + // Remove the non-current frame and expect nothing to change. + f1a.reset(); + ExpectPageData(2, 1); + ExpectRunning(); + + // Remove the frozen child frame and expect a change. + f1.reset(); + ExpectPageData(1, 0); + ExpectRunning(); + + // Freeze the main frame again. + f0.get()->SetLifecycleState(LifecycleState::kFrozen); + ExpectPageData(1, 1); + ExpectFrozen(); + + // Remove the main frame. An empty page is always considered as "running". + f0.reset(); + ExpectPageData(0, 0); + ExpectRunning(); +} + +} // namespace performance_manager
diff --git a/chrome/browser/performance_manager/decorators/page_almost_idle_decorator.cc b/chrome/browser/performance_manager/decorators/page_almost_idle_decorator.cc index ed02833..0587f23 100644 --- a/chrome/browser/performance_manager/decorators/page_almost_idle_decorator.cc +++ b/chrome/browser/performance_manager/decorators/page_almost_idle_decorator.cc
@@ -74,21 +74,6 @@ NOTREACHED(); } -void PageAlmostIdleDecorator::OnPageEventReceived( - PageNodeImpl* page_node, - resource_coordinator::mojom::Event event) { - // Only the navigation committed event is of interest. - if (event != resource_coordinator::mojom::Event::kNavigationCommitted) - return; - - // Reset the load-idle state associated with this page as a new navigation has - // started. - auto* data = DataImpl::GetOrCreate(page_node); - data->load_idle_state_ = LoadIdleState::kLoadingNotStarted; - PageAlmostIdleAccess::SetPageAlmostIdle(page_node, false); - UpdateLoadIdleStatePage(page_node); -} - void PageAlmostIdleDecorator::OnNetworkAlmostIdleChanged( FrameNodeImpl* frame_node) { UpdateLoadIdleStateFrame(frame_node); @@ -98,6 +83,16 @@ UpdateLoadIdleStatePage(page_node); } +void PageAlmostIdleDecorator::OnMainFrameNavigationCommitted( + PageNodeImpl* page_node) { + // Reset the load-idle state associated with this page as a new navigation has + // started. + auto* data = DataImpl::GetOrCreate(page_node); + data->load_idle_state_ = LoadIdleState::kLoadingNotStarted; + PageAlmostIdleAccess::SetPageAlmostIdle(page_node, false); + UpdateLoadIdleStatePage(page_node); +} + void PageAlmostIdleDecorator::OnMainThreadTaskLoadIsLow( ProcessNodeImpl* process_node) { UpdateLoadIdleStateProcess(process_node);
diff --git a/chrome/browser/performance_manager/decorators/page_almost_idle_decorator.h b/chrome/browser/performance_manager/decorators/page_almost_idle_decorator.h index 847af6a..4d03ae5 100644 --- a/chrome/browser/performance_manager/decorators/page_almost_idle_decorator.h +++ b/chrome/browser/performance_manager/decorators/page_almost_idle_decorator.h
@@ -28,10 +28,9 @@ // GraphObserver implementation: bool ShouldObserve(const NodeBase* node) override; - void OnPageEventReceived(PageNodeImpl* page_node, - resource_coordinator::mojom::Event event) override; void OnNetworkAlmostIdleChanged(FrameNodeImpl* frame_node) override; void OnIsLoadingChanged(PageNodeImpl* page_node) override; + void OnMainFrameNavigationCommitted(PageNodeImpl* page_node) override; void OnMainThreadTaskLoadIsLow(ProcessNodeImpl* process_node) override; protected:
diff --git a/chrome/browser/performance_manager/decorators/page_almost_idle_decorator_unittest.cc b/chrome/browser/performance_manager/decorators/page_almost_idle_decorator_unittest.cc index 44ab8f1..0c8ffc7 100644 --- a/chrome/browser/performance_manager/decorators/page_almost_idle_decorator_unittest.cc +++ b/chrome/browser/performance_manager/decorators/page_almost_idle_decorator_unittest.cc
@@ -22,35 +22,6 @@ namespace performance_manager { -namespace { - -class LenientMockGraphObserver : public GraphObserver { - public: - LenientMockGraphObserver() = default; - ~LenientMockGraphObserver() override = default; - - virtual bool ShouldObserve(const NodeBase* node) { - return node->id().type == PageNodeImpl::Type(); - } - - MOCK_METHOD1(OnPageAlmostIdleChanged, void(PageNodeImpl*)); - - void ExpectOnPageAlmostIdleChanged(PageNodeImpl* page_node, - bool page_almost_idle) { - EXPECT_CALL(*this, OnPageAlmostIdleChanged(page_node)) - .WillOnce(::testing::InvokeWithoutArgs([page_node, page_almost_idle]() { - EXPECT_EQ(page_almost_idle, page_node->page_almost_idle()); - })); - } - - private: - DISALLOW_COPY_AND_ASSIGN(LenientMockGraphObserver); -}; - -using MockGraphObserver = ::testing::StrictMock<LenientMockGraphObserver>; - -} // namespace - class PageAlmostIdleDecoratorTest : public GraphTestHarness { protected: PageAlmostIdleDecoratorTest() = default;
diff --git a/chrome/browser/performance_manager/graph/frame_node_impl.cc b/chrome/browser/performance_manager/graph/frame_node_impl.cc index 11629b81..b84f639 100644 --- a/chrome/browser/performance_manager/graph/frame_node_impl.cc +++ b/chrome/browser/performance_manager/graph/frame_node_impl.cc
@@ -37,15 +37,7 @@ void FrameNodeImpl::SetLifecycleState( resource_coordinator::mojom::LifecycleState state) { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - if (state == lifecycle_state_) - return; - - resource_coordinator::mojom::LifecycleState old_state = lifecycle_state_; - lifecycle_state_ = state; - - // Notify parents of this change. - process_node_->OnFrameLifecycleStateChanged(this, old_state); - page_node_->OnFrameLifecycleStateChanged(this, old_state); + lifecycle_state_.SetAndMaybeNotify(this, state); } void FrameNodeImpl::SetHasNonEmptyBeforeUnload(bool has_nonempty_beforeunload) { @@ -81,8 +73,9 @@ } void FrameNodeImpl::OnNonPersistentNotificationCreated() { - SendEvent( - resource_coordinator::mojom::Event::kNonPersistentNotificationCreated); + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + for (auto& observer : observers()) + observer.OnNonPersistentNotificationCreated(this); } FrameNodeImpl* FrameNodeImpl::parent_frame_node() const { @@ -109,7 +102,7 @@ resource_coordinator::mojom::LifecycleState FrameNodeImpl::lifecycle_state() const { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - return lifecycle_state_; + return lifecycle_state_.value(); } bool FrameNodeImpl::has_nonempty_beforeunload() const { @@ -265,12 +258,6 @@ process_node_->RemoveFrame(this); } -void FrameNodeImpl::OnEventReceived(resource_coordinator::mojom::Event event) { - DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - for (auto& observer : observers()) - observer.OnFrameEventReceived(this, event); -} - bool FrameNodeImpl::HasFrameNodeInAncestors(FrameNodeImpl* frame_node) const { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); if (parent_frame_node_ == frame_node ||
diff --git a/chrome/browser/performance_manager/graph/frame_node_impl.h b/chrome/browser/performance_manager/graph/frame_node_impl.h index f45eb5ab..bbbb0df 100644 --- a/chrome/browser/performance_manager/graph/frame_node_impl.h +++ b/chrome/browser/performance_manager/graph/frame_node_impl.h
@@ -111,9 +111,6 @@ void JoinGraph() override; void LeaveGraph() override; - // CoordinationUnitInterface implementation. - void OnEventReceived(resource_coordinator::mojom::Event event) override; - bool HasFrameNodeInAncestors(FrameNodeImpl* frame_node) const; bool HasFrameNodeInDescendants(FrameNodeImpl* frame_node) const; @@ -125,8 +122,11 @@ const int frame_tree_node_id_; base::flat_set<FrameNodeImpl*> child_frame_nodes_; - resource_coordinator::mojom::LifecycleState lifecycle_state_ = - resource_coordinator::mojom::LifecycleState::kRunning; + ObservedProperty::NotifiesOnlyOnChanges< + resource_coordinator::mojom::LifecycleState, + &GraphObserver::OnLifecycleStateChanged> + lifecycle_state_{resource_coordinator::mojom::LifecycleState::kRunning}; + bool has_nonempty_beforeunload_ = false; GURL url_;
diff --git a/chrome/browser/performance_manager/graph/frame_node_impl_unittest.cc b/chrome/browser/performance_manager/graph/frame_node_impl_unittest.cc index 9e01e6cd..fb0597b 100644 --- a/chrome/browser/performance_manager/graph/frame_node_impl_unittest.cc +++ b/chrome/browser/performance_manager/graph/frame_node_impl_unittest.cc
@@ -38,7 +38,7 @@ TEST_F(FrameNodeImplTest, AddFrameHierarchyBasic) { auto process = CreateNode<ProcessNodeImpl>(); - auto page = CreateNode<PageNodeImpl>(nullptr /*TEST*/); + auto page = CreateNode<PageNodeImpl>(nullptr); auto parent_node = CreateNode<FrameNodeImpl>(process.get(), page.get(), nullptr, 0); auto child2_node = CreateNode<FrameNodeImpl>(process.get(), page.get(), @@ -54,7 +54,7 @@ TEST_F(FrameNodeImplTest, Url) { auto process = CreateNode<ProcessNodeImpl>(); - auto page = CreateNode<PageNodeImpl>(nullptr /*TEST*/); + auto page = CreateNode<PageNodeImpl>(nullptr); auto frame_node = CreateNode<FrameNodeImpl>(process.get(), page.get(), nullptr, 0); EXPECT_TRUE(frame_node->url().is_empty()); @@ -65,7 +65,7 @@ TEST_F(FrameNodeImplTest, RemoveChildFrame) { auto process = CreateNode<ProcessNodeImpl>(); - auto page = CreateNode<PageNodeImpl>(nullptr /*TEST*/); + auto page = CreateNode<PageNodeImpl>(nullptr); auto parent_frame_node = CreateNode<FrameNodeImpl>(process.get(), page.get(), nullptr, 0); auto child_frame_node = CreateNode<FrameNodeImpl>(process.get(), page.get(), @@ -84,68 +84,4 @@ EXPECT_TRUE(!parent_frame_node->parent_frame_node()); } -TEST_F(FrameNodeImplTest, LifecycleStatesTransitions) { - const auto kRunning = resource_coordinator::mojom::LifecycleState::kRunning; - const auto kFrozen = resource_coordinator::mojom::LifecycleState::kFrozen; - MockMultiplePagesWithMultipleProcessesGraph mock_graph(graph()); - - // Verifying the model. - ASSERT_TRUE(mock_graph.frame->IsMainFrame()); - ASSERT_TRUE(mock_graph.other_frame->IsMainFrame()); - ASSERT_FALSE(mock_graph.child_frame->IsMainFrame()); - ASSERT_EQ(mock_graph.child_frame->parent_frame_node(), - mock_graph.other_frame.get()); - ASSERT_EQ(mock_graph.frame->page_node(), mock_graph.page.get()); - ASSERT_EQ(mock_graph.other_frame->page_node(), mock_graph.other_page.get()); - - // Freezing a child frame should not affect the page state. - mock_graph.child_frame->SetLifecycleState( - resource_coordinator::mojom::LifecycleState::kFrozen); - EXPECT_EQ(kRunning, mock_graph.page->lifecycle_state()); - EXPECT_EQ(kRunning, mock_graph.other_page->lifecycle_state()); - - // Freezing the only frame in a page should freeze that page. - mock_graph.frame->SetLifecycleState( - resource_coordinator::mojom::LifecycleState::kFrozen); - EXPECT_EQ(kFrozen, mock_graph.page->lifecycle_state()); - EXPECT_EQ(kRunning, mock_graph.other_page->lifecycle_state()); - - // Unfreeze the child frame in the other page. - mock_graph.child_frame->SetLifecycleState( - resource_coordinator::mojom::LifecycleState::kRunning); - EXPECT_EQ(kFrozen, mock_graph.page->lifecycle_state()); - EXPECT_EQ(kRunning, mock_graph.other_page->lifecycle_state()); - - // Freezing the main frame in the other page should not alter that pages - // state, as there is still a child frame that is running. - mock_graph.other_frame->SetLifecycleState( - resource_coordinator::mojom::LifecycleState::kFrozen); - EXPECT_EQ(kFrozen, mock_graph.page->lifecycle_state()); - EXPECT_EQ(kRunning, mock_graph.other_page->lifecycle_state()); - - // Refreezing the child frame should freeze the page. - mock_graph.child_frame->SetLifecycleState( - resource_coordinator::mojom::LifecycleState::kFrozen); - EXPECT_EQ(kFrozen, mock_graph.page->lifecycle_state()); - EXPECT_EQ(kFrozen, mock_graph.other_page->lifecycle_state()); - - // Unfreezing a main frame should unfreeze the associated page. - mock_graph.frame->SetLifecycleState( - resource_coordinator::mojom::LifecycleState::kRunning); - EXPECT_EQ(kRunning, mock_graph.page->lifecycle_state()); - EXPECT_EQ(kFrozen, mock_graph.other_page->lifecycle_state()); - - // Unfreezing the child frame should unfreeze the associated page. - mock_graph.child_frame->SetLifecycleState( - resource_coordinator::mojom::LifecycleState::kRunning); - EXPECT_EQ(kRunning, mock_graph.page->lifecycle_state()); - EXPECT_EQ(kRunning, mock_graph.other_page->lifecycle_state()); - - // Unfreezing the main frame shouldn't change anything. - mock_graph.other_frame->SetLifecycleState( - resource_coordinator::mojom::LifecycleState::kRunning); - EXPECT_EQ(kRunning, mock_graph.page->lifecycle_state()); - EXPECT_EQ(kRunning, mock_graph.other_page->lifecycle_state()); -} - } // namespace performance_manager
diff --git a/chrome/browser/performance_manager/graph/graph_test_harness.h b/chrome/browser/performance_manager/graph/graph_test_harness.h index 90ea2f1..f221655 100644 --- a/chrome/browser/performance_manager/graph/graph_test_harness.h +++ b/chrome/browser/performance_manager/graph/graph_test_harness.h
@@ -34,16 +34,22 @@ return TestNodeWrapper<NodeClass>(std::move(node)); } + TestNodeWrapper() {} + explicit TestNodeWrapper(std::unique_ptr<NodeClass> impl) : impl_(std::move(impl)) { DCHECK(impl_.get()); } + + TestNodeWrapper(TestNodeWrapper&& other) : impl_(std::move(other.impl_)) {} + + void operator=(TestNodeWrapper&& other) { impl_ = std::move(other.impl_); } + void operator=(TestNodeWrapper& other) = delete; + ~TestNodeWrapper() { reset(); } NodeClass* operator->() const { return impl_.get(); } - TestNodeWrapper(TestNodeWrapper&& other) : impl_(std::move(other.impl_)) {} - NodeClass* get() const { return impl_.get(); } void reset() { @@ -55,8 +61,6 @@ private: std::unique_ptr<NodeClass> impl_; - - DISALLOW_COPY_AND_ASSIGN(TestNodeWrapper); }; // This specialization is necessary because the graph has ownership of the
diff --git a/chrome/browser/performance_manager/graph/mock_graphs.cc b/chrome/browser/performance_manager/graph/mock_graphs.cc index a84f57c..4967aea 100644 --- a/chrome/browser/performance_manager/graph/mock_graphs.cc +++ b/chrome/browser/performance_manager/graph/mock_graphs.cc
@@ -31,7 +31,7 @@ Graph* graph) : system(TestNodeWrapper<SystemNodeImpl>::Create(graph)), process(TestNodeWrapper<TestProcessNodeImpl>::Create(graph)), - page(TestNodeWrapper<PageNodeImpl>::Create(graph, nullptr /*TEST*/)), + page(TestNodeWrapper<PageNodeImpl>::Create(graph, nullptr)), frame(TestNodeWrapper<FrameNodeImpl>::Create(graph, process.get(), page.get(), @@ -51,8 +51,7 @@ MockMultiplePagesInSingleProcessGraph::MockMultiplePagesInSingleProcessGraph( Graph* graph) : MockSinglePageInSingleProcessGraph(graph), - other_page( - TestNodeWrapper<PageNodeImpl>::Create(graph, nullptr /*TEST*/)), + other_page(TestNodeWrapper<PageNodeImpl>::Create(graph, nullptr)), other_frame(TestNodeWrapper<FrameNodeImpl>::Create(graph, process.get(), other_page.get(),
diff --git a/chrome/browser/performance_manager/graph/node_attached_data_impl.h b/chrome/browser/performance_manager/graph/node_attached_data_impl.h index 52c6cee..ec2c96d 100644 --- a/chrome/browser/performance_manager/graph/node_attached_data_impl.h +++ b/chrome/browser/performance_manager/graph/node_attached_data_impl.h
@@ -389,11 +389,8 @@ template <typename NodeType> DataType* NodeAttachedDataImpl<DataType>::NodeAttachedDataInternalOnNodeType< NodeType>::GetOrCreate(const NodeType* node) { - static_assert( - std::is_same<InternalNodeAttachedDataStorage<sizeof(DataType)>*, - typename std::result_of<decltype ( - &DataType::GetInternalStorage)(NodeType*)>::type>::value, - "NodeType provided internal storage doesn't match size of DataType"); + // TODO(chrisha): Add a compile test that this is enforced. Otherwise, there's + // potential for a OOB reads / security issues. https://www.crbug.com/952864 InternalNodeAttachedDataStorage<sizeof(DataType)>* storage = DataType::GetInternalStorage(const_cast<NodeType*>(node)); if (!storage->Get()) {
diff --git a/chrome/browser/performance_manager/graph/node_base.cc b/chrome/browser/performance_manager/graph/node_base.cc index 13be796..b7c70a23 100644 --- a/chrome/browser/performance_manager/graph/node_base.cc +++ b/chrome/browser/performance_manager/graph/node_base.cc
@@ -46,15 +46,4 @@ return graph_->GetNodeByID(other_node->id()) == other_node; } -void NodeBase::OnEventReceived(resource_coordinator::mojom::Event event) { - DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - for (auto& observer : observers()) - observer.OnEventReceived(this, event); -} - -void NodeBase::SendEvent(resource_coordinator::mojom::Event event) { - DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - OnEventReceived(event); -} - } // namespace performance_manager
diff --git a/chrome/browser/performance_manager/graph/node_base.h b/chrome/browser/performance_manager/graph/node_base.h index 593ccfce..e58aa5e 100644 --- a/chrome/browser/performance_manager/graph/node_base.h +++ b/chrome/browser/performance_manager/graph/node_base.h
@@ -62,10 +62,6 @@ // Returns true if |other_node| is in the same graph. bool NodeInGraph(const NodeBase* other_node) const; - virtual void OnEventReceived(resource_coordinator::mojom::Event event); - - void SendEvent(resource_coordinator::mojom::Event event); - Graph* const graph_; const resource_coordinator::CoordinationUnitID id_;
diff --git a/chrome/browser/performance_manager/graph/node_base_unittest.cc b/chrome/browser/performance_manager/graph/node_base_unittest.cc index f92ddcd..9c3890e8 100644 --- a/chrome/browser/performance_manager/graph/node_base_unittest.cc +++ b/chrome/browser/performance_manager/graph/node_base_unittest.cc
@@ -20,90 +20,86 @@ } // namespace -TEST_F(NodeBaseTest, - GetAssociatedCoordinationUnitsForSinglePageInSingleProcess) { +TEST_F(NodeBaseTest, GetAssociatedNodesForSinglePageInSingleProcess) { MockSinglePageInSingleProcessGraph mock_graph(graph()); auto pages_associated_with_process = - mock_graph.process->GetAssociatedPageCoordinationUnits(); + mock_graph.process->GetAssociatedPageNodes(); EXPECT_EQ(1u, pages_associated_with_process.size()); EXPECT_EQ(1u, pages_associated_with_process.count(mock_graph.page.get())); auto processes_associated_with_page = - mock_graph.page->GetAssociatedProcessCoordinationUnits(); + mock_graph.page->GetAssociatedProcessNodes(); EXPECT_EQ(1u, processes_associated_with_page.size()); EXPECT_EQ(1u, processes_associated_with_page.count(mock_graph.process.get())); } -TEST_F(NodeBaseTest, - GetAssociatedCoordinationUnitsForMultiplePagesInSingleProcess) { +TEST_F(NodeBaseTest, GetAssociatedNodesForMultiplePagesInSingleProcess) { MockMultiplePagesInSingleProcessGraph mock_graph(graph()); auto pages_associated_with_process = - mock_graph.process->GetAssociatedPageCoordinationUnits(); + mock_graph.process->GetAssociatedPageNodes(); EXPECT_EQ(2u, pages_associated_with_process.size()); EXPECT_EQ(1u, pages_associated_with_process.count(mock_graph.page.get())); EXPECT_EQ(1u, pages_associated_with_process.count(mock_graph.other_page.get())); auto processes_associated_with_page = - mock_graph.page->GetAssociatedProcessCoordinationUnits(); + mock_graph.page->GetAssociatedProcessNodes(); EXPECT_EQ(1u, processes_associated_with_page.size()); EXPECT_EQ(1u, processes_associated_with_page.count(mock_graph.process.get())); auto processes_associated_with_other_page = - mock_graph.other_page->GetAssociatedProcessCoordinationUnits(); + mock_graph.other_page->GetAssociatedProcessNodes(); EXPECT_EQ(1u, processes_associated_with_other_page.size()); EXPECT_EQ(1u, processes_associated_with_page.count(mock_graph.process.get())); } -TEST_F(NodeBaseTest, - GetAssociatedCoordinationUnitsForSinglePageWithMultipleProcesses) { +TEST_F(NodeBaseTest, GetAssociatedNodesForSinglePageWithMultipleProcesses) { MockSinglePageWithMultipleProcessesGraph mock_graph(graph()); auto pages_associated_with_process = - mock_graph.process->GetAssociatedPageCoordinationUnits(); + mock_graph.process->GetAssociatedPageNodes(); EXPECT_EQ(1u, pages_associated_with_process.size()); EXPECT_EQ(1u, pages_associated_with_process.count(mock_graph.page.get())); auto pages_associated_with_other_process = - mock_graph.other_process->GetAssociatedPageCoordinationUnits(); + mock_graph.other_process->GetAssociatedPageNodes(); EXPECT_EQ(1u, pages_associated_with_other_process.size()); EXPECT_EQ(1u, pages_associated_with_other_process.count(mock_graph.page.get())); auto processes_associated_with_page = - mock_graph.page->GetAssociatedProcessCoordinationUnits(); + mock_graph.page->GetAssociatedProcessNodes(); EXPECT_EQ(2u, processes_associated_with_page.size()); EXPECT_EQ(1u, processes_associated_with_page.count(mock_graph.process.get())); EXPECT_EQ( 1u, processes_associated_with_page.count(mock_graph.other_process.get())); } -TEST_F(NodeBaseTest, - GetAssociatedCoordinationUnitsForMultiplePagesWithMultipleProcesses) { +TEST_F(NodeBaseTest, GetAssociatedNodesForMultiplePagesWithMultipleProcesses) { MockMultiplePagesWithMultipleProcessesGraph mock_graph(graph()); auto pages_associated_with_process = - mock_graph.process->GetAssociatedPageCoordinationUnits(); + mock_graph.process->GetAssociatedPageNodes(); EXPECT_EQ(2u, pages_associated_with_process.size()); EXPECT_EQ(1u, pages_associated_with_process.count(mock_graph.page.get())); EXPECT_EQ(1u, pages_associated_with_process.count(mock_graph.other_page.get())); auto pages_associated_with_other_process = - mock_graph.other_process->GetAssociatedPageCoordinationUnits(); + mock_graph.other_process->GetAssociatedPageNodes(); EXPECT_EQ(1u, pages_associated_with_other_process.size()); EXPECT_EQ(1u, pages_associated_with_other_process.count( mock_graph.other_page.get())); auto processes_associated_with_page = - mock_graph.page->GetAssociatedProcessCoordinationUnits(); + mock_graph.page->GetAssociatedProcessNodes(); EXPECT_EQ(1u, processes_associated_with_page.size()); EXPECT_EQ(1u, processes_associated_with_page.count(mock_graph.process.get())); auto processes_associated_with_other_page = - mock_graph.other_page->GetAssociatedProcessCoordinationUnits(); + mock_graph.other_page->GetAssociatedProcessNodes(); EXPECT_EQ(2u, processes_associated_with_other_page.size()); EXPECT_EQ( 1u, processes_associated_with_other_page.count(mock_graph.process.get()));
diff --git a/chrome/browser/performance_manager/graph/page_node_impl.cc b/chrome/browser/performance_manager/graph/page_node_impl.cc index 1fc0f42ac..64c5a89cd 100644 --- a/chrome/browser/performance_manager/graph/page_node_impl.cc +++ b/chrome/browser/performance_manager/graph/page_node_impl.cc
@@ -64,16 +64,10 @@ DCHECK_EQ(this, frame_node->page_node()); DCHECK(NodeInGraph(frame_node)); - if (frame_node->parent_frame_node() == nullptr) { - main_frame_nodes_.insert(frame_node); - } ++frame_node_count_; + if (frame_node->parent_frame_node() == nullptr) + main_frame_nodes_.insert(frame_node); - OnNumFrozenFramesStateChange( - frame_node->lifecycle_state() == - resource_coordinator::mojom::LifecycleState::kFrozen - ? 1 - : 0); MaybeInvalidateInterventionPolicies(frame_node, true /* adding_frame */); } @@ -89,11 +83,6 @@ DCHECK_EQ(1u, removed); } - OnNumFrozenFramesStateChange( - frame_node->lifecycle_state() == - resource_coordinator::mojom::LifecycleState::kFrozen - ? -1 - : 0); MaybeInvalidateInterventionPolicies(frame_node, false /* adding_frame */); } @@ -116,11 +105,15 @@ } void PageNodeImpl::OnFaviconUpdated() { - SendEvent(resource_coordinator::mojom::Event::kFaviconUpdated); + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + for (auto& observer : observers()) + observer.OnFaviconUpdated(this); } void PageNodeImpl::OnTitleUpdated() { - SendEvent(resource_coordinator::mojom::Event::kTitleUpdated); + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + for (auto& observer : observers()) + observer.OnTitleUpdated(this); } void PageNodeImpl::OnMainFrameNavigationCommitted( @@ -131,11 +124,12 @@ navigation_committed_time_ = navigation_committed_time; main_frame_url_ = url; navigation_id_ = navigation_id; - SendEvent(resource_coordinator::mojom::Event::kNavigationCommitted); + for (auto& observer : observers()) + observer.OnMainFrameNavigationCommitted(this); } -base::flat_set<ProcessNodeImpl*> -PageNodeImpl::GetAssociatedProcessCoordinationUnits() const { +base::flat_set<ProcessNodeImpl*> PageNodeImpl::GetAssociatedProcessNodes() + const { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); base::flat_set<ProcessNodeImpl*> process_nodes; ForAllFrameNodes([&process_nodes](FrameNodeImpl* frame_node) -> bool { @@ -150,9 +144,8 @@ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); double cpu_usage = 0; - for (auto* process_node : GetAssociatedProcessCoordinationUnits()) { - size_t pages_in_process = - process_node->GetAssociatedPageCoordinationUnits().size(); + for (auto* process_node : GetAssociatedProcessNodes()) { + size_t pages_in_process = process_node->GetAssociatedPageNodes().size(); DCHECK_LE(1u, pages_in_process); cpu_usage += process_node->cpu_usage() / pages_in_process; } @@ -271,23 +264,6 @@ has_nonempty_beforeunload_ = has_nonempty_beforeunload; } -void PageNodeImpl::OnFrameLifecycleStateChanged( - FrameNodeImpl* frame_node, - resource_coordinator::mojom::LifecycleState old_state) { - DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - DCHECK_EQ(this, frame_node->page_node()); - DCHECK_NE(old_state, frame_node->lifecycle_state()); - - int delta = 0; - if (old_state == resource_coordinator::mojom::LifecycleState::kFrozen) - delta = -1; - else if (frame_node->lifecycle_state() == - resource_coordinator::mojom::LifecycleState::kFrozen) - delta = 1; - if (delta != 0) - OnNumFrozenFramesStateChange(delta); -} - void PageNodeImpl::OnFrameInterventionPolicyChanged( FrameNodeImpl* frame, resource_coordinator::mojom::PolicyControlledIntervention intervention, @@ -352,6 +328,8 @@ } bool PageNodeImpl::HasFrame(FrameNodeImpl* frame_node) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + bool has_node = false; ForAllFrameNodes([&has_node, &frame_node](FrameNodeImpl* node) -> bool { if (node != frame_node) @@ -365,51 +343,12 @@ } void PageNodeImpl::SetPageAlmostIdle(bool page_almost_idle) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); page_almost_idle_.SetAndMaybeNotify(this, page_almost_idle); } -void PageNodeImpl::OnEventReceived(resource_coordinator::mojom::Event event) { +void PageNodeImpl::SetLifecycleState(LifecycleState lifecycle_state) { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - for (auto& observer : observers()) - observer.OnPageEventReceived(this, event); -} - -void PageNodeImpl::OnNumFrozenFramesStateChange(int num_frozen_frames_delta) { - DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - num_frozen_frames_ += num_frozen_frames_delta; - DCHECK_LE(num_frozen_frames_, frame_node_count_); - - const auto kRunning = resource_coordinator::mojom::LifecycleState::kRunning; - const auto kFrozen = resource_coordinator::mojom::LifecycleState::kFrozen; - - // We are interested in knowing when we have transitioned to or from - // "fully frozen". A page with no frames is considered to be running by - // default. - bool was_fully_frozen = lifecycle_state_.value() == kFrozen; - bool is_fully_frozen = - (frame_node_count_ != 0) && num_frozen_frames_ == frame_node_count_; - if (was_fully_frozen == is_fully_frozen) - return; - - if (is_fully_frozen) { - // Aggregate the beforeunload handler information from the entire frame - // tree. - bool has_nonempty_beforeunload = false; - ForAllFrameNodes( - [&has_nonempty_beforeunload](FrameNodeImpl* frame_node) -> bool { - if (!frame_node->has_nonempty_beforeunload()) - return true; - has_nonempty_beforeunload = true; - return false; - }); - - set_has_nonempty_beforeunload(has_nonempty_beforeunload); - } - - // TODO(fdoray): Store the lifecycle state as a member on the - // PageCoordinationUnit rather than as a non-typed property. - resource_coordinator::mojom::LifecycleState lifecycle_state = - is_fully_frozen ? kFrozen : kRunning; lifecycle_state_.SetAndMaybeNotify(this, lifecycle_state); } @@ -486,6 +425,7 @@ template <typename MapFunction> void PageNodeImpl::ForAllFrameNodes(MapFunction map_function) const { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); for (auto* main_frame_node : main_frame_nodes_) ForFrameAndDescendents(main_frame_node, map_function); }
diff --git a/chrome/browser/performance_manager/graph/page_node_impl.h b/chrome/browser/performance_manager/graph/page_node_impl.h index 19f6355..77a5019 100644 --- a/chrome/browser/performance_manager/graph/page_node_impl.h +++ b/chrome/browser/performance_manager/graph/page_node_impl.h
@@ -52,8 +52,7 @@ // There is no direct relationship between processes and pages. However, // frames are accessible by both processes and frames, so we find all of the // processes that are reachable from the pages's accessible frames. - base::flat_set<ProcessNodeImpl*> GetAssociatedProcessCoordinationUnits() - const; + base::flat_set<ProcessNodeImpl*> GetAssociatedProcessNodes() const; // Returns the average CPU usage that can be attributed to this page over the // last measurement period. CPU usage is expressed as the average percentage @@ -95,11 +94,6 @@ uint64_t private_footprint_kb_estimate); void set_has_nonempty_beforeunload(bool has_nonempty_beforeunload); - // Invoked when the state of a frame in this page changes. - // TODO(chrisha): Move this out to a decorator. - void OnFrameLifecycleStateChanged(FrameNodeImpl* frame_node, - LifecycleState old_state); - // Invoked when a frame belonging to this page changes intervention policy // values. // TODO(chrisha): Move this out to a decorator. @@ -134,6 +128,7 @@ private: friend class FrameNodeImpl; + friend class FrozenFrameAggregatorAccess; friend class PageAlmostIdleAccess; void AddFrame(FrameNodeImpl* frame_node); @@ -145,16 +140,7 @@ bool HasFrame(FrameNodeImpl* frame_node); void SetPageAlmostIdle(bool page_almost_idle); - - // CoordinationUnitInterface implementation. - void OnEventReceived(resource_coordinator::mojom::Event event) override; - - // This is called whenever |num_frozen_frames_| changes, or whenever - // a frame is added to or removed from this page. It is used to synthesize the - // value of |has_nonempty_beforeunload| and to update the LifecycleState of - // the page. Calling this with |num_frozen_frames_delta == 0| implies that the - // number of frames itself has changed. - void OnNumFrozenFramesStateChange(int num_frozen_frames_delta); + void SetLifecycleState(LifecycleState lifecycle_state); // Invalidates all currently aggregated intervention policies. void InvalidateAllInterventionPolicies(); @@ -204,9 +190,6 @@ // The most current memory footprint estimate. uint64_t private_footprint_kb_estimate_ = 0; - // Counts the number of frames in a page that are frozen. - size_t num_frozen_frames_ = 0; - // Indicates whether or not this page has a non-empty beforeunload handler. // This is an aggregation of the same value on each frame in the page's frame // tree. The aggregation is made at the moment all frames associated with a @@ -263,6 +246,9 @@ // Storage for PageAlmostIdle user data. std::unique_ptr<NodeAttachedData> page_almost_idle_data_; + // Inline storage for FrozenFrameAggregator user data. + InternalNodeAttachedDataStorage<sizeof(uintptr_t) + 8> frozen_frame_data_; + DISALLOW_COPY_AND_ASSIGN(PageNodeImpl); };
diff --git a/chrome/browser/performance_manager/graph/page_node_impl_unittest.cc b/chrome/browser/performance_manager/graph/page_node_impl_unittest.cc index 3bbe79e7..43b7f78d 100644 --- a/chrome/browser/performance_manager/graph/page_node_impl_unittest.cc +++ b/chrome/browser/performance_manager/graph/page_node_impl_unittest.cc
@@ -40,7 +40,7 @@ TEST_F(PageNodeImplTest, AddFrameBasic) { auto process_node = CreateNode<ProcessNodeImpl>(); - auto page_node = CreateNode<PageNodeImpl>(nullptr /*TEST*/); + auto page_node = CreateNode<PageNodeImpl>(nullptr); auto parent_frame = CreateNode<FrameNodeImpl>(process_node.get(), page_node.get(), nullptr, 0); auto child1_frame = CreateNode<FrameNodeImpl>( @@ -54,7 +54,7 @@ TEST_F(PageNodeImplTest, RemoveFrame) { auto process_node = CreateNode<ProcessNodeImpl>(); - auto page_node = CreateNode<PageNodeImpl>(nullptr /*TEST*/); + auto page_node = CreateNode<PageNodeImpl>(nullptr); auto frame_node = CreateNode<FrameNodeImpl>(process_node.get(), page_node.get(), nullptr, 0); @@ -162,31 +162,6 @@ EXPECT_FALSE(page_node->is_loading()); } -TEST_F(PageNodeImplTest, OnAllFramesInPageFrozen) { - const auto kRunning = resource_coordinator::mojom::LifecycleState::kRunning; - const auto kFrozen = resource_coordinator::mojom::LifecycleState::kFrozen; - - MockSinglePageWithMultipleProcessesGraph mock_graph(graph()); - - EXPECT_EQ(kRunning, mock_graph.page->lifecycle_state()); - - // 1/2 frames in the page is frozen. Expect the page to still be running. - mock_graph.frame->SetLifecycleState(kFrozen); - EXPECT_EQ(kRunning, mock_graph.page->lifecycle_state()); - - // 2/2 frames in the process are frozen. We expect the page to be frozen. - mock_graph.child_frame->SetLifecycleState(kFrozen); - EXPECT_EQ(kFrozen, mock_graph.page->lifecycle_state()); - - // Unfreeze a frame and expect the page to be running again. - mock_graph.frame->SetLifecycleState(kRunning); - EXPECT_EQ(kRunning, mock_graph.page->lifecycle_state()); - - // Refreeze that frame and expect the page to be frozen again. - mock_graph.frame->SetLifecycleState(kFrozen); - EXPECT_EQ(kFrozen, mock_graph.page->lifecycle_state()); -} - namespace { const size_t kInterventionCount = @@ -227,7 +202,7 @@ TestNodeWrapper<ProcessNodeImpl> process = TestNodeWrapper<ProcessNodeImpl>::Create(mock_graph); TestNodeWrapper<PageNodeImpl> page = - TestNodeWrapper<PageNodeImpl>::Create(mock_graph, nullptr /*TEST*/); + TestNodeWrapper<PageNodeImpl>::Create(mock_graph, nullptr); // Check the initial values before any frames are added. EXPECT_EQ(0u, page->GetInterventionPolicyFramesReportedForTesting()); @@ -368,7 +343,7 @@ TestNodeWrapper<ProcessNodeImpl> process = TestNodeWrapper<ProcessNodeImpl>::Create(mock_graph); TestNodeWrapper<PageNodeImpl> page = - TestNodeWrapper<PageNodeImpl>::Create(mock_graph, nullptr /*TEST*/); + TestNodeWrapper<PageNodeImpl>::Create(mock_graph, nullptr); // Create two frames and immediately attach them to the page. TestNodeWrapper<FrameNodeImpl> f0 = TestNodeWrapper<FrameNodeImpl>::Create(
diff --git a/chrome/browser/performance_manager/graph/process_node_impl.cc b/chrome/browser/performance_manager/graph/process_node_impl.cc index 35803ba..9833e60 100644 --- a/chrome/browser/performance_manager/graph/process_node_impl.cc +++ b/chrome/browser/performance_manager/graph/process_node_impl.cc
@@ -20,12 +20,15 @@ } void ProcessNodeImpl::AddFrame(FrameNodeImpl* frame_node) { - const bool inserted = frame_nodes_.insert(frame_node).second; DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + const bool inserted = frame_nodes_.insert(frame_node).second; DCHECK(inserted); - if (frame_node->lifecycle_state() == - resource_coordinator::mojom::LifecycleState::kFrozen) - IncrementNumFrozenFrames(); +} + +void ProcessNodeImpl::RemoveFrame(FrameNodeImpl* frame_node) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + DCHECK(base::ContainsKey(frame_nodes_, frame_node)); + frame_nodes_.erase(frame_node); } void ProcessNodeImpl::SetCPUUsage(double cpu_usage) { @@ -67,7 +70,9 @@ } void ProcessNodeImpl::OnRendererIsBloated() { - SendEvent(resource_coordinator::mojom::Event::kRendererIsBloated); + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + for (auto& observer : observers()) + observer.OnRendererIsBloated(this); } const base::flat_set<FrameNodeImpl*>& ProcessNodeImpl::GetFrameNodes() const { @@ -79,29 +84,23 @@ // pages. However, frames are children of both processes and frames, so we // find all of the pages that are reachable from the process's child // frames. -base::flat_set<PageNodeImpl*> -ProcessNodeImpl::GetAssociatedPageCoordinationUnits() const { +base::flat_set<PageNodeImpl*> ProcessNodeImpl::GetAssociatedPageNodes() const { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); base::flat_set<PageNodeImpl*> page_nodes; - for (auto* frame_node : frame_nodes_) { - if (auto* page_node = frame_node->page_node()) - page_nodes.insert(page_node); - } + for (auto* frame_node : frame_nodes_) + page_nodes.insert(frame_node->page_node()); return page_nodes; } -void ProcessNodeImpl::OnFrameLifecycleStateChanged( - FrameNodeImpl* frame_node, - resource_coordinator::mojom::LifecycleState old_state) { - DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - DCHECK(base::ContainsKey(frame_nodes_, frame_node)); - DCHECK_NE(old_state, frame_node->lifecycle_state()); - - if (old_state == resource_coordinator::mojom::LifecycleState::kFrozen) - DecrementNumFrozenFrames(); - else if (frame_node->lifecycle_state() == - resource_coordinator::mojom::LifecycleState::kFrozen) - IncrementNumFrozenFrames(); +PageNodeImpl* ProcessNodeImpl::GetPageNodeIfExclusive() const { + PageNodeImpl* page_node = nullptr; + for (auto* frame_node : frame_nodes_) { + if (!page_node) + page_node = frame_node->page_node(); + if (page_node != frame_node->page_node()) + return nullptr; + } + return page_node; } void ProcessNodeImpl::SetProcessImpl(base::Process process, @@ -137,38 +136,4 @@ DCHECK(frame_nodes_.empty()); } -void ProcessNodeImpl::OnEventReceived( - resource_coordinator::mojom::Event event) { - DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - for (auto& observer : observers()) - observer.OnProcessEventReceived(this, event); -} - -void ProcessNodeImpl::RemoveFrame(FrameNodeImpl* frame_node) { - DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - DCHECK(base::ContainsKey(frame_nodes_, frame_node)); - frame_nodes_.erase(frame_node); - - if (frame_node->lifecycle_state() == - resource_coordinator::mojom::LifecycleState::kFrozen) - DecrementNumFrozenFrames(); -} - -void ProcessNodeImpl::DecrementNumFrozenFrames() { - DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - --num_frozen_frames_; - DCHECK_GE(num_frozen_frames_, 0); -} - -void ProcessNodeImpl::IncrementNumFrozenFrames() { - DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - ++num_frozen_frames_; - DCHECK_LE(num_frozen_frames_, static_cast<int>(frame_nodes_.size())); - - if (num_frozen_frames_ == static_cast<int>(frame_nodes_.size())) { - for (auto& observer : observers()) - observer.OnAllFramesInProcessFrozen(this); - } -} - } // namespace performance_manager
diff --git a/chrome/browser/performance_manager/graph/process_node_impl.h b/chrome/browser/performance_manager/graph/process_node_impl.h index 4751fdf6..a3bef1b 100644 --- a/chrome/browser/performance_manager/graph/process_node_impl.h +++ b/chrome/browser/performance_manager/graph/process_node_impl.h
@@ -11,6 +11,7 @@ #include "base/process/process.h" #include "base/process/process_handle.h" #include "base/time/time.h" +#include "chrome/browser/performance_manager/graph/node_attached_data.h" #include "chrome/browser/performance_manager/graph/node_base.h" #include "chrome/browser/performance_manager/graph/properties.h" #include "chrome/browser/performance_manager/observers/graph_observer.h" @@ -65,7 +66,11 @@ base::TimeDelta cumulative_cpu_usage() const { return cumulative_cpu_usage_; } const base::flat_set<FrameNodeImpl*>& GetFrameNodes() const; - base::flat_set<PageNodeImpl*> GetAssociatedPageCoordinationUnits() const; + base::flat_set<PageNodeImpl*> GetAssociatedPageNodes() const; + + // If this process is associated with only one page, returns that page. + // Otherwise, returns nullptr. + PageNodeImpl* GetPageNodeIfExclusive() const; // Use process_id() in preference to process().Pid(). It's always valid to // access, but will return kNullProcessId when the process is not valid. It @@ -91,25 +96,16 @@ // from the destructor of FrameNodeImpl. void RemoveFrame(FrameNodeImpl* frame_node); - // Invoked when the state of a frame hosted by this process changes. - void OnFrameLifecycleStateChanged( - FrameNodeImpl* frame_node, - resource_coordinator::mojom::LifecycleState old_state); - protected: void SetProcessImpl(base::Process process, base::ProcessId process_id, base::Time launch_time); private: + friend class FrozenFrameAggregatorAccess; + void LeaveGraph() override; - // CoordinationUnitInterface implementation. - void OnEventReceived(resource_coordinator::mojom::Event event) override; - - void DecrementNumFrozenFrames(); - void IncrementNumFrozenFrames(); - base::TimeDelta cumulative_cpu_usage_; uint64_t private_footprint_kb_ = 0u; @@ -129,8 +125,8 @@ base::flat_set<FrameNodeImpl*> frame_nodes_; - // The number of frames hosted by this process that are frozen. - int num_frozen_frames_ = 0; + // Inline storage for FrozenFrameAggregator user data. + InternalNodeAttachedDataStorage<sizeof(uintptr_t) + 8> frozen_frame_data_; DISALLOW_COPY_AND_ASSIGN(ProcessNodeImpl); };
diff --git a/chrome/browser/performance_manager/graph/process_node_impl_unittest.cc b/chrome/browser/performance_manager/graph/process_node_impl_unittest.cc index d0cc7fb6..8f3575c0 100644 --- a/chrome/browser/performance_manager/graph/process_node_impl_unittest.cc +++ b/chrome/browser/performance_manager/graph/process_node_impl_unittest.cc
@@ -17,22 +17,6 @@ class ProcessNodeImplTest : public GraphTestHarness {}; -class MockGraphObserver : public GraphObserver { - public: - MockGraphObserver() = default; - virtual ~MockGraphObserver() = default; - - bool ShouldObserve(const NodeBase* node) override { - return node->id().type == - resource_coordinator::CoordinationUnitType::kProcess; - } - - MOCK_METHOD1(OnAllFramesInProcessFrozen, void(ProcessNodeImpl*)); - - private: - DISALLOW_COPY_AND_ASSIGN(MockGraphObserver); -}; - } // namespace TEST_F(ProcessNodeImplTest, MeasureCPUUsage) { @@ -41,38 +25,6 @@ EXPECT_EQ(1.0, process_node->cpu_usage()); } -TEST_F(ProcessNodeImplTest, OnAllFramesInProcessFrozen) { - testing::StrictMock<MockGraphObserver> observer; - graph()->RegisterObserver(&observer); - MockMultiplePagesInSingleProcessGraph mock_graph(graph()); - - // 1/2 frame in the process is frozen. - // No call to OnAllFramesInProcessFrozen() is expected. - mock_graph.frame->SetLifecycleState( - resource_coordinator::mojom::LifecycleState::kFrozen); - - // 2/2 frames in the process are frozen. - EXPECT_CALL(observer, OnAllFramesInProcessFrozen(mock_graph.process.get())); - mock_graph.other_frame->SetLifecycleState( - resource_coordinator::mojom::LifecycleState::kFrozen); - testing::Mock::VerifyAndClear(&observer); - - // A frame is unfrozen and frozen. - mock_graph.frame->SetLifecycleState( - resource_coordinator::mojom::LifecycleState::kRunning); - EXPECT_CALL(observer, OnAllFramesInProcessFrozen(mock_graph.process.get())); - mock_graph.frame->SetLifecycleState( - resource_coordinator::mojom::LifecycleState::kFrozen); - testing::Mock::VerifyAndClear(&observer); - - // A frozen frame is frozen again. - // No call to OnAllFramesInProcessFrozen() is expected. - mock_graph.frame->SetLifecycleState( - resource_coordinator::mojom::LifecycleState::kFrozen); - - graph()->UnregisterObserver(&observer); -} - TEST_F(ProcessNodeImplTest, ProcessLifeCycle) { auto process_node = CreateNode<ProcessNodeImpl>(); @@ -124,4 +76,28 @@ EXPECT_EQ(base::TimeDelta(), process_node->cumulative_cpu_usage()); } +TEST_F(ProcessNodeImplTest, GetPageNodeIfExclusive) { + { + MockSinglePageInSingleProcessGraph g(graph()); + EXPECT_EQ(g.page.get(), g.process.get()->GetPageNodeIfExclusive()); + } + + { + MockSinglePageWithMultipleProcessesGraph g(graph()); + EXPECT_EQ(g.page.get(), g.process.get()->GetPageNodeIfExclusive()); + } + + { + MockMultiplePagesInSingleProcessGraph g(graph()); + EXPECT_FALSE(g.process.get()->GetPageNodeIfExclusive()); + } + + { + MockMultiplePagesWithMultipleProcessesGraph g(graph()); + EXPECT_FALSE(g.process.get()->GetPageNodeIfExclusive()); + EXPECT_EQ(g.other_page.get(), + g.other_process.get()->GetPageNodeIfExclusive()); + } +} + } // namespace performance_manager
diff --git a/chrome/browser/performance_manager/graph/system_node_impl.cc b/chrome/browser/performance_manager/graph/system_node_impl.cc index 997e78e..bef2d26b 100644 --- a/chrome/browser/performance_manager/graph/system_node_impl.cc +++ b/chrome/browser/performance_manager/graph/system_node_impl.cc
@@ -27,10 +27,6 @@ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); } -void SystemNodeImpl::OnProcessCPUUsageReady() { - SendEvent(resource_coordinator::mojom::Event::kProcessCPUUsageReady); -} - void SystemNodeImpl::DistributeMeasurementBatch( std::unique_ptr<ProcessResourceMeasurementBatch> measurement_batch) { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); @@ -155,14 +151,8 @@ DCHECK_EQ(last_measurement_end_time_, page->usage_estimate_time()); } - // Fire the end update signal. - OnProcessCPUUsageReady(); -} - -void SystemNodeImpl::OnEventReceived(resource_coordinator::mojom::Event event) { - DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); for (auto& observer : observers()) - observer.OnSystemEventReceived(this, event); + observer.OnProcessCPUUsageReady(this); } } // namespace performance_manager
diff --git a/chrome/browser/performance_manager/graph/system_node_impl.h b/chrome/browser/performance_manager/graph/system_node_impl.h index 7895ea7..27f62a7 100644 --- a/chrome/browser/performance_manager/graph/system_node_impl.h +++ b/chrome/browser/performance_manager/graph/system_node_impl.h
@@ -54,7 +54,6 @@ explicit SystemNodeImpl(Graph* graph); ~SystemNodeImpl() override; - void OnProcessCPUUsageReady(); void DistributeMeasurementBatch( std::unique_ptr<ProcessResourceMeasurementBatch> measurement_batch); @@ -71,9 +70,6 @@ base::TimeTicks last_measurement_start_time_; base::TimeTicks last_measurement_end_time_; - // CoordinationUnitInterface implementation: - void OnEventReceived(resource_coordinator::mojom::Event event) override; - DISALLOW_COPY_AND_ASSIGN(SystemNodeImpl); };
diff --git a/chrome/browser/performance_manager/graph/system_node_impl_unittest.cc b/chrome/browser/performance_manager/graph/system_node_impl_unittest.cc index ec3070a1..eb7a977 100644 --- a/chrome/browser/performance_manager/graph/system_node_impl_unittest.cc +++ b/chrome/browser/performance_manager/graph/system_node_impl_unittest.cc
@@ -28,10 +28,7 @@ return cu_type == resource_coordinator::CoordinationUnitType::kSystem; } - void OnSystemEventReceived( - SystemNodeImpl* system_node, - resource_coordinator::mojom::Event event) override { - EXPECT_EQ(resource_coordinator::mojom::Event::kProcessCPUUsageReady, event); + void OnProcessCPUUsageReady(SystemNodeImpl* system_node) override { ++system_event_seen_count_; } @@ -83,17 +80,6 @@ } // namespace -TEST_F(SystemNodeImplTest, OnProcessCPUUsageReady) { - SystemAndProcessObserver observer; - MockMultiplePagesWithMultipleProcessesGraph mock_graph(graph()); - mock_graph.system->AddObserver(&observer); - EXPECT_EQ(0u, observer.system_event_seen_count()); - mock_graph.system->OnProcessCPUUsageReady(); - EXPECT_EQ(1u, observer.system_event_seen_count()); - - mock_graph.system->RemoveObserver(&observer); -} - TEST_F(SystemNodeImplTest, DistributeMeasurementBatch) { SystemAndProcessObserver observer; MockMultiplePagesWithMultipleProcessesGraph mock_graph(graph());
diff --git a/chrome/browser/performance_manager/observers/graph_observer.h b/chrome/browser/performance_manager/observers/graph_observer.h index 90636130..786e58a 100644 --- a/chrome/browser/performance_manager/observers/graph_observer.h +++ b/chrome/browser/performance_manager/observers/graph_observer.h
@@ -50,43 +50,32 @@ // Called when the |node| is about to be destroyed. virtual void OnBeforeNodeRemoved(NodeBase* node) {} - // Called whenever an event is received in |node| if the - // |node| doesn't implement its own EventReceived handler. - virtual void OnEventReceived(NodeBase* node, - resource_coordinator::mojom::Event event) {} - virtual void OnFrameEventReceived(FrameNodeImpl* frame_node, - resource_coordinator::mojom::Event event) {} - virtual void OnPageEventReceived(PageNodeImpl* page_node, - resource_coordinator::mojom::Event event) {} - virtual void OnProcessEventReceived( - ProcessNodeImpl* process_node, - resource_coordinator::mojom::Event event) {} - virtual void OnSystemEventReceived(SystemNodeImpl* system_node, - resource_coordinator::mojom::Event event) { - } - // FrameNodeImpl notifications. virtual void OnIsCurrentChanged(FrameNodeImpl* frame_node) {} virtual void OnNetworkAlmostIdleChanged(FrameNodeImpl* frame_node) {} + virtual void OnLifecycleStateChanged(FrameNodeImpl* frame_node) {} + virtual void OnNonPersistentNotificationCreated(FrameNodeImpl* frame_node) {} // PageNodeImpl notifications. virtual void OnIsVisibleChanged(PageNodeImpl* page_node) {} virtual void OnIsLoadingChanged(PageNodeImpl* page_node) {} virtual void OnUkmSourceIdChanged(PageNodeImpl* page_node) {} virtual void OnLifecycleStateChanged(PageNodeImpl* page_node) {} + virtual void OnPageAlmostIdleChanged(PageNodeImpl* page_node) {} + virtual void OnFaviconUpdated(PageNodeImpl* page_node) {} + virtual void OnTitleUpdated(PageNodeImpl* page_node) {} + virtual void OnMainFrameNavigationCommitted(PageNodeImpl* page_node) {} // ProcessNodeImpl notifications. virtual void OnExpectedTaskQueueingDurationSample( ProcessNodeImpl* process_node) {} virtual void OnMainThreadTaskLoadIsLow(ProcessNodeImpl* process_node) {} - - // Called when page almost idle state changes. This is a computed property and - // will only be maintained if a PageAlmostIdleDecorator exists on the graph. - virtual void OnPageAlmostIdleChanged(PageNodeImpl* page_node) {} - - // Called when all the frames in a process become frozen. + virtual void OnRendererIsBloated(ProcessNodeImpl* process_node) {} virtual void OnAllFramesInProcessFrozen(ProcessNodeImpl* process_node) {} + // SystemNodeImpl notifications. + virtual void OnProcessCPUUsageReady(SystemNodeImpl* system_node) {} + void set_node_graph(Graph* graph) { node_graph_ = graph; } Graph* graph() const { return node_graph_; }
diff --git a/chrome/browser/performance_manager/observers/graph_observer_unittest.cc b/chrome/browser/performance_manager/observers/graph_observer_unittest.cc index 45b177e..fe6706b 100644 --- a/chrome/browser/performance_manager/observers/graph_observer_unittest.cc +++ b/chrome/browser/performance_manager/observers/graph_observer_unittest.cc
@@ -53,7 +53,7 @@ { auto process_node = CreateNode<ProcessNodeImpl>(); - auto page_node = CreateNode<PageNodeImpl>(nullptr /*TEST*/); + auto page_node = CreateNode<PageNodeImpl>(nullptr); auto root_frame_node = CreateNode<FrameNodeImpl>( process_node.get(), page_node.get(), nullptr, 0); auto frame_node = CreateNode<FrameNodeImpl>(
diff --git a/chrome/browser/performance_manager/observers/metrics_collector.cc b/chrome/browser/performance_manager/observers/metrics_collector.cc index 05220b4..cfba2e9 100644 --- a/chrome/browser/performance_manager/observers/metrics_collector.cc +++ b/chrome/browser/performance_manager/observers/metrics_collector.cc
@@ -38,10 +38,8 @@ // coordination unit. size_t GetNumCoresidentTabs(const PageNodeImpl* page_node) { std::set<NodeBase*> coresident_tabs; - for (auto* process_node : - page_node->GetAssociatedProcessCoordinationUnits()) { - for (auto* associated_page_node : - process_node->GetAssociatedPageCoordinationUnits()) { + for (auto* process_node : page_node->GetAssociatedProcessNodes()) { + for (auto* associated_page_node : process_node->GetAssociatedPageNodes()) { coresident_tabs.insert(associated_page_node); } } @@ -76,47 +74,18 @@ } } -void MetricsCollector::OnFrameEventReceived( - FrameNodeImpl* frame_node, - resource_coordinator::mojom::Event event) { - if (event == - resource_coordinator::mojom::Event::kNonPersistentNotificationCreated) { - auto* page_node = frame_node->page_node(); - // Only record metrics while it is backgrounded. - if (!page_node || page_node->is_visible() || - !ShouldReportMetrics(page_node)) { - return; - } - MetricsReportRecord& record = - metrics_report_record_map_.find(page_node->id())->second; - record.first_non_persistent_notification_created.OnSignalReceived( - frame_node->IsMainFrame(), page_node->TimeSinceLastVisibilityChange(), - graph()->ukm_recorder()); - } -} +void MetricsCollector::OnNonPersistentNotificationCreated( + FrameNodeImpl* frame_node) { + // Only record metrics while a page is backgrounded. + auto* page_node = frame_node->page_node(); + if (page_node->is_visible() || !ShouldReportMetrics(page_node)) + return; -void MetricsCollector::OnPageEventReceived( - PageNodeImpl* page_node, - resource_coordinator::mojom::Event event) { - if (event == resource_coordinator::mojom::Event::kTitleUpdated) { - // Only record metrics while it is backgrounded. - if (page_node->is_visible() || !ShouldReportMetrics(page_node)) - return; - MetricsReportRecord& record = - metrics_report_record_map_.find(page_node->id())->second; - record.first_title_updated.OnSignalReceived( - true, page_node->TimeSinceLastVisibilityChange(), - graph()->ukm_recorder()); - } else if (event == resource_coordinator::mojom::Event::kFaviconUpdated) { - // Only record metrics while it is backgrounded. - if (page_node->is_visible() || !ShouldReportMetrics(page_node)) - return; - MetricsReportRecord& record = - metrics_report_record_map_.find(page_node->id())->second; - record.first_favicon_updated.OnSignalReceived( - true, page_node->TimeSinceLastVisibilityChange(), - graph()->ukm_recorder()); - } + MetricsReportRecord& record = + metrics_report_record_map_.find(page_node->id())->second; + record.first_non_persistent_notification_created.OnSignalReceived( + frame_node->IsMainFrame(), page_node->TimeSinceLastVisibilityChange(), + graph()->ukm_recorder()); } void MetricsCollector::OnIsVisibleChanged(PageNodeImpl* page_node) { @@ -135,6 +104,28 @@ record.UpdateUkmSourceID(ukm_source_id); } +void MetricsCollector::OnFaviconUpdated(PageNodeImpl* page_node) { + // Only record metrics while it is backgrounded. + if (page_node->is_visible() || !ShouldReportMetrics(page_node)) + return; + MetricsReportRecord& record = + metrics_report_record_map_.find(page_node->id())->second; + record.first_favicon_updated.OnSignalReceived( + true, page_node->TimeSinceLastVisibilityChange(), + graph()->ukm_recorder()); +} + +void MetricsCollector::OnTitleUpdated(PageNodeImpl* page_node) { + // Only record metrics while it is backgrounded. + if (page_node->is_visible() || !ShouldReportMetrics(page_node)) + return; + MetricsReportRecord& record = + metrics_report_record_map_.find(page_node->id())->second; + record.first_title_updated.OnSignalReceived( + true, page_node->TimeSinceLastVisibilityChange(), + graph()->ukm_recorder()); +} + void MetricsCollector::OnExpectedTaskQueueingDurationSample( ProcessNodeImpl* process_node) { // Report this measurement to all pages that are hosting a main frame in
diff --git a/chrome/browser/performance_manager/observers/metrics_collector.h b/chrome/browser/performance_manager/observers/metrics_collector.h index d8dd14c..6090e87d3 100644 --- a/chrome/browser/performance_manager/observers/metrics_collector.h +++ b/chrome/browser/performance_manager/observers/metrics_collector.h
@@ -39,12 +39,11 @@ bool ShouldObserve(const NodeBase* node) override; void OnNodeAdded(NodeBase* node) override; void OnBeforeNodeRemoved(NodeBase* node) override; - void OnFrameEventReceived(FrameNodeImpl* frame_node, - resource_coordinator::mojom::Event event) override; - void OnPageEventReceived(PageNodeImpl* page_node, - resource_coordinator::mojom::Event event) override; + void OnNonPersistentNotificationCreated(FrameNodeImpl* frame_node) override; void OnIsVisibleChanged(PageNodeImpl* page_node) override; void OnUkmSourceIdChanged(PageNodeImpl* page_node) override; + void OnFaviconUpdated(PageNodeImpl* page_node) override; + void OnTitleUpdated(PageNodeImpl* page_node) override; void OnExpectedTaskQueueingDurationSample( ProcessNodeImpl* process_node) override;
diff --git a/chrome/browser/performance_manager/observers/metrics_collector_unittest.cc b/chrome/browser/performance_manager/observers/metrics_collector_unittest.cc index 0290b33..1ad012a 100644 --- a/chrome/browser/performance_manager/observers/metrics_collector_unittest.cc +++ b/chrome/browser/performance_manager/observers/metrics_collector_unittest.cc
@@ -62,7 +62,7 @@ }; TEST_F(MAYBE_MetricsCollectorTest, FromBackgroundedToFirstTitleUpdatedUMA) { - auto page_node = CreateNode<PageNodeImpl>(nullptr /*TEST*/); + auto page_node = CreateNode<PageNodeImpl>(nullptr); page_node->OnMainFrameNavigationCommitted(PerformanceManagerClock::NowTicks(), kDummyID, kDummyUrl); @@ -95,7 +95,7 @@ TEST_F(MAYBE_MetricsCollectorTest, FromBackgroundedToFirstTitleUpdatedUMA5MinutesTimeout) { - auto page_node = CreateNode<PageNodeImpl>(nullptr /*TEST*/); + auto page_node = CreateNode<PageNodeImpl>(nullptr); page_node->OnMainFrameNavigationCommitted(PerformanceManagerClock::NowTicks(), kDummyID, kDummyUrl); @@ -114,7 +114,7 @@ TEST_F(MAYBE_MetricsCollectorTest, FromBackgroundedToFirstNonPersistentNotificationCreatedUMA) { auto process_node = CreateNode<ProcessNodeImpl>(); - auto page_node = CreateNode<PageNodeImpl>(nullptr /*TEST*/); + auto page_node = CreateNode<PageNodeImpl>(nullptr); auto frame_node = CreateNode<FrameNodeImpl>(process_node.get(), page_node.get(), nullptr, 0); @@ -151,7 +151,7 @@ MAYBE_MetricsCollectorTest, FromBackgroundedToFirstNonPersistentNotificationCreatedUMA5MinutesTimeout) { auto process_node = CreateNode<ProcessNodeImpl>(); - auto page_node = CreateNode<PageNodeImpl>(nullptr /*TEST*/); + auto page_node = CreateNode<PageNodeImpl>(nullptr); auto frame_node = CreateNode<FrameNodeImpl>(process_node.get(), page_node.get(), nullptr, 0); @@ -170,7 +170,7 @@ } TEST_F(MAYBE_MetricsCollectorTest, FromBackgroundedToFirstFaviconUpdatedUMA) { - auto page_node = CreateNode<PageNodeImpl>(nullptr /*TEST*/); + auto page_node = CreateNode<PageNodeImpl>(nullptr); page_node->OnMainFrameNavigationCommitted(PerformanceManagerClock::NowTicks(), kDummyID, kDummyUrl); @@ -203,7 +203,7 @@ TEST_F(MAYBE_MetricsCollectorTest, FromBackgroundedToFirstFaviconUpdatedUMA5MinutesTimeout) { - auto page_node = CreateNode<PageNodeImpl>(nullptr /*TEST*/); + auto page_node = CreateNode<PageNodeImpl>(nullptr); page_node->OnMainFrameNavigationCommitted(PerformanceManagerClock::NowTicks(), kDummyID, kDummyUrl); @@ -222,7 +222,7 @@ // Flaky test: https://crbug.com/833028 TEST_F(MAYBE_MetricsCollectorTest, ResponsivenessMetric) { auto process_node = CreateNode<ProcessNodeImpl>(); - auto page_node = CreateNode<PageNodeImpl>(nullptr /*TEST*/); + auto page_node = CreateNode<PageNodeImpl>(nullptr); auto frame_node = CreateNode<FrameNodeImpl>(process_node.get(), page_node.get(), nullptr, 0);
diff --git a/chrome/browser/performance_manager/observers/page_signal_generator_impl.cc b/chrome/browser/performance_manager/observers/page_signal_generator_impl.cc index de8a230..e7856db31 100644 --- a/chrome/browser/performance_manager/observers/page_signal_generator_impl.cc +++ b/chrome/browser/performance_manager/observers/page_signal_generator_impl.cc
@@ -85,13 +85,8 @@ DCHECK_EQ(1u, count); // This should always erase exactly one CU. } -void PageSignalGeneratorImpl::OnFrameEventReceived( - FrameNodeImpl* frame_node, - resource_coordinator::mojom::Event event) { - if (event != - resource_coordinator::mojom::Event::kNonPersistentNotificationCreated) - return; - +void PageSignalGeneratorImpl::OnNonPersistentNotificationCreated( + FrameNodeImpl* frame_node) { auto* page_node = frame_node->page_node(); if (!page_node) return; @@ -101,57 +96,6 @@ NotifyNonPersistentNotificationCreated); } -void PageSignalGeneratorImpl::OnProcessEventReceived( - ProcessNodeImpl* process_node, - resource_coordinator::mojom::Event event) { - if (event == resource_coordinator::mojom::Event::kRendererIsBloated) { - base::flat_set<PageNodeImpl*> page_nodes = - process_node->GetAssociatedPageCoordinationUnits(); - // Currently bloated renderer handling supports only a single page. - if (page_nodes.size() == 1u) { - auto* page_node = *page_nodes.begin(); - DispatchPageSignal(page_node, - &resource_coordinator::mojom::PageSignalReceiver:: - NotifyRendererIsBloated); - RecordBloatedRendererHandling( - BloatedRendererHandlingInResourceCoordinator::kForwardedToBrowser); - } else { - RecordBloatedRendererHandling( - BloatedRendererHandlingInResourceCoordinator:: - kIgnoredDueToMultiplePages); - } - } -} - -void PageSignalGeneratorImpl::OnSystemEventReceived( - SystemNodeImpl* system_node, - resource_coordinator::mojom::Event event) { - if (event == resource_coordinator::mojom::Event::kProcessCPUUsageReady) { - base::TimeTicks measurement_start = - system_node->last_measurement_start_time(); - - for (auto& entry : page_data_) { - const PageNodeImpl* page = entry.first; - PageData* data = &entry.second; - // TODO(siggi): Figure "recency" here, to avoid firing a measurement event - // for state transitions that happened "too long" before a - // measurement started. Alternatively perhaps this bit of policy is - // better done in the observer, in which case it needs the time stamps - // involved. - if (page->page_almost_idle() && !data->performance_estimate_issued && - data->last_state_change < measurement_start) { - DispatchPageSignal(page, - &resource_coordinator::mojom::PageSignalReceiver:: - OnLoadTimePerformanceEstimate, - page->TimeSinceLastNavigation(), - page->cumulative_cpu_usage_estimate(), - page->private_footprint_kb_estimate()); - data->performance_estimate_issued = true; - } - } - } -} - void PageSignalGeneratorImpl::OnPageAlmostIdleChanged(PageNodeImpl* page_node) { GetPageData(page_node)->Reset(); @@ -187,6 +131,48 @@ } } +void PageSignalGeneratorImpl::OnRendererIsBloated( + ProcessNodeImpl* process_node) { + // Currently bloated renderer handling supports only a single page. + auto* page_node = process_node->GetPageNodeIfExclusive(); + if (page_node) { + DispatchPageSignal(page_node, + &resource_coordinator::mojom::PageSignalReceiver:: + NotifyRendererIsBloated); + RecordBloatedRendererHandling( + BloatedRendererHandlingInResourceCoordinator::kForwardedToBrowser); + } else { + RecordBloatedRendererHandling(BloatedRendererHandlingInResourceCoordinator:: + kIgnoredDueToMultiplePages); + } +} + +void PageSignalGeneratorImpl::OnProcessCPUUsageReady( + SystemNodeImpl* system_node) { + base::TimeTicks measurement_start = + system_node->last_measurement_start_time(); + + for (auto& entry : page_data_) { + const PageNodeImpl* page = entry.first; + PageData* data = &entry.second; + // TODO(siggi): Figure "recency" here, to avoid firing a measurement event + // for state transitions that happened "too long" before a + // measurement started. Alternatively perhaps this bit of policy is + // better done in the observer, in which case it needs the time stamps + // involved. + if (page->page_almost_idle() && !data->performance_estimate_issued && + data->last_state_change < measurement_start) { + DispatchPageSignal(page, + &resource_coordinator::mojom::PageSignalReceiver:: + OnLoadTimePerformanceEstimate, + page->TimeSinceLastNavigation(), + page->cumulative_cpu_usage_estimate(), + page->private_footprint_kb_estimate()); + data->performance_estimate_issued = true; + } + } +} + void PageSignalGeneratorImpl::BindToInterface( resource_coordinator::mojom::PageSignalGeneratorRequest request, const service_manager::BindSourceInfo& source_info) {
diff --git a/chrome/browser/performance_manager/observers/page_signal_generator_impl.h b/chrome/browser/performance_manager/observers/page_signal_generator_impl.h index 2b93910..9e5fe4ff 100644 --- a/chrome/browser/performance_manager/observers/page_signal_generator_impl.h +++ b/chrome/browser/performance_manager/observers/page_signal_generator_impl.h
@@ -23,7 +23,7 @@ // The PageSignalGenerator is a dedicated |GraphObserver| for // calculating and emitting page-scoped signals. This observer observes -// PageCoordinationUnits, ProcessCoordinationUnits and FrameNodes, +// PageNodes, ProcessNodes and FrameNodes, // combining information from the graph to generate page level signals. class PageSignalGeneratorImpl : public GraphObserver, @@ -40,17 +40,13 @@ bool ShouldObserve(const NodeBase* node) override; void OnNodeAdded(NodeBase* node) override; void OnBeforeNodeRemoved(NodeBase* node) override; - void OnFrameEventReceived(FrameNodeImpl* frame_node, - resource_coordinator::mojom::Event event) override; - void OnProcessEventReceived( - ProcessNodeImpl* page_node, - resource_coordinator::mojom::Event event) override; - void OnSystemEventReceived(SystemNodeImpl* system_node, - resource_coordinator::mojom::Event event) override; + void OnNonPersistentNotificationCreated(FrameNodeImpl* frame_node) override; void OnPageAlmostIdleChanged(PageNodeImpl* page_node) override; void OnLifecycleStateChanged(PageNodeImpl* page_node) override; void OnExpectedTaskQueueingDurationSample( ProcessNodeImpl* process_node) override; + void OnRendererIsBloated(ProcessNodeImpl* process_node) override; + void OnProcessCPUUsageReady(SystemNodeImpl* system_node) override; void BindToInterface( resource_coordinator::mojom::PageSignalGeneratorRequest request,
diff --git a/chrome/browser/performance_manager/observers/page_signal_generator_impl_unittest.cc b/chrome/browser/performance_manager/observers/page_signal_generator_impl_unittest.cc index 83ddb6c..6a37eea 100644 --- a/chrome/browser/performance_manager/observers/page_signal_generator_impl_unittest.cc +++ b/chrome/browser/performance_manager/observers/page_signal_generator_impl_unittest.cc
@@ -201,9 +201,7 @@ // Send a // resource_coordinator::mojom::Event::kNonPersistentNotificationCreated event // and wait for the receiver to get it. - page_signal_generator()->OnFrameEventReceived( - frame_node, - resource_coordinator::mojom::Event::kNonPersistentNotificationCreated); + page_signal_generator()->OnNonPersistentNotificationCreated(frame_node); run_loop.Run(); ::testing::Mock::VerifyAndClear(&mock_receiver);
diff --git a/chrome/browser/performance_manager/performance_manager.cc b/chrome/browser/performance_manager/performance_manager.cc index 593a0eae..70a6ac1 100644 --- a/chrome/browser/performance_manager/performance_manager.cc +++ b/chrome/browser/performance_manager/performance_manager.cc
@@ -15,6 +15,7 @@ #include "base/task/post_task.h" #include "base/task/task_traits.h" #include "build/build_config.h" +#include "chrome/browser/performance_manager/decorators/frozen_frame_aggregator.h" #include "chrome/browser/performance_manager/decorators/page_almost_idle_decorator.h" #include "chrome/browser/performance_manager/graph/page_node_impl.h" #include "chrome/browser/performance_manager/graph/system_node_impl.h" @@ -261,6 +262,7 @@ RegisterObserver(std::make_unique<MetricsCollector>()); RegisterObserver(std::make_unique<PageAlmostIdleDecorator>()); + RegisterObserver(std::make_unique<FrozenFrameAggregator>()); #if defined(OS_WIN) if (base::FeatureList::IsEnabled(features::kEmptyWorkingSet))
diff --git a/chrome/browser/performance_manager/performance_manager_tab_helper.cc b/chrome/browser/performance_manager/performance_manager_tab_helper.cc index fd259ef..827b3fd 100644 --- a/chrome/browser/performance_manager/performance_manager_tab_helper.cc +++ b/chrome/browser/performance_manager/performance_manager_tab_helper.cc
@@ -56,10 +56,9 @@ std::vector<content::RenderFrameHost*> existing_frames = web_contents->GetAllFrames(); for (content::RenderFrameHost* frame : existing_frames) { - // Only send notifications for live frames, the non-live ones will generate - // creation notifications when animated. - // TODO(siggi): Add an IsRenderFrameCreated accessor to WebContents. - if (frame->IsRenderFrameLive()) + // Only send notifications for created frames, the others will generate + // creation notifications in due course (or not at all). + if (frame->IsRenderFrameCreated()) RenderFrameCreated(frame); } @@ -134,17 +133,10 @@ void PerformanceManagerTabHelper::RenderFrameDeleted( content::RenderFrameHost* render_frame_host) { auto it = frames_.find(render_frame_host); - // Apparently there exists a condition where the construction-time iteration - // fails to turn up every frame that has been created, and for which there - // will be an enventual deletion notification. This is because - // IsRenderFrameLive() will return false if the associated process is dead - // at the time of query. The process can however be later resurrected, so - // the condition can't be tested here. - // See https://crbug.com/948088. - if (it != frames_.end()) { - performance_manager_->DeleteNode(std::move(it->second)); - frames_.erase(it); - } + DCHECK(it != frames_.end()); + + performance_manager_->DeleteNode(std::move(it->second)); + frames_.erase(it); } void PerformanceManagerTabHelper::RenderFrameHostChanged(
diff --git a/chrome/browser/performance_manager/performance_manager_unittest.cc b/chrome/browser/performance_manager/performance_manager_unittest.cc index 1c48c18d..8be3fcb6 100644 --- a/chrome/browser/performance_manager/performance_manager_unittest.cc +++ b/chrome/browser/performance_manager/performance_manager_unittest.cc
@@ -57,7 +57,7 @@ performance_manager()->CreateProcessNode(); EXPECT_NE(nullptr, process_node.get()); std::unique_ptr<PageNodeImpl> page_node = - performance_manager()->CreatePageNode(nullptr /*TEST*/); + performance_manager()->CreatePageNode(nullptr); EXPECT_NE(nullptr, page_node.get()); // Create a node of each type. @@ -76,7 +76,7 @@ std::unique_ptr<ProcessNodeImpl> process_node = performance_manager()->CreateProcessNode(); std::unique_ptr<PageNodeImpl> page_node = - performance_manager()->CreatePageNode(nullptr /*TEST*/); + performance_manager()->CreatePageNode(nullptr); std::unique_ptr<FrameNodeImpl> parent1_frame = performance_manager()->CreateFrameNode(process_node.get(), @@ -113,7 +113,7 @@ TEST_F(PerformanceManagerTest, CallOnGraph) { // Create a page node for something to target. std::unique_ptr<PageNodeImpl> page_node = - performance_manager()->CreatePageNode(nullptr /*TEST*/); + performance_manager()->CreatePageNode(nullptr); PerformanceManager::GraphCallback graph_callback = base::BindLambdaForTesting( [&page_node](Graph* graph) { EXPECT_EQ(page_node->graph(), graph); });
diff --git a/chrome/browser/prefs/browser_prefs.cc b/chrome/browser/prefs/browser_prefs.cc index 6028c14..e02aac7 100644 --- a/chrome/browser/prefs/browser_prefs.cc +++ b/chrome/browser/prefs/browser_prefs.cc
@@ -224,7 +224,7 @@ #include "chrome/browser/ui/startup/startup_browser_creator.h" #include "chrome/browser/ui/webui/foreign_session_handler.h" #include "chrome/browser/ui/webui/history_ui.h" -#include "chrome/browser/ui/webui/settings/md_settings_ui.h" +#include "chrome/browser/ui/webui/settings/settings_ui.h" #include "chrome/browser/upgrade_detector/upgrade_detector.h" #endif // defined(OS_ANDROID) @@ -266,10 +266,10 @@ #include "chrome/browser/chromeos/policy/auto_enrollment_client_impl.h" #include "chrome/browser/chromeos/policy/browser_policy_connector_chromeos.h" #include "chrome/browser/chromeos/policy/device_cloud_policy_manager_chromeos.h" -#include "chrome/browser/chromeos/policy/device_status_collector.h" #include "chrome/browser/chromeos/policy/device_wallpaper_image_handler.h" #include "chrome/browser/chromeos/policy/dm_token_storage.h" #include "chrome/browser/chromeos/policy/policy_cert_service_factory.h" +#include "chrome/browser/chromeos/policy/status_collector/device_status_collector.h" #include "chrome/browser/chromeos/power/auto_screen_brightness/metrics_reporter.h" #include "chrome/browser/chromeos/power/power_metrics_reporter.h" #include "chrome/browser/chromeos/preferences.h" @@ -847,7 +847,7 @@ #if !defined(OS_ANDROID) HistoryUI::RegisterProfilePrefs(registry); - settings::MdSettingsUI::RegisterProfilePrefs(registry); + settings::SettingsUI::RegisterProfilePrefs(registry); #endif #if BUILDFLAG(ENABLE_OFFLINE_PAGES)
diff --git a/chrome/browser/previews/hints_fetcher_browsertest.cc b/chrome/browser/previews/hints_fetcher_browsertest.cc index 4f531ab..fa3a735 100644 --- a/chrome/browser/previews/hints_fetcher_browsertest.cc +++ b/chrome/browser/previews/hints_fetcher_browsertest.cc
@@ -15,6 +15,7 @@ #include "base/test/scoped_feature_list.h" #include "build/build_config.h" #include "chrome/browser/browser_process.h" +#include "chrome/browser/engagement/site_engagement_service.h" #include "chrome/browser/metrics/subprocess_metrics_provider.h" #include "chrome/browser/previews/previews_service.h" #include "chrome/browser/previews/previews_service_factory.h" @@ -111,6 +112,8 @@ // only provides the URL. cmd->AppendSwitchASCII(previews::switches::kOptimizationGuideServiceURL, https_server_->base_url().spec()); + cmd->AppendSwitchASCII(previews::switches::kFetchHintsOverride, + "example1.com, example2.com"); } // Creates hint data for the |hint_setup_url|'s so that OnHintsUpdated in @@ -138,6 +141,27 @@ run_loop.Run(); } + // Seeds the Site Engagement Service with two HTTP and two HTTPS sites for the + // current profile. + void SeedSiteEngagementService() { + SiteEngagementService* service = SiteEngagementService::Get( + Profile::FromBrowserContext(browser() + ->tab_strip_model() + ->GetActiveWebContents() + ->GetBrowserContext())); + GURL https_url1("https://images.google.com/"); + service->AddPointsForTesting(https_url1, 15); + + GURL https_url2("https://news.google.com/"); + service->AddPointsForTesting(https_url2, 3); + + GURL http_url1("http://photos.google.com/"); + service->AddPointsForTesting(http_url1, 21); + + GURL http_url2("http://maps.google.com/"); + service->AddPointsForTesting(http_url2, 2); + } + const GURL& https_url() const { return https_url_; } const base::HistogramTester* GetHistogramTester() { return &histogram_tester_; @@ -224,3 +248,34 @@ histogram_tester->ExpectTotalCount( "Previews.HintsFetcher.GetHintsRequest.HostCount", 0); } + +// This test creates a new browser and seeds the Site Engagement Service with +// both HTTP and HTTPS sites. The test confirms that PreviewsTopHostProviderImpl +// used by PreviewsOptimizationGuide to provide a list of hosts to HintsFetcher +// only returns HTTPS-schemed hosts. We verify this with the UMA histogram +// logged when the GetHintsRequest is made to the remote Optimization Guide +// Service. +IN_PROC_BROWSER_TEST_F(OptimizationGuideServiceHintsFetcherBrowserTest, + PreviewsTopHostProviderHTTPSOnly) { + const base::HistogramTester* histogram_tester = GetHistogramTester(); + + // Adds two HTTP and two HTTPS sites into the Site Engagement Service. + SeedSiteEngagementService(); + + // This forces the hint cache to be initialized and hints to be fetched. + // Whitelist NoScript for https_url()'s' host. + SetUpComponentUpdateHints(https_url()); + + // Expect that the browser initialization will record at least one sample as + // Hints Fetching is enabled. This also ensures that the histograms have been + // updated to verify the correct number of hosts that hints will be requested + // for. + EXPECT_GE(RetryForHistogramUntilCountReached( + histogram_tester, + "Previews.HintsFetcher.GetHintsRequest.HostCount", 1), + 1); + + // Only the 2 HTTPS hosts should be requested hints for. + histogram_tester->ExpectBucketCount( + "Previews.HintsFetcher.GetHintsRequest.HostCount", 2, 1); +}
diff --git a/chrome/browser/previews/previews_service.cc b/chrome/browser/previews/previews_service.cc index ef10bf2..2fbf933 100644 --- a/chrome/browser/previews/previews_service.cc +++ b/chrome/browser/previews/previews_service.cc
@@ -142,7 +142,8 @@ profile_path.Append(chrome::kPreviewsOptOutDBFilename)), optimization_guide_service ? std::make_unique<previews::PreviewsOptimizationGuide>( - optimization_guide_service, ui_task_runner, profile_path, + optimization_guide_service, ui_task_runner, + background_task_runner, profile_path, previews_top_host_provider_.get(), previews_url_loader_factory_) : nullptr, base::Bind(&IsPreviewsTypeEnabled),
diff --git a/chrome/browser/previews/previews_top_host_provider_impl.cc b/chrome/browser/previews/previews_top_host_provider_impl.cc index 673e7dd7..cbcd9dc1 100644 --- a/chrome/browser/previews/previews_top_host_provider_impl.cc +++ b/chrome/browser/previews/previews_top_host_provider_impl.cc
@@ -33,15 +33,21 @@ SiteEngagementService::Get(profile); // Create a vector of the top hosts by engagement score up to |max_sites| - // size. Currently utilizes just the first |max_sites| entries. - // TODO(crbug.com/932707): Select TOP HTTPS hosts from site engagement. + // size. Currently utilizes just the first |max_sites| entries. Only HTTPS + // schemed hosts are included. + // + // TODO(mcrouse): Filter to only include Top hosts since Data Saver was + // enabled. std::vector<mojom::SiteEngagementDetails> engagement_details = engagement_service->GetAllDetails(); for (const auto& detail : engagement_details) { - if (top_hosts.size() <= max_sites) - top_hosts.push_back(detail.origin.host()); - else + if (top_hosts.size() <= max_sites) { + if (detail.origin.SchemeIs(url::kHttpsScheme)) { + top_hosts.push_back(detail.origin.host()); + } + } else { break; + } } return top_hosts;
diff --git a/chrome/browser/profiling_host/BUILD.gn b/chrome/browser/profiling_host/BUILD.gn index 9fd1b0b4..5c73c19 100644 --- a/chrome/browser/profiling_host/BUILD.gn +++ b/chrome/browser/profiling_host/BUILD.gn
@@ -19,6 +19,7 @@ "//chrome/common:non_code_constants", "//components/heap_profiling", "//components/services/heap_profiling/public/cpp", + "//components/signin/core/browser", "//content/public/browser", "//content/public/common", ]
diff --git a/chrome/browser/resource_coordinator/local_site_characteristics_data_store.cc b/chrome/browser/resource_coordinator/local_site_characteristics_data_store.cc index 1003acc..42a6c36 100644 --- a/chrome/browser/resource_coordinator/local_site_characteristics_data_store.cc +++ b/chrome/browser/resource_coordinator/local_site_characteristics_data_store.cc
@@ -14,6 +14,7 @@ #include "chrome/browser/resource_coordinator/local_site_characteristics_data_writer.h" #include "chrome/browser/resource_coordinator/tab_manager_features.h" #include "components/history/core/browser/history_service.h" +#include "components/history/core/browser/url_row.h" namespace resource_coordinator { @@ -22,6 +23,13 @@ constexpr char kSiteCharacteristicsDirectoryName[] = "Site Characteristics Database"; +size_t CountOriginsInURLRows(const history::URLRows& rows) { + std::set<url::Origin> origins; + for (auto& row : rows) + origins.insert(url::Origin::Create(row.url())); + return origins.size(); +} + } // namespace LocalSiteCharacteristicsDataStore::LocalSiteCharacteristicsDataStore( @@ -155,17 +163,28 @@ data.second->ClearObservationsAndInvalidateReadOperation(); database_->ClearDatabase(); } else { - std::vector<url::Origin> entries_to_remove; - for (auto deleted_row : deletion_info.deleted_rows()) { - url::Origin origin = url::Origin::Create(deleted_row.url()); - auto map_iter = origin_data_map_.find(origin); - if (map_iter != origin_data_map_.end()) - map_iter->second->ClearObservationsAndInvalidateReadOperation(); + std::vector<url::Origin> origins_to_remove; - // The database will ignore the entries that don't exist in it. - entries_to_remove.emplace_back(origin); + DCHECK_EQ(deletion_info.deleted_urls_origin_map().size(), + CountOriginsInURLRows(deletion_info.deleted_rows())); + for (const auto& it : deletion_info.deleted_urls_origin_map()) { + const url::Origin origin = url::Origin::Create(it.first); + const int remaining_visits_in_history = it.second.first; + + // If the origin no longer exists in history, clear the site + // characteristics from memory and from the database. + DCHECK_GE(remaining_visits_in_history, 0); + if (remaining_visits_in_history == 0) { + auto map_iter = origin_data_map_.find(origin); + if (map_iter != origin_data_map_.end()) + map_iter->second->ClearObservationsAndInvalidateReadOperation(); + + origins_to_remove.emplace_back(origin); + } } - database_->RemoveSiteCharacteristicsFromDB(entries_to_remove); + + if (!origins_to_remove.empty()) + database_->RemoveSiteCharacteristicsFromDB(origins_to_remove); } }
diff --git a/chrome/browser/resource_coordinator/local_site_characteristics_data_store.h b/chrome/browser/resource_coordinator/local_site_characteristics_data_store.h index 9021973a..b4a3ae2 100644 --- a/chrome/browser/resource_coordinator/local_site_characteristics_data_store.h +++ b/chrome/browser/resource_coordinator/local_site_characteristics_data_store.h
@@ -72,7 +72,11 @@ private: FRIEND_TEST_ALL_PREFIXES(LocalSiteCharacteristicsDataStoreTest, EndToEnd); FRIEND_TEST_ALL_PREFIXES(LocalSiteCharacteristicsDataStoreTest, - HistoryServiceObserver); + OnURLsDeleted_Partial_OriginNotReferenced); + FRIEND_TEST_ALL_PREFIXES(LocalSiteCharacteristicsDataStoreTest, + OnURLsDeleted_Partial_OriginStillReferenced); + FRIEND_TEST_ALL_PREFIXES(LocalSiteCharacteristicsDataStoreTest, + OnURLsDeleted_Full); // Returns a pointer to the LocalSiteCharacteristicsDataImpl object // associated with |origin|, create one and add it to |origin_data_map_|
diff --git a/chrome/browser/resource_coordinator/local_site_characteristics_data_store_unittest.cc b/chrome/browser/resource_coordinator/local_site_characteristics_data_store_unittest.cc index b7903e786..0510fec 100644 --- a/chrome/browser/resource_coordinator/local_site_characteristics_data_store_unittest.cc +++ b/chrome/browser/resource_coordinator/local_site_characteristics_data_store_unittest.cc
@@ -28,6 +28,8 @@ const url::Origin kTestOrigin2 = url::Origin::Create(GURL("http://www.bar.com")); +constexpr base::TimeDelta kDelay = base::TimeDelta::FromMinutes(1); + class MockLocalSiteCharacteristicsDatabase : public testing::NoopLocalSiteCharacteristicsDatabase { public: @@ -45,13 +47,16 @@ } // namespace class LocalSiteCharacteristicsDataStoreTest : public ::testing::Test { - public: + protected: LocalSiteCharacteristicsDataStoreTest() : scoped_set_tick_clock_for_testing_(&test_clock_) { scoped_feature_list_.InitAndEnableFeature( features::kSiteCharacteristicsDatabase); data_store_ = std::make_unique<LocalSiteCharacteristicsDataStore>(&profile_); + mock_db_ = + new ::testing::StrictMock<MockLocalSiteCharacteristicsDatabase>(); + data_store_->SetDatabaseForTesting(base::WrapUnique(mock_db_)); test_clock_.SetNowTicks(base::TimeTicks::UnixEpoch()); test_clock_.Advance(base::TimeDelta::FromHours(1)); WaitForAsyncOperationsToComplete(); @@ -63,13 +68,76 @@ test_browser_thread_bundle_.RunUntilIdle(); } - protected: + // Populates |writer_|, |reader_| and |data_| to refer to a tab navigated to + // |kTestOrigin| that updated its title in background. Populates |writer2_|, + // |reader2_| and |data2_| to refer to a tab navigated to |kTestOrigin2| that + // updates its favicon in background. + void SetupTwoSitesUsingFeaturesInBackground() { + // Load a first origin, and then make use of a feature on it. + ASSERT_FALSE(reader_); + reader_ = data_store_->GetReaderForOrigin(kTestOrigin); + EXPECT_TRUE(reader_); + + ASSERT_FALSE(writer_); + writer_ = data_store_->GetWriterForOrigin(kTestOrigin, + TabVisibility::kBackground); + EXPECT_TRUE(writer_); + + ASSERT_FALSE(data_); + data_ = + data_store_->origin_data_map_for_testing().find(kTestOrigin)->second; + EXPECT_TRUE(data_); + + EXPECT_EQ(SiteFeatureUsage::kSiteFeatureUsageUnknown, + reader_->UpdatesTitleInBackground()); + writer_->NotifySiteLoaded(); + writer_->NotifyUpdatesTitleInBackground(); + EXPECT_EQ(SiteFeatureUsage::kSiteFeatureInUse, + reader_->UpdatesTitleInBackground()); + test_clock_.Advance(kDelay); + + // Load a second origin, make use of a feature on it too. + ASSERT_FALSE(reader2_); + reader2_ = data_store_->GetReaderForOrigin(kTestOrigin2); + EXPECT_TRUE(reader2_); + + ASSERT_FALSE(writer2_); + writer2_ = data_store_->GetWriterForOrigin(kTestOrigin2, + TabVisibility::kBackground); + EXPECT_TRUE(writer2_); + + ASSERT_FALSE(data2_); + data2_ = + data_store_->origin_data_map_for_testing().find(kTestOrigin2)->second; + EXPECT_TRUE(data2_); + + EXPECT_EQ(SiteFeatureUsage::kSiteFeatureUsageUnknown, + reader2_->UpdatesFaviconInBackground()); + writer2_->NotifySiteLoaded(); + writer2_->NotifyUpdatesFaviconInBackground(); + EXPECT_EQ(SiteFeatureUsage::kSiteFeatureInUse, + reader2_->UpdatesFaviconInBackground()); + test_clock_.Advance(kDelay); + } + base::SimpleTestTickClock test_clock_; ScopedSetTickClockForTesting scoped_set_tick_clock_for_testing_; content::TestBrowserThreadBundle test_browser_thread_bundle_; base::test::ScopedFeatureList scoped_feature_list_; TestingProfile profile_; + + // Owned by |data_store_|. + ::testing::StrictMock<MockLocalSiteCharacteristicsDatabase>* mock_db_ = + nullptr; std::unique_ptr<LocalSiteCharacteristicsDataStore> data_store_; + + std::unique_ptr<SiteCharacteristicsDataReader> reader_; + std::unique_ptr<SiteCharacteristicsDataWriter> writer_; + internal::LocalSiteCharacteristicsDataImpl* data_ = nullptr; + + std::unique_ptr<SiteCharacteristicsDataReader> reader2_; + std::unique_ptr<SiteCharacteristicsDataWriter> writer2_; + internal::LocalSiteCharacteristicsDataImpl* data2_ = nullptr; }; TEST_F(LocalSiteCharacteristicsDataStoreTest, EndToEnd) { @@ -105,51 +173,19 @@ writer.reset(); EXPECT_TRUE(data_store_->origin_data_map_for_testing().empty()); + EXPECT_CALL(*mock_db_, ClearDatabase()); data_store_->OnURLsDeleted(nullptr, history::DeletionInfo::ForAllHistory()); } -TEST_F(LocalSiteCharacteristicsDataStoreTest, HistoryServiceObserver) { - // Mock the database to ensure that the delete events get propagated properly. - ::testing::StrictMock<MockLocalSiteCharacteristicsDatabase>* mock_db = - new ::testing::StrictMock<MockLocalSiteCharacteristicsDatabase>(); - data_store_->SetDatabaseForTesting(base::WrapUnique(mock_db)); +// Verify that an origin is removed from the data store (in memory and on disk) +// when there are no more references to it in the history, after the history is +// partially cleared. +TEST_F(LocalSiteCharacteristicsDataStoreTest, + OnURLsDeleted_Partial_OriginNotReferenced) { + SetupTwoSitesUsingFeaturesInBackground(); - // Load a first origin, and then make use of a feature on it. - - auto reader = data_store_->GetReaderForOrigin(kTestOrigin); - EXPECT_TRUE(reader); - - auto writer = - data_store_->GetWriterForOrigin(kTestOrigin, TabVisibility::kBackground); - EXPECT_TRUE(writer); - - internal::LocalSiteCharacteristicsDataImpl* data = - data_store_->origin_data_map_for_testing().find(kTestOrigin)->second; - EXPECT_TRUE(data); - - constexpr base::TimeDelta kDelay = base::TimeDelta::FromHours(1); - - EXPECT_EQ(SiteFeatureUsage::kSiteFeatureUsageUnknown, - reader->UpdatesTitleInBackground()); - writer->NotifySiteLoaded(); - base::TimeDelta last_loaded_time = data->last_loaded_time_for_testing(); - writer->NotifyUpdatesTitleInBackground(); - EXPECT_EQ(SiteFeatureUsage::kSiteFeatureInUse, - reader->UpdatesTitleInBackground()); - test_clock_.Advance(kDelay); - - // Load a second origin, make use of a feature on it too. - auto reader2 = data_store_->GetReaderForOrigin(kTestOrigin2); - EXPECT_TRUE(reader2); - auto writer2 = - data_store_->GetWriterForOrigin(kTestOrigin2, TabVisibility::kBackground); - EXPECT_TRUE(writer2); - writer2->NotifySiteLoaded(); - writer2->NotifyUpdatesFaviconInBackground(); - - // This site hasn'be been unloaded yet, so the last loaded time shouldn't have - // changed. - EXPECT_EQ(data->last_loaded_time_for_testing(), last_loaded_time); + const base::TimeDelta last_loaded_time2_before_urls_deleted = + data2_->last_loaded_time_for_testing(); // Make sure that all data passed to |OnURLsDeleted| get passed to the // database, even if they're not in the internal map used by the data store. @@ -157,45 +193,97 @@ url::Origin::Create(GURL("http://www.url-not-in-map.com")); history::URLRows urls_to_delete = {history::URLRow(kTestOrigin.GetURL()), history::URLRow(kOriginNotInMap.GetURL())}; - EXPECT_CALL(*mock_db, - RemoveSiteCharacteristicsFromDB(::testing::ContainerEq( - std::vector<url::Origin>({kTestOrigin, kOriginNotInMap})))); - data_store_->OnURLsDeleted(nullptr, history::DeletionInfo::ForUrls( - urls_to_delete, std::set<GURL>())); - ::testing::Mock::VerifyAndClear(mock_db); + history::DeletionInfo deletion_info = + history::DeletionInfo::ForUrls(urls_to_delete, std::set<GURL>()); + deletion_info.set_deleted_urls_origin_map({ + {kTestOrigin.GetURL(), {0, base::Time::Now()}}, + {kOriginNotInMap.GetURL(), {0, base::Time::Now()}}, + }); + EXPECT_CALL(*mock_db_, + RemoveSiteCharacteristicsFromDB(::testing::WhenSorted( + ::testing::ElementsAre(kTestOrigin, kOriginNotInMap)))); + data_store_->OnURLsDeleted(nullptr, deletion_info); + ::testing::Mock::VerifyAndClear(mock_db_); - // The information for this site have been reset, so the last loaded time - // should now be equal to the current time and the title update feature - // observation should have been cleared. - EXPECT_NE(data->last_loaded_time_for_testing(), last_loaded_time); - EXPECT_EQ(data->last_loaded_time_for_testing(), + // The information for the first site should have been cleared. The last + // loaded time should be equal to the current time. + EXPECT_EQ(data_->last_loaded_time_for_testing(), test_clock_.NowTicks() - base::TimeTicks::UnixEpoch()); EXPECT_EQ(SiteFeatureUsage::kSiteFeatureUsageUnknown, - reader->UpdatesTitleInBackground()); + reader_->UpdatesTitleInBackground()); // The second site shouldn't have been cleared. + EXPECT_EQ(data2_->last_loaded_time_for_testing(), + last_loaded_time2_before_urls_deleted); EXPECT_EQ(SiteFeatureUsage::kSiteFeatureInUse, - reader2->UpdatesFaviconInBackground()); + reader2_->UpdatesFaviconInBackground()); - test_clock_.Advance(kDelay); + writer_->NotifySiteUnloaded(); + writer2_->NotifySiteUnloaded(); +} + +// Verify that an origin is *not* removed from the data store (in memory and on +// disk) when there remain references to it in the history, after the history is +// partially cleared. +TEST_F(LocalSiteCharacteristicsDataStoreTest, + OnURLsDeleted_Partial_OriginStillReferenced) { + SetupTwoSitesUsingFeaturesInBackground(); + + const base::TimeDelta last_loaded_time_before_urls_deleted = + data_->last_loaded_time_for_testing(); + const base::TimeDelta last_loaded_time2_before_urls_deleted = + data2_->last_loaded_time_for_testing(); + + // Make sure that all data passed to |OnURLsDeleted| get passed to the + // database, even if they're not in the internal map used by the data store. + const url::Origin kOriginNotInMap = + url::Origin::Create(GURL("http://www.url-not-in-map.com")); + history::URLRows urls_to_delete = {history::URLRow(kTestOrigin.GetURL()), + history::URLRow(kOriginNotInMap.GetURL())}; + history::DeletionInfo deletion_info = + history::DeletionInfo::ForUrls(urls_to_delete, std::set<GURL>()); + deletion_info.set_deleted_urls_origin_map({ + {kTestOrigin.GetURL(), {4, base::Time::Now()}}, + {kOriginNotInMap.GetURL(), {3, base::Time::Now()}}, + }); + data_store_->OnURLsDeleted(nullptr, deletion_info); + ::testing::Mock::VerifyAndClear(mock_db_); + + // Sites shouldn't have been cleared. + EXPECT_EQ(data_->last_loaded_time_for_testing(), + last_loaded_time_before_urls_deleted); + EXPECT_EQ(SiteFeatureUsage::kSiteFeatureInUse, + reader_->UpdatesTitleInBackground()); + EXPECT_EQ(data2_->last_loaded_time_for_testing(), + last_loaded_time2_before_urls_deleted); + EXPECT_EQ(SiteFeatureUsage::kSiteFeatureInUse, + reader2_->UpdatesFaviconInBackground()); + + writer_->NotifySiteUnloaded(); + writer2_->NotifySiteUnloaded(); +} + +// Verify that origins are removed from the data store (in memory and on disk) +// when the history is completely cleared. +TEST_F(LocalSiteCharacteristicsDataStoreTest, OnURLsDeleted_Full) { + SetupTwoSitesUsingFeaturesInBackground(); // Delete all the information stored in the data store. - EXPECT_CALL(*mock_db, ClearDatabase()); + EXPECT_CALL(*mock_db_, ClearDatabase()); data_store_->OnURLsDeleted(nullptr, history::DeletionInfo::ForAllHistory()); - ::testing::Mock::VerifyAndClear(mock_db); + ::testing::Mock::VerifyAndClear(mock_db_); + // The information for both sites should have been cleared. + EXPECT_EQ(data_->last_loaded_time_for_testing(), + test_clock_.NowTicks() - base::TimeTicks::UnixEpoch()); EXPECT_EQ(SiteFeatureUsage::kSiteFeatureUsageUnknown, - reader2->UpdatesFaviconInBackground()); - EXPECT_EQ(data->last_loaded_time_for_testing(), + reader_->UpdatesTitleInBackground()); + EXPECT_EQ(data2_->last_loaded_time_for_testing(), test_clock_.NowTicks() - base::TimeTicks::UnixEpoch()); + EXPECT_EQ(SiteFeatureUsage::kSiteFeatureUsageUnknown, + reader2_->UpdatesFaviconInBackground()); - internal::LocalSiteCharacteristicsDataImpl* data2 = - data_store_->origin_data_map_for_testing().find(kTestOrigin2)->second; - EXPECT_TRUE(data2); - EXPECT_EQ(data2->last_loaded_time_for_testing(), - test_clock_.NowTicks() - base::TimeTicks::UnixEpoch()); - - writer->NotifySiteUnloaded(); - writer2->NotifySiteUnloaded(); + writer_->NotifySiteUnloaded(); + writer2_->NotifySiteUnloaded(); } TEST_F(LocalSiteCharacteristicsDataStoreTest, InspectorWorks) {
diff --git a/chrome/browser/resources/chromeos/chromevox/chromevox/background/options.css b/chrome/browser/resources/chromeos/chromevox/chromevox/background/options.css index 42a51c1..677a061 100644 --- a/chrome/browser/resources/chromeos/chromevox/chromevox/background/options.css +++ b/chrome/browser/resources/chromeos/chromevox/chromevox/background/options.css
@@ -104,10 +104,8 @@ } .option .developer-option-icon-button { - background-color: #fff; float: right; - height: 60px; - vertical-align: middle; + margin-top: 10px; } .option .change-display-style { @@ -159,4 +157,3 @@ vertical-align: middle; width: 646px; } -
diff --git a/chrome/browser/resources/chromeos/chromevox/chromevox/background/options.html b/chrome/browser/resources/chromeos/chromevox/chromevox/background/options.html index 385f05ee..12c224a8 100644 --- a/chrome/browser/resources/chromeos/chromevox/chromevox/background/options.html +++ b/chrome/browser/resources/chromeos/chromevox/chromevox/background/options.html
@@ -12,10 +12,10 @@ <script type="text/javascript" src="options_loader.js"></script> <script type="text/javascript" src="../../chromeVoxChromeOptionsScript.js"> </script> -<link rel="import" href="chrome://resources/cr_elements/icons.html"> +<link rel="import" href="chrome://resources/cr_elements/cr_icon_button/cr_icon_button.html"> <link rel="import" href="chrome://resources/cr_elements/cr_radio_button/cr_radio_button.html"> +<link rel="import" href="chrome://resources/cr_elements/icons.html"> <link rel="import" href="chrome://resources/html/polymer.html"> -<link rel="import" href="chrome://resources/polymer/v1_0/paper-icon-button/paper-icon-button.html"> </head> <body> @@ -150,12 +150,12 @@ <!-- Braille Settings --> <div class="chromeos option"> <label> - <paper-icon-button icon="cr:expand-more" - class="developer-option-icon-button" id="braille-settings-more" - name="chromevox-braille-settings"></paper-icon-button> - <paper-icon-button icon="cr:expand-less" - class="developer-option-icon-button" id="braille-settings-less" - name="chromevox-developer-options" hidden></paper-icon-button> + <cr-icon-button iron-icon="cr:expand-more" + class="developer-option-icon-button" id="braille-settings-more" + name="chromevox-braille-settings"></cr-icon-button> + <cr-icon-button iron-icon="cr:expand-less" + class="developer-option-icon-button" id="braille-settings-less" + name="chromevox-developer-options" hidden></cr-icon-button> <span class="i18n" msgid="options_braille_settings"> Braille Settings </span> @@ -194,12 +194,12 @@ <!-- Bluetooth braille display --> <div class="chromeos option"> <label> - <paper-icon-button icon="cr:expand-more" - class="developer-option-icon-button" id="bt-braille-settings-more" - name="chromevox-braille-settings"></paper-icon-button> - <paper-icon-button icon="cr:expand-less" - class="developer-option-icon-button" id="bt-braille-settings-less" - name="chromevox-developer-options" hidden></paper-icon-button> + <cr-icon-button iron-icon="cr:expand-more" + class="developer-option-icon-button" id="bt-braille-settings-more" + name="chromevox-braille-settings"></cr-icon-button> + <cr-icon-button iron-icon="cr:expand-less" + class="developer-option-icon-button" id="bt-braille-settings-less" + name="chromevox-developer-options" hidden></cr-icon-button> <span class="i18n" msgid="options_bluetooth_braille_display_title"> Bluetooth braille display </span> @@ -211,12 +211,14 @@ <!-- Virtual braille display --> <div class="chromeos option"> <label> - <paper-icon-button icon="cr:expand-more" - class="developer-option-icon-button" id="virtual-braille-settings-more" - name="chromevox-braille-settings"></paper-icon-button> - <paper-icon-button icon="cr:expand-less" - class="developer-option-icon-button" id="virtual-braille-settings-less" - name="chromevox-developer-options" hidden></paper-icon-button> + <cr-icon-button iron-icon="cr:expand-more" + class="developer-option-icon-button" + id="virtual-braille-settings-more" name="chromevox-braille-settings"> + </cr-icon-button> + <cr-icon-button iron-icon="cr:expand-less" + class="developer-option-icon-button" + id="virtual-braille-settings-less" name="chromevox-developer-options" + hidden></cr-icon-button> <span class="i18n settings-description" msgid="options_virtual_braille_display"> Virtual braille display @@ -275,16 +277,16 @@ </h2> <div class="chromeos option"> <label> - <paper-icon-button icon="cr:expand-more" - aria-describedby="developer-options-label" aria-expanded="false" - class="developer-option-icon-button" - id="chromevox-developer-options-more" - name="chromevox-developer-options"></paper-icon-button> - <paper-icon-button icon="cr:expand-less" - aria-describedby="developer-options-label" aria-expanded="true" - class="developer-option-icon-button" - id="chromevox-developer-options-less" - name="chromevox-developer-options" hidden></paper-icon-button> + <cr-icon-button iron-icon="cr:expand-more" + aria-describedby="developer-options-label" aria-expanded="false" + class="developer-option-icon-button" + id="chromevox-developer-options-more" + name="chromevox-developer-options"></cr-icon-button> + <cr-icon-button iron-icon="cr:expand-less" + aria-describedby="developer-options-label" aria-expanded="true" + class="developer-option-icon-button" + id="chromevox-developer-options-less" + name="chromevox-developer-options" hidden></cr-icon-button> <span class="i18n" msgid="options_developer_options" id="developer-options-label"> Enable developer options @@ -327,10 +329,9 @@ </div> <div class="settings-expand-row" id="show-developer-log"> <label> - <paper-icon-button icon="cr:open-in-new" - aria-describedby="show-log-label" - class="developer-option-icon-button" id="open-developer-log"> - </paper-icon-button> + <cr-icon-button iron-icon="cr:open-in-new" + aria-describedby="show-log-label" class="developer-option-icon-button" + id="open-developer-log"></cr-icon-button> <span class="i18n settings-description" msgid="options_show_log" id="show-log-label"> Show Log</span>
diff --git a/chrome/browser/resources/chromeos/login/BUILD.gn b/chrome/browser/resources/chromeos/login/BUILD.gn index 4a05290..afe04dda 100644 --- a/chrome/browser/resources/chromeos/login/BUILD.gn +++ b/chrome/browser/resources/chromeos/login/BUILD.gn
@@ -49,7 +49,6 @@ ":saml_interstitial", ":sync_consent", ":throbber_notice", - ":unrecoverable_cryptohome_error_card", ":update_required_card", ] } @@ -336,8 +335,5 @@ js_library("throbber_notice") { } -js_library("unrecoverable_cryptohome_error_card") { -} - js_library("update_required_card") { }
diff --git a/chrome/browser/resources/chromeos/login/custom_elements_login.html b/chrome/browser/resources/chromeos/login/custom_elements_login.html index 2192ac3..36d9825 100644 --- a/chrome/browser/resources/chromeos/login/custom_elements_login.html +++ b/chrome/browser/resources/chromeos/login/custom_elements_login.html
@@ -11,7 +11,6 @@ <include src="throbber_notice.html"> <include src="navigation_bar.html"> <include src="network_select_login.html"> -<include src="unrecoverable_cryptohome_error_card.html"> <include src="update_required_card.html"> <include src="offline_ad_login.html"> <include src="active_directory_password_change.html">
diff --git a/chrome/browser/resources/chromeos/login/custom_elements_login.js b/chrome/browser/resources/chromeos/login/custom_elements_login.js index eeb4aac..a1ade1a 100644 --- a/chrome/browser/resources/chromeos/login/custom_elements_login.js +++ b/chrome/browser/resources/chromeos/login/custom_elements_login.js
@@ -17,7 +17,6 @@ // <include src="throbber_notice.js"> // <include src="navigation_bar.js"> // <include src="network_select_login.js"> -// <include src="unrecoverable_cryptohome_error_card.js"> // <include src="update_required_card.js"> // <include src="offline_ad_login.js"> // <include src="active_directory_password_change.js">
diff --git a/chrome/browser/resources/chromeos/login/gaia_password_changed.html b/chrome/browser/resources/chromeos/login/gaia_password_changed.html index 30877f3f..974db4a 100644 --- a/chrome/browser/resources/chromeos/login/gaia_password_changed.html +++ b/chrome/browser/resources/chromeos/login/gaia_password_changed.html
@@ -66,7 +66,8 @@ <div slot="label" i18n-content="oldPasswordHint"></div> <div slot="error" i18n-content="oldPasswordIncorrect"></div> </gaia-input> - <gaia-button type="link" on-tap="onForgotPasswordClicked_" + <gaia-button id="forgot-password-link" type="link" + on-tap="onForgotPasswordClicked_" i18n-content="forgotOldPasswordButtonText"> </gaia-button> </gaia-input-form> @@ -83,7 +84,8 @@ </p> </div> <div class="horizontal layout justified center"> - <gaia-button type="link" on-tap="onTryAgainClicked_" + <gaia-button id="try-again-link" type="link" + on-tap="onTryAgainClicked_" i18n-content="passwordChangedTryAgain"> </gaia-button> <gaia-button id="proceedAnywayBtn" on-tap="onProceedClicked_"
diff --git a/chrome/browser/resources/chromeos/login/md_login.html b/chrome/browser/resources/chromeos/login/md_login.html index a7500833..c57e172 100644 --- a/chrome/browser/resources/chromeos/login/md_login.html +++ b/chrome/browser/resources/chromeos/login/md_login.html
@@ -70,7 +70,6 @@ <link rel="stylesheet" href="screen_confirm_password.css"> <link rel="stylesheet" href="screen_fatal_error.css"> <link rel="stylesheet" href="screen_device_disabled.css"> -<link rel="stylesheet" href="screen_unrecoverable_cryptohome_error.css"> <link rel="stylesheet" href="screen_active_directory_password_change.css"> <link rel="stylesheet" href="screen_update_required.css">
diff --git a/chrome/browser/resources/chromeos/login/md_login.js b/chrome/browser/resources/chromeos/login/md_login.js index 04cfbfc8..de43f1bf 100644 --- a/chrome/browser/resources/chromeos/login/md_login.js +++ b/chrome/browser/resources/chromeos/login/md_login.js
@@ -38,7 +38,6 @@ // <include src="screen_confirm_password.js"> // <include src="screen_fatal_error.js"> // <include src="screen_device_disabled.js"> -// <include src="screen_unrecoverable_cryptohome_error.js"> // <include src="screen_active_directory_password_change.js"> // <include src="screen_encryption_migration.js"> // <include src="screen_update_required.js"> @@ -92,7 +91,6 @@ login.ConfirmPasswordScreen.register(); login.FatalErrorScreen.register(); login.DeviceDisabledScreen.register(); - login.UnrecoverableCryptohomeErrorScreen.register(); login.ActiveDirectoryPasswordChangeScreen.register(/* lazyInit= */ true); login.EncryptionMigrationScreen.register(); login.SupervisionTransitionScreen.register();
diff --git a/chrome/browser/resources/chromeos/login/md_login_screens.html b/chrome/browser/resources/chromeos/login/md_login_screens.html index e12f206..50b8ee40 100644 --- a/chrome/browser/resources/chromeos/login/md_login_screens.html +++ b/chrome/browser/resources/chromeos/login/md_login_screens.html
@@ -16,7 +16,6 @@ <include src="screen_confirm_password.html"> <include src="screen_fatal_error.html"> <include src="screen_device_disabled.html"> -<include src="screen_unrecoverable_cryptohome_error.html"> <include src="screen_active_directory_password_change.html"> <include src="screen_encryption_migration.html"> <include src="screen_update_required.html">
diff --git a/chrome/browser/resources/chromeos/login/oobe.html b/chrome/browser/resources/chromeos/login/oobe.html index d6dea95..506f7b5c 100644 --- a/chrome/browser/resources/chromeos/login/oobe.html +++ b/chrome/browser/resources/chromeos/login/oobe.html
@@ -76,7 +76,6 @@ <link rel="stylesheet" href="screen_confirm_password.css"> <link rel="stylesheet" href="screen_fatal_error.css"> <link rel="stylesheet" href="screen_device_disabled.css"> -<link rel="stylesheet" href="screen_unrecoverable_cryptohome_error.css"> <link rel="stylesheet" href="screen_active_directory_password_change.css"> <link rel="stylesheet" href="screen_update_required.css">
diff --git a/chrome/browser/resources/chromeos/login/oobe.js b/chrome/browser/resources/chromeos/login/oobe.js index cd3b386..155b9c9 100644 --- a/chrome/browser/resources/chromeos/login/oobe.js +++ b/chrome/browser/resources/chromeos/login/oobe.js
@@ -39,7 +39,6 @@ // <include src="screen_confirm_password.js"> // <include src="screen_fatal_error.js"> // <include src="screen_device_disabled.js"> -// <include src="screen_unrecoverable_cryptohome_error.js"> // <include src="screen_active_directory_password_change.js"> // <include src="screen_encryption_migration.js"> // <include src="screen_update_required.js">
diff --git a/chrome/browser/resources/chromeos/login/screen_password_changed.js b/chrome/browser/resources/chromeos/login/screen_password_changed.js index 0e70231..a32c225 100644 --- a/chrome/browser/resources/chromeos/login/screen_password_changed.js +++ b/chrome/browser/resources/chromeos/login/screen_password_changed.js
@@ -33,7 +33,8 @@ cancel: function() { if (!this.gaiaPasswordChanged_.disabled) { chrome.send( - 'cancelPasswordChangedFlow', [this.gaiaPasswordChanged_.email]); + 'cancelPasswordChangedFlow', + [this.gaiaPasswordChanged_.email || '']); } },
diff --git a/chrome/browser/resources/chromeos/login/screen_unrecoverable_cryptohome_error.css b/chrome/browser/resources/chromeos/login/screen_unrecoverable_cryptohome_error.css deleted file mode 100644 index 83b2bf8b..0000000 --- a/chrome/browser/resources/chromeos/login/screen_unrecoverable_cryptohome_error.css +++ /dev/null
@@ -1,7 +0,0 @@ -/* 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. */ - -#unrecoverable-cryptohome-error { - width: 448px; /* Should be the same as #gaia-signin. */ -}
diff --git a/chrome/browser/resources/chromeos/login/screen_unrecoverable_cryptohome_error.html b/chrome/browser/resources/chromeos/login/screen_unrecoverable_cryptohome_error.html deleted file mode 100644 index 8ec9891..0000000 --- a/chrome/browser/resources/chromeos/login/screen_unrecoverable_cryptohome_error.html +++ /dev/null
@@ -1,12 +0,0 @@ -<link rel="import" href="chrome://oobe/custom_elements.html"> - -<div id="unrecoverable-cryptohome-error" class="step faded" - aria-live="polite" hidden> - <unrecoverable-cryptohome-error-card id="unrecoverable-cryptohome-error-card"> - </unrecoverable-cryptohome-error-card> - <div id="unrecoverable-cryptohome-error-busy" class="step-loading" hidden> - <throbber-notice - i18n-values="text:unrecoverableCryptohomeErrorRecreatingProfile"> - </throbber-notice> - </div> -</div>
diff --git a/chrome/browser/resources/chromeos/login/screen_unrecoverable_cryptohome_error.js b/chrome/browser/resources/chromeos/login/screen_unrecoverable_cryptohome_error.js deleted file mode 100644 index 2112a3bb..0000000 --- a/chrome/browser/resources/chromeos/login/screen_unrecoverable_cryptohome_error.js +++ /dev/null
@@ -1,49 +0,0 @@ -// 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. - -login.createScreen( - 'UnrecoverableCryptohomeErrorScreen', 'unrecoverable-cryptohome-error', - function() { - return { - EXTERNAL_API: ['show', 'resumeAfterFeedbackUI'], - - /** @override */ - decorate: function() { - this.card_ = $('unrecoverable-cryptohome-error-card'); - this.throbber_ = $('unrecoverable-cryptohome-error-busy'); - - this.card_.addEventListener('done', function(e) { - this.setLoading_(true); - $('oobe').hidden = true; // Hide while showing the feedback UI. - chrome.send('sendFeedbackAndResyncUserData'); - }.bind(this)); - }, - - /** - * Sets whether to show the loading throbber. - * @param {boolean} loading - */ - setLoading_: function(loading) { - this.card_.hidden = loading; - this.throbber_.hidden = !loading; - }, - - /** - * Show the unrecoverable cryptohome error screen to ask user permission - * to collect a feedback report. - */ - show: function() { - this.setLoading_(false); - - Oobe.showScreen({id: SCREEN_UNRECOVERABLE_CRYPTOHOME_ERROR}); - }, - - /** - * Shows the loading UI after the feedback UI is dismissed. - */ - resumeAfterFeedbackUI: function() { - $('oobe').hidden = false; - } - }; - });
diff --git a/chrome/browser/resources/chromeos/login/unrecoverable_cryptohome_error_card.css b/chrome/browser/resources/chromeos/login/unrecoverable_cryptohome_error_card.css deleted file mode 100644 index 2703e0b4..0000000 --- a/chrome/browser/resources/chromeos/login/unrecoverable_cryptohome_error_card.css +++ /dev/null
@@ -1,18 +0,0 @@ -/* 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. */ - -:host .content { - padding: 24px 24px 16px; -} - -:host .title { - font-weight: bold; - white-space: nowrap; - word-wrap: normal; -} - -:host .message { - margin-bottom: 25px; - margin-top: 20px; -}
diff --git a/chrome/browser/resources/chromeos/login/unrecoverable_cryptohome_error_card.html b/chrome/browser/resources/chromeos/login/unrecoverable_cryptohome_error_card.html deleted file mode 100644 index 5507718..0000000 --- a/chrome/browser/resources/chromeos/login/unrecoverable_cryptohome_error_card.html +++ /dev/null
@@ -1,34 +0,0 @@ -<!-- 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. --> - -<link rel="import" href="chrome://resources/html/polymer.html"> - -<!-- - Unrecoverable cryptohome error card that shows to use when an unrecoverable - cryptohome error happens. It asks user to help us debugging the issue by - sending us a feedback and allows user to re-create the cryptohome so that - the device could still be used. - - Events: - 'done' - fired when user clicks on continue button. ---> -<dom-module id="unrecoverable-cryptohome-error-card"> - <link rel="stylesheet" href="oobe_flex_layout.css"> - <link rel="stylesheet" href="unrecoverable_cryptohome_error_card.css"> - - <template> - <div class="content"> - <div i18n-content="unrecoverableCryptohomeErrorMessageTitle" class="title"> - </div> - <div class="message" - i18n-values=".innerHTML:unrecoverableCryptohomeErrorMessage"> - </div> - <div class="horizontal-reverse layout justified center"> - <gaia-button on-tap="onContinueClicked_" - i18n-content="unrecoverableCryptohomeErrorContinue"> - </gaia-button> - </div> - </div> - </template> -</dom-module>
diff --git a/chrome/browser/resources/chromeos/login/unrecoverable_cryptohome_error_card.js b/chrome/browser/resources/chromeos/login/unrecoverable_cryptohome_error_card.js deleted file mode 100644 index a92d0c78..0000000 --- a/chrome/browser/resources/chromeos/login/unrecoverable_cryptohome_error_card.js +++ /dev/null
@@ -1,12 +0,0 @@ -// 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. - -Polymer({ - is: 'unrecoverable-cryptohome-error-card', - - /** @private */ - onContinueClicked_: function() { - this.fire('done'); - }, -});
diff --git a/chrome/browser/resources/chromeos/switch_access/options.js b/chrome/browser/resources/chromeos/switch_access/options.js index aab4c8e..7b62d789 100644 --- a/chrome/browser/resources/chromeos/switch_access/options.js +++ b/chrome/browser/resources/chromeos/switch_access/options.js
@@ -7,19 +7,13 @@ */ class SwitchAccessOptions { constructor() { - const background = chrome.extension.getBackgroundPage(); - /** * SwitchAccess reference. * @private {!SwitchAccessInterface} */ - this.switchAccess_ = background.switchAccess; + this.switchAccess_ = chrome.extension.getBackgroundPage().switchAccess; this.init_(); - - document.addEventListener('change', this.handleInputChange_.bind(this)); - background.document.addEventListener( - 'prefsUpdate', this.handlePrefsUpdate_.bind(this)); } /** @@ -29,6 +23,9 @@ * @private */ init_() { + document.addEventListener('change', this.handleInputChange_.bind(this)); + chrome.storage.onChanged.addListener(this.handleStorageChange_.bind(this)); + document.getElementById('enableAutoScan').checked = this.switchAccess_.getBooleanPreference('enableAutoScan'); document.getElementById('autoScanTime').value = @@ -106,23 +103,23 @@ /** * Handle a change in user preferences. * - * @param {!Event} event + * @param {!Object} storageChanges + * @param {string} areaName * @private */ - handlePrefsUpdate_(event) { - const updatedPrefs = event.detail; - for (let key of Object.keys(updatedPrefs)) { + handleStorageChange_(storageChanges, areaName) { + for (let key of Object.keys(storageChanges)) { + const newValue = storageChanges[key].newValue; switch (key) { case 'enableAutoScan': - document.getElementById(key).checked = updatedPrefs[key]; + document.getElementById(key).checked = newValue; break; case 'autoScanTime': - document.getElementById(key).value = updatedPrefs[key] / 1000; + document.getElementById(key).value = newValue / 1000; break; default: if (this.switchAccess_.getCommands().includes(key)) - document.getElementById(key).value = - String.fromCharCode(updatedPrefs[key]); + document.getElementById(key).value = String.fromCharCode(newValue); } } }
diff --git a/chrome/browser/resources/chromeos/switch_access/preferences.js b/chrome/browser/resources/chromeos/switch_access/preferences.js index 9f64f93..8f6f913 100644 --- a/chrome/browser/resources/chromeos/switch_access/preferences.js +++ b/chrome/browser/resources/chromeos/switch_access/preferences.js
@@ -27,8 +27,7 @@ /** * Store any changes from |chrome.storage.sync| to |this.preferences_|, if - * |this.preferences_| is not already set to that value. If - * |this.preferences_| changes, fire a |prefsUpdate| event. + * |this.preferences_| is not already set to that value. * * @param {!Object} storageChanges * @param {string} areaName @@ -42,11 +41,8 @@ updatedPreferences[key] = storageChanges[key].newValue; } } - if (Object.keys(updatedPreferences).length > 0) { - let event = - new CustomEvent('prefsUpdate', {'detail': updatedPreferences}); - document.dispatchEvent(event); - } + if (Object.keys(updatedPreferences).length > 0) + this.switchAccess_.onPreferencesChanged(updatedPreferences); } /** @@ -64,8 +60,7 @@ /** * Asynchronously load the current preferences from |chrome.storage.sync| and * store them in |this.preferences_|, if |this.preferences_| is not already - * set to that value. If |this.preferences_| changes, fire a |prefsUpdate| - * event. + * set to that value. * * @private */ @@ -81,11 +76,8 @@ } } - if (Object.keys(updatedPreferences).length > 0) { - const event = - new CustomEvent('prefsUpdate', {'detail': updatedPreferences}); - document.dispatchEvent(event); - } + if (Object.keys(updatedPreferences).length > 0) + this.switchAccess_.onPreferencesChanged(updatedPreferences); }); }
diff --git a/chrome/browser/resources/chromeos/switch_access/switch_access.js b/chrome/browser/resources/chromeos/switch_access/switch_access.js index f9bc0f5e..364f3e0 100644 --- a/chrome/browser/resources/chromeos/switch_access/switch_access.js +++ b/chrome/browser/resources/chromeos/switch_access/switch_access.js
@@ -73,9 +73,6 @@ if (this.navReadyCallback_) this.navReadyCallback_(); }.bind(this)); - - document.addEventListener( - 'prefsUpdate', this.handlePrefsUpdate_.bind(this)); } /** @@ -178,18 +175,16 @@ /** * Handle a change in user preferences. - * @param {!Event} event - * @private + * @param {!Object} changes */ - handlePrefsUpdate_(event) { - const updatedPrefs = event.detail; - for (const key of Object.keys(updatedPrefs)) { + onPreferencesChanged(changes) { + for (const key of Object.keys(changes)) { switch (key) { case 'enableAutoScan': - this.autoScanManager_.setEnabled(updatedPrefs[key]); + this.autoScanManager_.setEnabled(changes[key]); break; case 'autoScanTime': - this.autoScanManager_.setScanTime(updatedPrefs[key]); + this.autoScanManager_.setScanTime(changes[key]); break; default: if (this.commands_.getCommands().includes(key))
diff --git a/chrome/browser/resources/chromeos/switch_access/switch_access_interface.js b/chrome/browser/resources/chromeos/switch_access/switch_access_interface.js index 2c51db7b..39889fc 100644 --- a/chrome/browser/resources/chromeos/switch_access/switch_access_interface.js +++ b/chrome/browser/resources/chromeos/switch_access/switch_access_interface.js
@@ -70,6 +70,12 @@ performedUserAction() {} /** + * Handle a change in user preferences. + * @param {!Object} changes + */ + onPreferencesChanged(changes) {} + + /** * Set the value of the preference |key| to |value| in chrome.storage.sync. * The behavior is not updated until the storage update is complete. *
diff --git a/chrome/browser/resources/extensions/activity_log/activity_log.html b/chrome/browser/resources/extensions/activity_log/activity_log.html index b896e43..301a0ad 100644 --- a/chrome/browser/resources/extensions/activity_log/activity_log.html +++ b/chrome/browser/resources/extensions/activity_log/activity_log.html
@@ -1,6 +1,7 @@ <link rel="import" href="chrome://resources/html/polymer.html"> <link rel="import" href="chrome://resources/cr_elements/cr_container_shadow_behavior.html"> +<link rel="import" href="chrome://resources/cr_elements/cr_icon_button/cr_icon_button.html"> <link rel="import" href="chrome://resources/cr_elements/cr_icons_css.html"> <link rel="import" href="chrome://resources/cr_elements/paper_tabs_style_css.html"> <link rel="import" href="chrome://resources/cr_elements/shared_vars_css.html"> @@ -8,7 +9,6 @@ <link rel="import" href="chrome://resources/html/cr/ui/focus_without_ink.html"> <link rel="import" href="chrome://resources/html/i18n_behavior.html"> <link rel="import" href="chrome://resources/polymer/v1_0/iron-pages/iron-pages.html"> -<link rel="import" href="chrome://resources/polymer/v1_0/paper-icon-button/paper-icon-button-light.html"> <link rel="import" href="chrome://resources/polymer/v1_0/paper-tabs/paper-tabs.html"> <link rel="import" href="activity_log_history.html"> <link rel="import" href="activity_log_stream.html"> @@ -36,7 +36,7 @@ } paper-tabs { - border-bottom: 1px solid var(--google-grey-refresh-300); + border-bottom: 1px solid var(--google-grey-refresh-300); font-size: inherit; height: 40px; } @@ -63,10 +63,9 @@ <div class="page-container" id="container"> <div class="page-content"> <div class="page-header"> - <paper-icon-button-light class="icon-arrow-back no-overlap"> - <button id="closeButton" aria-label="$i18n{back}" - on-click="onCloseButtonTap_"></button> - </paper-icon-button-light> + <cr-icon-button class="icon-arrow-back no-overlap" id="closeButton" + aria-label="$i18n{back}" on-click="onCloseButtonTap_"> + </cr-icon-button> <img id="icon" src="[[extensionInfo.iconUrl]]" alt$="[[appOrExtension( extensionInfo.type,
diff --git a/chrome/browser/resources/extensions/activity_log/activity_log_history.html b/chrome/browser/resources/extensions/activity_log/activity_log_history.html index 6b6e1b65..f7004f83 100644 --- a/chrome/browser/resources/extensions/activity_log/activity_log_history.html +++ b/chrome/browser/resources/extensions/activity_log/activity_log_history.html
@@ -27,7 +27,7 @@ } cr-icon-button { - margin-inline-start: 0; + margin: 0; } .activity-table-headings {
diff --git a/chrome/browser/resources/extensions/activity_log/activity_log_history_item.html b/chrome/browser/resources/extensions/activity_log/activity_log_history_item.html index 35fb811..5ed47ab 100644 --- a/chrome/browser/resources/extensions/activity_log/activity_log_history_item.html +++ b/chrome/browser/resources/extensions/activity_log/activity_log_history_item.html
@@ -1,10 +1,10 @@ <link rel="import" href="chrome://resources/html/polymer.html"> <link rel="import" href="chrome://resources/cr_elements/cr_expand_button/cr_expand_button.html"> +<link rel="import" href="chrome://resources/cr_elements/cr_icon_button/cr_icon_button.html"> <link rel="import" href="chrome://resources/cr_elements/cr_icons_css.html"> <link rel="import" href="chrome://resources/cr_elements/shared_vars_css.html"> <link rel="import" href="chrome://resources/html/cr.html"> -<link rel="import" href="chrome://resources/polymer/v1_0/paper-icon-button/paper-icon-button-light.html"> <link rel="import" href="../shared_style.html"> <link rel="import" href="../shared_vars.html"> @@ -14,9 +14,12 @@ :host { border-top: var(--cr-separator-line); display: block; - /* Unequal padding on left/right side as the paper-icon-button-light's - * width is greater than the delete icon's width. */ - padding: 8px 8px 8px var(--cr-section-padding); + /* Unequal padding on left/right side as the cr-icon-button's width is + * greater than the delete icon's width. */ + padding-bottom: 8px; + padding-inline-end: 8px; + padding-inline-start: var(--cr-section-padding); + padding-top: 8px; } #activity-item-main-row { @@ -93,11 +96,9 @@ hidden$="[[!isExpandable_]]"> </cr-expand-button> <div class="separator" hidden$="[[!isExpandable_]]"></div> - <paper-icon-button-light id="activity-delete" class="icon-delete-gray"> - <button id="activity-delete-button" aria-describedby="api-call" - aria-label="$i18n{clearEntry}" on-click="onDeleteTap_"> - </button> - </paper-icon-button-light> + <cr-icon-button id="activity-delete" class="icon-delete-gray" + aria-describedby="api-call" aria-label="$i18n{clearEntry}" + on-click="onDeleteTap_"></cr-icon-button> </div> <div id="page-url-list" hidden$="[[!data.expanded]]"> <template is="dom-repeat" items="[[getPageUrls_(data)]]">
diff --git a/chrome/browser/resources/extensions/detail_view.html b/chrome/browser/resources/extensions/detail_view.html index 45b3284..b021c518 100644 --- a/chrome/browser/resources/extensions/detail_view.html +++ b/chrome/browser/resources/extensions/detail_view.html
@@ -1,6 +1,7 @@ <link rel="import" href="chrome://resources/html/polymer.html"> <link rel="import" href="chrome://resources/cr_elements/cr_container_shadow_behavior.html"> +<link rel="import" href="chrome://resources/cr_elements/cr_icon_button/cr_icon_button.html"> <link rel="import" href="chrome://resources/cr_elements/cr_icons_css.html"> <link rel="import" href="chrome://resources/cr_elements/cr_link_row/cr_link_row.html"> <link rel="import" href="chrome://resources/cr_elements/cr_toggle/cr_toggle.html"> @@ -16,7 +17,6 @@ <link rel="import" href="chrome://resources/polymer/v1_0/iron-flex-layout/iron-flex-layout-classes.html"> <link rel="import" href="chrome://resources/polymer/v1_0/iron-icon/iron-icon.html"> <link rel="import" href="chrome://resources/polymer/v1_0/paper-button/paper-button.html"> -<link rel="import" href="chrome://resources/polymer/v1_0/paper-icon-button/paper-icon-button-light.html"> <link rel="import" href="chrome://resources/polymer/v1_0/paper-spinner/paper-spinner-lite.html"> <link rel="import" href="chrome://resources/polymer/v1_0/paper-styles/color.html"> <link rel="import" href="item_behavior.html"> @@ -167,10 +167,9 @@ <div class="page-container" id="container"> <div class="page-content"> <div class="page-header"> - <paper-icon-button-light class="icon-arrow-back no-overlap"> - <button id="closeButton" aria-label="$i18n{back}" - on-click="onCloseButtonTap_"></button> - </paper-icon-button-light> + <cr-icon-button class="icon-arrow-back no-overlap" id="closeButton" + aria-label="$i18n{back}" on-click="onCloseButtonTap_"> + </cr-icon-button> <img id="icon" src="[[data.iconUrl]]" alt$="[[appOrExtension( data.type,
diff --git a/chrome/browser/resources/extensions/error_page.html b/chrome/browser/resources/extensions/error_page.html index da59eb9..5067418 100644 --- a/chrome/browser/resources/extensions/error_page.html +++ b/chrome/browser/resources/extensions/error_page.html
@@ -1,6 +1,7 @@ <link rel="import" href="chrome://resources/html/polymer.html"> <link rel="import" href="chrome://resources/cr_elements/cr_container_shadow_behavior.html"> +<link rel="import" href="chrome://resources/cr_elements/cr_icon_button/cr_icon_button.html"> <link rel="import" href="chrome://resources/cr_elements/cr_icons_css.html"> <link rel="import" href="chrome://resources/cr_elements/icons.html"> <link rel="import" href="chrome://resources/cr_elements/paper_button_style_css.html"> @@ -12,7 +13,6 @@ <link rel="import" href="chrome://resources/polymer/v1_0/iron-collapse/iron-collapse.html"> <link rel="import" href="chrome://resources/polymer/v1_0/iron-icon/iron-icon.html"> <link rel="import" href="chrome://resources/polymer/v1_0/paper-button/paper-button.html"> -<link rel="import" href="chrome://resources/polymer/v1_0/paper-icon-button/paper-icon-button-light.html"> <link rel="import" href="chrome://resources/polymer/v1_0/paper-styles/color.html"> <link rel="import" href="code_section.html"> <link rel="import" href="item_util.html"> @@ -69,8 +69,8 @@ padding-inline-start: 0; } - .error-item paper-icon-button-light { - margin-inline-end: 0; + .error-item cr-icon-button { + margin: 0; } .error-item.selected { @@ -140,11 +140,9 @@ <div class="page-container" id="container"> <div class="page-content"> <div id="heading"> - <paper-icon-button-light class="icon-arrow-back no-overlap"> - <button id="closeButton" aria-label="$i18n{back}" - on-click="onCloseButtonTap_"> - </button> - </paper-icon-button-light> + <cr-icon-button class="icon-arrow-back no-overlap" id="closeButton" + aria-label="$i18n{back}" on-click="onCloseButtonTap_"> + </cr-icon-button> <span>$i18n{errorsPageHeading}</span> <paper-button on-click="onClearAllTap_" hidden="[[!entries_.length]]"> $i18n{clearAll} @@ -170,13 +168,11 @@ </div> </div> <div class="separator"></div> - <paper-icon-button-light class="icon-delete-gray"> - <button on-click="onDeleteErrorAction_" - aria-describedby$="[[item.id]]" - aria-label="$i18n{clearEntry}" - on-keydown="onDeleteErrorAction_"> - </button> - </paper-icon-button-light> + <cr-icon-button class="icon-delete-gray" + on-click="onDeleteErrorAction_" + aria-describedby$="[[item.id]]" + aria-label="$i18n{clearEntry}" + on-keydown="onDeleteErrorAction_"></cr-icon-button> </div> <iron-collapse opened="[[isOpened_(index, selectedEntry_)]]"> <div class="devtools-controls">
diff --git a/chrome/browser/resources/extensions/item.html b/chrome/browser/resources/extensions/item.html index 5cab351..5fa187a 100644 --- a/chrome/browser/resources/extensions/item.html +++ b/chrome/browser/resources/extensions/item.html
@@ -1,5 +1,6 @@ <link rel="import" href="chrome://resources/html/polymer.html"> +<link rel="import" href="chrome://resources/cr_elements/cr_icon_button/cr_icon_button.html"> <link rel="import" href="chrome://resources/cr_elements/cr_icons_css.html"> <link rel="import" href="chrome://resources/cr_elements/cr_toggle/cr_toggle.html"> <link rel="import" href="chrome://resources/cr_elements/hidden_style_css.html"> @@ -19,7 +20,6 @@ <link rel="import" href="strings.html"> <link rel="import" href="chrome://resources/polymer/v1_0/iron-flex-layout/iron-flex-layout-classes.html"> <link rel="import" href="chrome://resources/polymer/v1_0/iron-icon/iron-icon.html"> -<link rel="import" href="chrome://resources/polymer/v1_0/paper-icon-button/paper-icon-button-light.html"> <link rel="import" href="chrome://resources/polymer/v1_0/paper-tooltip/paper-tooltip.html"> <link rel="import" href="navigation_helper.html"> @@ -184,7 +184,7 @@ color: var(--error-color); } - #dev-reload-button-container { + #dev-reload-button { margin-inline-end: 12px; } @@ -321,12 +321,9 @@ </template> </div> <template is="dom-if" if="[[!computeDevReloadButtonHidden_(data.*)]]"> - <paper-icon-button-light id="dev-reload-button-container" - class="icon-refresh no-overlap"> - <button id="dev-reload-button" aria-label="$i18n{itemReload}" - aria-describedby="a11yAssociation" on-click="onReloadTap_"> - </button> - </paper-icon-button-light> + <cr-icon-button id="dev-reload-button" class="icon-refresh no-overlap" + aria-label="$i18n{itemReload}" aria-describedby="a11yAssociation" + on-click="onReloadTap_"></cr-icon-button> </template> <template is="dom-if" if="[[data.disableReasons.corruptInstall]]"> <paper-button id="repair-button" class="action-button"
diff --git a/chrome/browser/resources/extensions/kiosk_dialog.html b/chrome/browser/resources/extensions/kiosk_dialog.html index 302e64c..78eb1bae 100644 --- a/chrome/browser/resources/extensions/kiosk_dialog.html +++ b/chrome/browser/resources/extensions/kiosk_dialog.html
@@ -2,8 +2,9 @@ <link rel="import" href="chrome://resources/cr_elements/cr_checkbox/cr_checkbox.html"> <link rel="import" href="chrome://resources/cr_elements/cr_dialog/cr_dialog.html"> -<link rel="import" href="chrome://resources/cr_elements/cr_input/cr_input.html"> +<link rel="import" href="chrome://resources/cr_elements/cr_icon_button/cr_icon_button.html"> <link rel="import" href="chrome://resources/cr_elements/cr_icons_css.html"> +<link rel="import" href="chrome://resources/cr_elements/cr_input/cr_input.html"> <link rel="import" href="chrome://resources/cr_elements/paper_button_style_css.html"> <link rel="import" href="chrome://resources/cr_elements/shared_style_css.html"> <link rel="import" href="chrome://resources/html/assert.html"> @@ -11,7 +12,6 @@ <link rel="import" href="chrome://resources/html/util.html"> <link rel="import" href="chrome://resources/html/web_ui_listener_behavior.html"> <link rel="import" href="chrome://resources/polymer/v1_0/paper-button/paper-button.html"> -<link rel="import" href="chrome://resources/polymer/v1_0/paper-icon-button/paper-icon-button-light.html"> <link rel="import" href="item_behavior.html"> <link rel="import" href="kiosk_browser_proxy.html"> @@ -64,6 +64,10 @@ .list-item:hover .item-controls { visibility: visible; } + + cr-icon-button { + margin: 0; + } </style> <cr-dialog id="dialog" close-text="$i18n{close}" ignore-enter-key> @@ -90,9 +94,8 @@ '$i18nPolymer{kioskDisableAutoLaunch}', '$i18nPolymer{kioskEnableAutoLaunch}')]] </paper-button> - <paper-icon-button-light class="icon-delete-gray"> - <button on-click="onDeleteAppTap_"></button> - </paper-icon-button-light> + <cr-icon-button class="icon-delete-gray" + on-click="onDeleteAppTap_"></cr-icon-button> </div> </div> </template>
diff --git a/chrome/browser/resources/extensions/navigation_helper.html b/chrome/browser/resources/extensions/navigation_helper.html index 2521c5d5..4ebc7d4 100644 --- a/chrome/browser/resources/extensions/navigation_helper.html +++ b/chrome/browser/resources/extensions/navigation_helper.html
@@ -1,3 +1,5 @@ +<link rel="import" href="chrome://resources/html/polymer.html"> + <link rel="import" href="chrome://resources/html/assert.html"> <link rel="import" href="chrome://resources/html/cr.html"> <link rel="import" href="strings.html">
diff --git a/chrome/browser/resources/extensions/runtime_host_permissions.html b/chrome/browser/resources/extensions/runtime_host_permissions.html index 9eaad24..32ebf180 100644 --- a/chrome/browser/resources/extensions/runtime_host_permissions.html +++ b/chrome/browser/resources/extensions/runtime_host_permissions.html
@@ -1,6 +1,7 @@ <link rel="import" href="chrome://resources/html/polymer.html"> <link rel="import" href="chrome://resources/cr_elements/cr_action_menu/cr_action_menu.html"> +<link rel="import" href="chrome://resources/cr_elements/cr_icon_button/cr_icon_button.html"> <link rel="import" href="chrome://resources/cr_elements/cr_radio_group/cr_radio_group.html"> <link rel="import" href="chrome://resources/cr_elements/cr_radio_button/cr_radio_button.html"> <link rel="import" href="chrome://resources/cr_elements/paper_button_style_css.html"> @@ -63,6 +64,10 @@ cr-radio-button.multi-row { align-items: normal; } + + cr-icon-button { + margin: 0; + } </style> <div id="permissions-mode"> <div id="section-heading"> @@ -88,12 +93,9 @@ items="[[getRuntimeHosts_(permissions.hosts)]]"> <li> <div>[[item]]</div> - <paper-icon-button-light class="icon-more-vert"> - <button class="edit-host" - on-click="onEditHostClick_" - title="$i18n{hostPermissionsEdit}"> - </button> - </paper-icon-button-light> + <cr-icon-button class="icon-more-vert edit-host" + on-click="onEditHostClick_" + title="$i18n{hostPermissionsEdit}"></cr-icon-button> </li> </template> <li>
diff --git a/chrome/browser/resources/extensions/shared_style.html b/chrome/browser/resources/extensions/shared_style.html index 765e6c5..0410a18c 100644 --- a/chrome/browser/resources/extensions/shared_style.html +++ b/chrome/browser/resources/extensions/shared_style.html
@@ -81,10 +81,6 @@ * once .separator styling is extracted from settings. */ margin-inline-start: 0; } - - .separator + paper-icon-button-light { - margin-inline-start: var(--cr-icon-ripple-margin); - } </style> </template> </dom-module>
diff --git a/chrome/browser/resources/extensions/shortcut_input.html b/chrome/browser/resources/extensions/shortcut_input.html index 5d9ce55..e7c6b44 100644 --- a/chrome/browser/resources/extensions/shortcut_input.html +++ b/chrome/browser/resources/extensions/shortcut_input.html
@@ -1,12 +1,12 @@ <link rel="import" href="chrome://resources/html/polymer.html"> +<link rel="import" href="chrome://resources/cr_elements/cr_icon_button/cr_icon_button.html"> <link rel="import" href="chrome://resources/cr_elements/cr_icons_css.html"> <link rel="import" href="chrome://resources/cr_elements/cr_input/cr_input.html"> <link rel="import" href="chrome://resources/cr_elements/hidden_style_css.html"> <link rel="import" href="chrome://resources/html/assert.html"> <link rel="import" href="chrome://resources/html/cr.html"> <link rel="import" href="chrome://resources/html/i18n_behavior.html"> -<link rel="import" href="chrome://resources/polymer/v1_0/paper-icon-button/paper-icon-button-light.html"> <link rel="import" href="chrome://resources/polymer/v1_0/paper-styles/color.html"> <link rel="import" href="shortcut_util.html"> @@ -18,13 +18,13 @@ width: 200px; } - #clearContainer { - --cr-icon-ripple-size: 28px; + #clear { + --cr-icon-button-size: 28px; position: absolute; right: 2px; } - :host-context([dir='rtl']) #clearContainer { + :host-context([dir='rtl']) #clear { left: -2px; right: inherit; } @@ -36,11 +36,9 @@ '$i18nPolymer{shortcutTooManyModifiers}', '$i18nPolymer{shortcutNeedCharacter}')]]" value="[[computeText_(capturing_, shortcut, pendingShortcut_)]]"> - <paper-icon-button-light id="clearContainer" slot="suffix" - class="icon-cancel no-overlap" - hidden$="[[computeClearHidden_(capturing_, shortcut)]]"> - <button id="clear" on-click="onClearTap_"></button> - </paper-icon-button-light> + <cr-icon-button id="clear" slot="suffix" class="icon-cancel no-overlap" + hidden$="[[computeClearHidden_(capturing_, shortcut)]]" + on-click="onClearTap_"></cr-icon-button> </cr-input> </div> </template>
diff --git a/chrome/browser/resources/inline_login/inline_login.css b/chrome/browser/resources/inline_login/inline_login.css index f550f1c..a749a40f 100644 --- a/chrome/browser/resources/inline_login/inline_login.css +++ b/chrome/browser/resources/inline_login/inline_login.css
@@ -34,7 +34,8 @@ } #navigation-button { - color: white; + --cr-icon-button-color: white; + margin: 0; position: absolute; top: 0; visibility: hidden;
diff --git a/chrome/browser/resources/inline_login/inline_login.html b/chrome/browser/resources/inline_login/inline_login.html index 74a981386..ab87271 100644 --- a/chrome/browser/resources/inline_login/inline_login.html +++ b/chrome/browser/resources/inline_login/inline_login.html
@@ -3,8 +3,8 @@ <head> <title>$i18n{title}</title> <link rel="import" href="chrome://resources/html/polymer.html"> + <link rel="import" href="chrome://resources/cr_elements/cr_icon_button/cr_icon_button.html"> <link rel="import" href="chrome://resources/cr_elements/icons.html"> - <link rel="import" href="chrome://resources/polymer/v1_0/paper-icon-button/paper-icon-button.html"> <link rel="stylesheet" href="chrome://resources/css/spinner.css"> <link rel="stylesheet" href="chrome://resources/css/text_defaults.css"> <link rel="stylesheet" href="chrome://chrome-signin/inline_login.css"> @@ -23,7 +23,6 @@ <div class="spinner"></div> </div> </div> - <paper-icon-button id="navigation-button" - icon="cr:close"></paper-icon-button> + <cr-icon-button id="navigation-button" iron-icon="cr:close"></cr-icon-button> </body> </html>
diff --git a/chrome/browser/resources/inline_login/inline_login.js b/chrome/browser/resources/inline_login/inline_login.js index a77f3313..71a6b6b72 100644 --- a/chrome/browser/resources/inline_login/inline_login.js +++ b/chrome/browser/resources/inline_login/inline_login.js
@@ -134,7 +134,7 @@ } function showBackButton() { - $('navigation-button').icon = + $('navigation-button').ironIcon = isRTL() ? 'cr:arrow-forward' : 'cr:arrow-back'; $('navigation-button') @@ -143,7 +143,7 @@ } function showCloseButton() { - $('navigation-button').icon = 'cr:close'; + $('navigation-button').ironIcon = 'cr:close'; $('navigation-button').classList.add('enabled'); $('navigation-button') .setAttribute(
diff --git a/chrome/browser/resources/local_ntp/custom_backgrounds.css b/chrome/browser/resources/local_ntp/custom_backgrounds.css index 11a7f0ee..ab8067d 100644 --- a/chrome/browser/resources/local_ntp/custom_backgrounds.css +++ b/chrome/browser/resources/local_ntp/custom_backgrounds.css
@@ -87,6 +87,7 @@ #edit-bg-text { display: none; + user-select: none; } .ep-enhanced #edit-bg-text {
diff --git a/chrome/browser/resources/pdf/elements/shared-vars.html b/chrome/browser/resources/pdf/elements/shared-vars.html index 2ba13040..164abe7c 100644 --- a/chrome/browser/resources/pdf/elements/shared-vars.html +++ b/chrome/browser/resources/pdf/elements/shared-vars.html
@@ -7,12 +7,6 @@ html { --iron-icon-height: 20px; --iron-icon-width: 20px; - --paper-icon-button: { - height: 32px; - padding: 6px; - width: 32px; - }; - --paper-icon-button-ink-color: rgb(189, 189, 189); --viewer-icon-ink-color: rgb(189, 189, 189); } </style>
diff --git a/chrome/browser/resources/pdf/elements/viewer-bookmark/viewer-bookmark.html b/chrome/browser/resources/pdf/elements/viewer-bookmark/viewer-bookmark.html index 174b498..d3c1167 100644 --- a/chrome/browser/resources/pdf/elements/viewer-bookmark/viewer-bookmark.html +++ b/chrome/browser/resources/pdf/elements/viewer-bookmark/viewer-bookmark.html
@@ -1,5 +1,6 @@ <link rel="import" href="chrome://resources/html/polymer.html"> +<link rel="import" href="chrome://resources/cr_elements/cr_icon_button/cr_icon_button.html"> <link rel="import" href="chrome://resources/cr_elements/icons.html"> <link rel="import" href="chrome://resources/polymer/v1_0/paper-ripple/paper-ripple.html"> <link rel="import" href="chrome://resources/polymer/v1_0/paper-styles/color.html"> @@ -36,14 +37,11 @@ } #expand { - --iron-icon-height: 16px; - --iron-icon-width: 16px; - --paper-icon-button-ink-color: var(--paper-grey-900); - height: 28px; - min-width: 28px; - padding: 6px; + --cr-icon-button-color: var(--primary-text-color); + --cr-icon-button-icon-size: 16px; + --cr-icon-button-size: 28px; + margin: 0; transition: transform 150ms; - width: 28px; } :host-context([dir=rtl]) #expand { @@ -56,9 +54,8 @@ </style> <div id="item" on-click="onClick"> <paper-ripple></paper-ripple> - <paper-icon-button id="expand" icon="cr:chevron-right" - on-click="toggleChildren"> - </paper-icon-button> + <cr-icon-button id="expand" iron-icon="cr:chevron-right" + on-click="toggleChildren"></cr-icon-button> <span id="title" tabindex="0">{{bookmark.title}}</span> </div> <!-- dom-if will stamp the complex bookmark tree lazily as individual nodes
diff --git a/chrome/browser/resources/pdf/elements/viewer-bookmark/viewer-bookmark.js b/chrome/browser/resources/pdf/elements/viewer-bookmark/viewer-bookmark.js index baad577..160191b6 100644 --- a/chrome/browser/resources/pdf/elements/viewer-bookmark/viewer-bookmark.js +++ b/chrome/browser/resources/pdf/elements/viewer-bookmark/viewer-bookmark.js
@@ -91,7 +91,7 @@ }, onSpace_: function(e) { - // paper-icon-button stops propagation of space events, so there's no need + // cr-icon-button stops propagation of space events, so there's no need // to check the event source here. this.onClick(); // Prevent default space scroll behavior.
diff --git a/chrome/browser/resources/pdf/elements/viewer-pdf-toolbar/viewer-pdf-toolbar.html b/chrome/browser/resources/pdf/elements/viewer-pdf-toolbar/viewer-pdf-toolbar.html index 921ce0f2..2f87e5e 100644 --- a/chrome/browser/resources/pdf/elements/viewer-pdf-toolbar/viewer-pdf-toolbar.html +++ b/chrome/browser/resources/pdf/elements/viewer-pdf-toolbar/viewer-pdf-toolbar.html
@@ -1,7 +1,7 @@ <link rel="import" href="chrome://resources/html/polymer.html"> +<link rel="import" href="chrome://resources/cr_elements/cr_icon_button/cr_icon_button.html"> <link rel="import" href="chrome://resources/cr_elements/hidden_style_css.html"> -<link rel="import" href="chrome://resources/polymer/v1_0/paper-icon-button/paper-icon-button.html"> <link rel="import" href="chrome://resources/polymer/v1_0/paper-progress/paper-progress.html"> <link rel="import" href="chrome://resources/cr_elements/icons.html"> <link rel="import" href="../icons.html"> @@ -51,23 +51,16 @@ user-select: none; } - paper-icon-button { - height: 36px; + cr-icon-button { + --cr-icon-button-color: rgb(241, 241, 241); margin: 6px; - padding: 8px; - width: 36px; } - paper-icon-button:hover { + cr-icon-button:hover { background: rgba(255, 255, 255, 0.08); border-radius: 50%; } - paper-icon-button:focus { - --paper-icon-button-ink-color:white; - --paper-ripple-opacity: 0.24; - } - paper-progress { --paper-progress-active-color: var(--google-blue-300); --paper-progress-container-color: transparent; @@ -195,32 +188,24 @@ <div id="buttons" class="invisible"> <template is="dom-if" if="[[pdfAnnotationsEnabled]]"> - <paper-icon-button id="annotate" icon="pdf:create" - disabled="[[!annotationAvailable]]" - on-click="toggleAnnotation" + <cr-icon-button id="annotate" iron-icon="pdf:create" + disabled="[[!annotationAvailable]]" on-click="toggleAnnotation" aria-label$="{{strings.tooltipAnnotate}}" - title$="{{strings.tooltipAnnotate}}"> - </paper-icon-button> + title$="{{strings.tooltipAnnotate}}"></cr-icon-button> </template> - <paper-icon-button id="rotate-right" icon="pdf:rotate-right" - disabled="[[annotationMode]]" - on-click="rotateRight" + <cr-icon-button id="rotate-right" iron-icon="pdf:rotate-right" + disabled="[[annotationMode]]" on-click="rotateRight" aria-label$="{{strings.tooltipRotateCW}}" - title$="{{strings.tooltipRotateCW}}"> - </paper-icon-button> + title$="{{strings.tooltipRotateCW}}"></cr-icon-button> - <paper-icon-button id="download" icon="cr:file-download" - on-click="download" - aria-label$="{{strings.tooltipDownload}}" - title$="{{strings.tooltipDownload}}"> - </paper-icon-button> + <cr-icon-button id="download" iron-icon="cr:file-download" + on-click="download" aria-label$="{{strings.tooltipDownload}}" + title$="{{strings.tooltipDownload}}"></cr-icon-button> - <paper-icon-button id="print" icon="cr:print" - on-click="print" + <cr-icon-button id="print" iron-icon="cr:print" on-click="print" aria-label$="{{strings.tooltipPrint}}" - title$="{{strings.tooltipPrint}}"> - </paper-icon-button> + title$="{{strings.tooltipPrint}}"></cr-icon-button> <viewer-toolbar-dropdown id="bookmarks" selected @@ -229,8 +214,8 @@ open-icon="pdf:bookmark" closed-icon="pdf:bookmark-border" header="{{strings.bookmarks}}"> - <viewer-bookmarks-content bookmarks="{{bookmarks}}"> - </viewer-bookmarks-content> + <viewer-bookmarks-content bookmarks="{{bookmarks}}"> + </viewer-bookmarks-content> </viewer-toolbar-dropdown> </div> </div> @@ -280,31 +265,23 @@ </viewer-pen-options> </viewer-toolbar-dropdown> - <paper-icon-button id="eraser" + <cr-icon-button id="eraser" selected$="[[equal_('eraser', annotationTool.tool)]]" - on-click="annotationToolClicked_" - icon="pdf:eraser" + on-click="annotationToolClicked_" iron-icon="pdf:eraser" aria-label$="{{strings.annotationEraser}}" - title$="{{strings.annotationEraser}}"> - </paper-icon-button> + title$="{{strings.annotationEraser}}"></cr-icon-button> <div id="annotation-separator"></div> - <paper-icon-button id="undo" - disabled="[[!canUndoAnnotation]]" - icon="pdf:undo" - on-click="undo" + <cr-icon-button id="undo" disabled="[[!canUndoAnnotation]]" + iron-icon="pdf:undo" on-click="undo" aria-label$="{{strings.annotationUndo}}" - title$="{{strings.annotationUndo}}"> - </paper-icon-button> + title$="{{strings.annotationUndo}}"></cr-icon-button> - <paper-icon-button id="redo" - disabled="[[!canRedoAnnotation]]" - icon="pdf:redo" - on-click="redo" + <cr-icon-button id="redo" disabled="[[!canRedoAnnotation]]" + iron-icon="pdf:redo" on-click="redo" aria-label$="{{strings.annotationRedo}}" - title$="{{strings.annotationRedo}}"> - </paper-icon-button> + title$="{{strings.annotationRedo}}"></cr-icon-button> </div> </template> <script src="viewer-pdf-toolbar.js"></script>
diff --git a/chrome/browser/resources/pdf/elements/viewer-pen-options/viewer-pen-options.html b/chrome/browser/resources/pdf/elements/viewer-pen-options/viewer-pen-options.html index 3151736c..dbf18b0 100644 --- a/chrome/browser/resources/pdf/elements/viewer-pen-options/viewer-pen-options.html +++ b/chrome/browser/resources/pdf/elements/viewer-pen-options/viewer-pen-options.html
@@ -1,5 +1,6 @@ <link rel="import" href="chrome://resources/html/polymer.html"> -<link rel="import" href="chrome://resources/polymer/v1_0/paper-icon-button/paper-icon-button.html"> + +<link rel="import" href="chrome://resources/cr_elements/cr_icon_button/cr_icon_button.html"> <dom-module id="viewer-pen-options"> <template> @@ -14,21 +15,20 @@ #colors { overflow: hidden; } - input, #expand { - height: 32px; - width: 32px; - } - #expand { + --cr-icon-button-icon-size: 16px; + --cr-icon-button-size: 32px; grid-column: 8; grid-row: 1 / 4; + margin: 0; } input { -webkit-appearance: none; border-radius: 16px; + height: 32px; margin: 0; padding: 0; - + width: 32px; } #sizes input { background: black; @@ -70,12 +70,9 @@ aria-label$="[[lookup_(strings, item.name)]]" on-pointerdown="blurOnPointerDown"> </template> - <paper-icon-button id="expand" icon="cr:expand-more" - tabindex="3" - on-click="toggleExpanded_" - aria-label$="[[strings.annotationExpand]]" - title$="[[strings.annotationExpand]]"> - </paper-icon-button> + <cr-icon-button id="expand" iron-icon="cr:expand-more" tabindex="3" + on-click="toggleExpanded_" aria-label$="[[strings.annotationExpand]]" + title$="[[strings.annotationExpand]]"></cr-icon-button> </div> <div id="separator"></div> <div id="sizes" on-change="sizeChanged_"> @@ -88,7 +85,6 @@ on-pointerdown="blurOnPointerDown"> </template> </div> - </paper-icon-button> </template> <script src="viewer-pen-options.js"></script> </dom-module>
diff --git a/chrome/browser/resources/print_preview/new/pin_settings.js b/chrome/browser/resources/print_preview/new/pin_settings.js index fc87cfc0..0aed18d 100644 --- a/chrome/browser/resources/print_preview/new/pin_settings.js +++ b/chrome/browser/resources/print_preview/new/pin_settings.js
@@ -73,7 +73,7 @@ * @private */ computeCheckboxDisabled_: function(inputValid, disabled, managed) { - return inputValid && (disabled || managed); + return managed || (inputValid && disabled); }, /**
diff --git a/chrome/browser/resources/print_preview/new/select_behavior.html b/chrome/browser/resources/print_preview/new/select_behavior.html index f6ef628..2059526 100644 --- a/chrome/browser/resources/print_preview/new/select_behavior.html +++ b/chrome/browser/resources/print_preview/new/select_behavior.html
@@ -1,3 +1,5 @@ +<link rel="import" href="chrome://resources/html/polymer.html"> + <link rel="import" href="chrome://resources/html/cr.html"> <script src="select_behavior.js"></script>
diff --git a/chrome/browser/resources/print_preview/print_preview_new.html b/chrome/browser/resources/print_preview/print_preview_new.html index fa13d77..e2408a5 100644 --- a/chrome/browser/resources/print_preview/print_preview_new.html +++ b/chrome/browser/resources/print_preview/print_preview_new.html
@@ -1,6 +1,6 @@ <!doctype html> <html dir="$i18n{textdirection}" lang="$i18n{language}" class="loading" - $i18n{dark}> + $i18n{dark} $i18n{newprintpreviewlayout}> <head> <meta charset="utf-8"> <style> @@ -14,6 +14,14 @@ background: rgb(189, 193, 198); /* --google-grey-400 */ } + html[new-print-preview-layout] { + background: rgb(218, 220, 224); /* --google-grey-refresh-300 */ + } + + html[new-print-preview-layout][dark] { + background: rgb(95, 99, 104); /* --google-grey-refresh-700 */ + } + html, body { height: 100%; @@ -22,18 +30,36 @@ width: 100%; } + .loading body { + display: flex; + } + + [new-print-preview-layout].loading body { + flex-direction: row-reverse; + } + .loading body::before { background: white; - border-inline-end: 1px solid rgb(232, 234, 237); + border-bottom-width: 0; + border-color: rgb(232, 234, 237); + border-inline-end-width: 1px; + border-inline-start-width: 0; + border-style: solid; + border-top-width: 0; content: ''; display: block; height: 100%; width: 311px; } + [new-print-preview-layout].loading body::before { + border-inline-end-width: 0; + border-inline-start-width: 1px; + } + [dark].loading body::before { background: rgb(40, 41, 44); - border-inline-end-color: rgba(255, 255, 255, .04); + border-color: rgba(255, 255, 255, .04); } </style> </head>
diff --git a/chrome/browser/resources/settings/page_visibility.js b/chrome/browser/resources/settings/page_visibility.js index 0dee440..c475e67 100644 --- a/chrome/browser/resources/settings/page_visibility.js +++ b/chrome/browser/resources/settings/page_visibility.js
@@ -66,8 +66,7 @@ */ let pageVisibility; - const showOSSettings = loadTimeData.valueExists('showOSSettings') && - loadTimeData.getBoolean('showOSSettings'); + const showOSSettings = loadTimeData.getBoolean('showOSSettings'); if (loadTimeData.getBoolean('isGuest')) { // "if not chromeos" and "if chromeos" in two completely separate blocks
diff --git a/chrome/browser/resources/settings/settings_resources.grd b/chrome/browser/resources/settings/settings_resources.grd index 830ccc5..fa4b6f25 100644 --- a/chrome/browser/resources/settings/settings_resources.grd +++ b/chrome/browser/resources/settings/settings_resources.grd
@@ -34,7 +34,7 @@ <structure name="IDR_SETTINGS_TTS_SUBPAGE_HTML" file="a11y_page/tts_subpage.html" type="chrome_html" /> - <structure name="IDR_MD_SETTINGS_MANIFEST" + <structure name="IDR_SETTINGS_MANIFEST" file="manifest.json" type="chrome_html" />
diff --git a/chrome/browser/resources/settings/settings_resources_vulcanized.grd b/chrome/browser/resources/settings/settings_resources_vulcanized.grd index d6163532..5420b5ed 100644 --- a/chrome/browser/resources/settings/settings_resources_vulcanized.grd +++ b/chrome/browser/resources/settings/settings_resources_vulcanized.grd
@@ -12,13 +12,13 @@ </outputs> <release seq="1"> <includes> - <include name="IDR_MD_SETTINGS_VULCANIZED_HTML" file="${root_gen_dir}\chrome\browser\resources\settings\vulcanized.html" use_base_dir="false" flattenhtml="true" allowexternalscript="true" type="BINDATA" compress="gzip" /> - <include name="IDR_MD_SETTINGS_VULCANIZED_P2_HTML" file="${root_gen_dir}\chrome\browser\resources\settings\vulcanized.p2.html" use_base_dir="false" flattenhtml="true" allowexternalscript="true" type="BINDATA" compress="gzip" /> - <include name="IDR_MD_SETTINGS_CRISPER_JS" file="${root_gen_dir}\chrome\browser\resources\settings\crisper.js" use_base_dir="false" flattenhtml="true" type="BINDATA" compress="gzip" /> - <include name="IDR_MD_SETTINGS_LAZY_LOAD_VULCANIZED_HTML" file="${root_gen_dir}\chrome\browser\resources\settings\lazy_load.vulcanized.html" use_base_dir="false" flattenhtml="true" allowexternalscript="true" type="BINDATA" compress="gzip" /> - <include name="IDR_MD_SETTINGS_LAZY_LOAD_VULCANIZED_P2_HTML" file="${root_gen_dir}\chrome\browser\resources\settings\lazy_load.vulcanized.p2.html" use_base_dir="false" flattenhtml="true" allowexternalscript="true" type="BINDATA" compress="gzip" /> - <include name="IDR_MD_SETTINGS_LAZY_LOAD_CRISPER_JS" file="${root_gen_dir}\chrome\browser\resources\settings\lazy_load.crisper.js" use_base_dir="false" flattenhtml="true" type="BINDATA" compress="gzip" /> - <include name="IDR_MD_SETTINGS_MANIFEST" file="manifest.json" type="BINDATA" compress="gzip" /> + <include name="IDR_SETTINGS_VULCANIZED_HTML" file="${root_gen_dir}\chrome\browser\resources\settings\vulcanized.html" use_base_dir="false" flattenhtml="true" allowexternalscript="true" type="BINDATA" compress="gzip" /> + <include name="IDR_SETTINGS_VULCANIZED_P2_HTML" file="${root_gen_dir}\chrome\browser\resources\settings\vulcanized.p2.html" use_base_dir="false" flattenhtml="true" allowexternalscript="true" type="BINDATA" compress="gzip" /> + <include name="IDR_SETTINGS_CRISPER_JS" file="${root_gen_dir}\chrome\browser\resources\settings\crisper.js" use_base_dir="false" flattenhtml="true" type="BINDATA" compress="gzip" /> + <include name="IDR_SETTINGS_LAZY_LOAD_VULCANIZED_HTML" file="${root_gen_dir}\chrome\browser\resources\settings\lazy_load.vulcanized.html" use_base_dir="false" flattenhtml="true" allowexternalscript="true" type="BINDATA" compress="gzip" /> + <include name="IDR_SETTINGS_LAZY_LOAD_VULCANIZED_P2_HTML" file="${root_gen_dir}\chrome\browser\resources\settings\lazy_load.vulcanized.p2.html" use_base_dir="false" flattenhtml="true" allowexternalscript="true" type="BINDATA" compress="gzip" /> + <include name="IDR_SETTINGS_LAZY_LOAD_CRISPER_JS" file="${root_gen_dir}\chrome\browser\resources\settings\lazy_load.crisper.js" use_base_dir="false" flattenhtml="true" type="BINDATA" compress="gzip" /> + <include name="IDR_SETTINGS_MANIFEST" file="manifest.json" type="BINDATA" compress="gzip" /> </includes> </release> </grit>
diff --git a/chrome/browser/resources/settings/settings_ui/settings_ui.js b/chrome/browser/resources/settings/settings_ui/settings_ui.js index 35ea54ca..2d14eb3 100644 --- a/chrome/browser/resources/settings/settings_ui/settings_ui.js +++ b/chrome/browser/resources/settings/settings_ui/settings_ui.js
@@ -143,15 +143,23 @@ }; // </if> - this.showAndroidApps_ = loadTimeData.valueExists('androidAppsVisible') && + // The SplitSettings feature hides OS settings in the browser settings page. + // https://crbug.com/950007 + const showOSSettings = loadTimeData.getBoolean('showOSSettings'); + this.showAndroidApps_ = showOSSettings && + loadTimeData.valueExists('androidAppsVisible') && loadTimeData.getBoolean('androidAppsVisible'); - this.showKioskNextShell_ = loadTimeData.valueExists('showKioskNextShell') && + this.showKioskNextShell_ = showOSSettings && + loadTimeData.valueExists('showKioskNextShell') && loadTimeData.getBoolean('showKioskNextShell'); - this.showCrostini_ = loadTimeData.valueExists('showCrostini') && + this.showCrostini_ = showOSSettings && + loadTimeData.valueExists('showCrostini') && loadTimeData.getBoolean('showCrostini'); - this.showPluginVm_ = loadTimeData.valueExists('showPluginVm') && + this.showPluginVm_ = showOSSettings && + loadTimeData.valueExists('showPluginVm') && loadTimeData.getBoolean('showPluginVm'); - this.havePlayStoreApp_ = loadTimeData.valueExists('havePlayStoreApp') && + this.havePlayStoreApp_ = showOSSettings && + loadTimeData.valueExists('havePlayStoreApp') && loadTimeData.getBoolean('havePlayStoreApp'); this.addEventListener('show-container', () => {
diff --git a/chrome/browser/resources/welcome/onboarding_welcome/navigation_behavior.html b/chrome/browser/resources/welcome/onboarding_welcome/navigation_behavior.html index 2bde7e1..1488b4d 100644 --- a/chrome/browser/resources/welcome/onboarding_welcome/navigation_behavior.html +++ b/chrome/browser/resources/welcome/onboarding_welcome/navigation_behavior.html
@@ -1,3 +1,5 @@ +<link rel="import" href="chrome://resources/html/polymer.html"> + <link rel="import" href="chrome://resources/html/assert.html"> <link rel="import" href="chrome://resources/html/cr.html">
diff --git a/chrome/browser/ui/BUILD.gn b/chrome/browser/ui/BUILD.gn index b9405d4..5b302c4 100644 --- a/chrome/browser/ui/BUILD.gn +++ b/chrome/browser/ui/BUILD.gn
@@ -1216,10 +1216,6 @@ "webui/settings/font_handler.h", "webui/settings/languages_handler.cc", "webui/settings/languages_handler.h", - "webui/settings/md_settings_localized_strings_provider.cc", - "webui/settings/md_settings_localized_strings_provider.h", - "webui/settings/md_settings_ui.cc", - "webui/settings/md_settings_ui.h", "webui/settings/metrics_reporting_handler.cc", "webui/settings/metrics_reporting_handler.h", "webui/settings/on_startup_handler.cc", @@ -1240,6 +1236,8 @@ "webui/settings/settings_cookies_view_handler.h", "webui/settings/settings_import_data_handler.cc", "webui/settings/settings_import_data_handler.h", + "webui/settings/settings_localized_strings_provider.cc", + "webui/settings/settings_localized_strings_provider.h", "webui/settings/settings_media_devices_selection_handler.cc", "webui/settings/settings_media_devices_selection_handler.h", "webui/settings/settings_page_ui_handler.cc", @@ -1248,6 +1246,8 @@ "webui/settings/settings_security_key_handler.h", "webui/settings/settings_startup_pages_handler.cc", "webui/settings/settings_startup_pages_handler.h", + "webui/settings/settings_ui.cc", + "webui/settings/settings_ui.h", "webui/settings/site_settings_handler.cc", "webui/settings/site_settings_handler.h", "webui/settings_utils.cc",
diff --git a/chrome/browser/ui/cocoa/touchbar/browser_window_default_touch_bar.h b/chrome/browser/ui/cocoa/touchbar/browser_window_default_touch_bar.h index da3f60c..aab52b7 100644 --- a/chrome/browser/ui/cocoa/touchbar/browser_window_default_touch_bar.h +++ b/chrome/browser/ui/cocoa/touchbar/browser_window_default_touch_bar.h
@@ -29,12 +29,6 @@ // True if the current page is starred. Used by star touch bar button. @property(nonatomic, assign) BOOL isStarred; -// True if the back button is enabled. -@property(nonatomic, assign) BOOL canGoBack; - -// True if the forward button is enabled. -@property(nonatomic, assign) BOOL canGoForward; - // Designated initializer. - (instancetype)initWithBrowser:(Browser*)browser controller:(BrowserWindowTouchBarController*)controller; @@ -44,6 +38,10 @@ - (void)updateWebContents:(content::WebContents*)contents; +// Updates the back/forward button. Called when creating the touch bar or when +// the back and forward commands have changed. +- (void)updateBackForwardControl; + - (BrowserWindowTouchBarController*)controller; @end @@ -51,11 +49,6 @@ // Private methods exposed for testing. @interface BrowserWindowDefaultTouchBar (ExposedForTesting) -@property(readonly, class) NSString* reloadOrStopItemIdentifier; -@property(readonly, class) NSString* backItemIdentifier; -@property(readonly, class) NSString* forwardItemIdentifier; -@property(readonly, class) NSString* fullscreenOriginItemIdentifier; - // Updates the reload/stop button. Called when creating the touch bar or the // page load state has been updated. - (void)updateReloadStopButton; @@ -63,6 +56,10 @@ // Returns the reload/stop button on the touch bar. Creates it if it's null. - (NSButton*)reloadStopButton; +// Returns the back/forward segmented control on the touch bar. Creates it if +// it's null. +- (NSSegmentedControl*)backForwardControl; + // Returns the bridge object that BrowserWindowDefaultTouchBar uses to receive // notifications. - (BookmarkTabHelperObserver*)bookmarkTabObserver;
diff --git a/chrome/browser/ui/cocoa/touchbar/browser_window_default_touch_bar.mm b/chrome/browser/ui/cocoa/touchbar/browser_window_default_touch_bar.mm index d4f5f37..84099212 100644 --- a/chrome/browser/ui/cocoa/touchbar/browser_window_default_touch_bar.mm +++ b/chrome/browser/ui/cocoa/touchbar/browser_window_default_touch_bar.mm
@@ -51,8 +51,7 @@ NSString* const kTabFullscreenTouchBarId = @"tab-fullscreen"; // Touch bar items identifiers. -NSString* const kBackTouchId = @"BACK"; -NSString* const kForwardTouchId = @"FORWARD"; +NSString* const kBackForwardTouchId = @"BACK-FWD"; NSString* const kReloadOrStopTouchId = @"RELOAD-STOP"; NSString* const kHomeTouchId = @"HOME"; NSString* const kSearchTouchId = @"SEARCH"; @@ -60,11 +59,9 @@ NSString* const kNewTabTouchId = @"NEW-TAB"; NSString* const kFullscreenOriginLabelTouchId = @"FULLSCREEN-ORIGIN-LABEL"; -// This is a combined back and forward control which can no longer be selected -// but may be in an existing customized Touch Bar. It now represents a group -// containing the back and forward buttons, and adding the back or forward -// buttons to the Touch Bar individually magically decomposes the group. -NSString* const kBackForwardTouchId = @"BACK-FWD"; +// The button indexes in the back and forward segment control. +const int kBackSegmentIndex = 0; +const int kForwardSegmentIndex = 1; // Touch bar icon colors values. const SkColor kTouchBarDefaultIconColor = SK_ColorWHITE; @@ -98,7 +95,7 @@ target:owner action:@selector(executeCommand:)]; button.tag = command; - button.accessibilityTitle = l10n_util::GetNSString(tooltip_id); + [button setAccessibilityLabel:l10n_util::GetNSString(tooltip_id)]; return button; } @@ -179,10 +176,7 @@ // CommandObserver: void EnabledStateChangedForCommand(int command, bool enabled) override { DCHECK(command == IDC_BACK || command == IDC_FORWARD); - if (command == IDC_BACK) - owner_.canGoBack = enabled; - else if (command == IDC_FORWARD) - owner_.canGoForward = enabled; + [owner_ updateBackForwardControl]; } // WebContentsObserver: @@ -209,6 +203,10 @@ DISALLOW_COPY_AND_ASSIGN(TouchBarNotificationBridge); }; +id<NSAccessibility> ToNSAccessibility(id object) { + return [object conformsToProtocol:@protocol(NSAccessibility)] ? object : nil; +} + } // namespace @interface BrowserWindowDefaultTouchBar () { @@ -232,6 +230,9 @@ // The stop/reload button in the touch bar. base::scoped_nsobject<NSButton> reloadStopButton_; + // The back/forward segmented control in the touch bar. + base::scoped_nsobject<NSSegmentedControl> backForwardControl_; + // The starred button in the touch bar. base::scoped_nsobject<NSButton> starredButton_; } @@ -239,6 +240,9 @@ // Creates and returns a touch bar for tab fullscreen mode. - (NSTouchBar*)createTabFullscreenTouchBar; +// Sets up the back and forward segmented control. +- (void)setupBackForwardControl; + // Updates the starred button in the touch bar. - (void)updateStarredButton; @@ -251,8 +255,6 @@ @synthesize isPageLoading = isPageLoading_; @synthesize isStarred = isStarred_; -@synthesize canGoBack = canGoBack_; -@synthesize canGoForward = canGoForward_; - (instancetype)initWithBrowser:(Browser*)browser controller:(BrowserWindowTouchBarController*)controller { @@ -264,12 +266,8 @@ notificationBridge_.reset(new TouchBarNotificationBridge(self, browser)); commandUpdater_ = browser->command_controller(); - commandUpdater_->AddCommandObserver(IDC_BACK, notificationBridge_.get()); - self.canGoBack = commandUpdater_->IsCommandEnabled(IDC_BACK); - commandUpdater_->AddCommandObserver(IDC_FORWARD, notificationBridge_.get()); - self.canGoForward = commandUpdater_->IsCommandEnabled(IDC_FORWARD); PrefService* prefs = browser->profile()->GetPrefs(); showHomeButton_.Init( @@ -303,12 +301,12 @@ setCustomizationIdentifier:ui::GetTouchBarId(kBrowserWindowTouchBarId)]; [touchBar setDelegate:self]; - NSMutableArray<NSString*>* customIdentifiers = [NSMutableArray array]; - NSMutableArray<NSString*>* defaultIdentifiers = [NSMutableArray array]; + NSMutableArray* customIdentifiers = [NSMutableArray arrayWithCapacity:7]; + NSMutableArray* defaultIdentifiers = [NSMutableArray arrayWithCapacity:6]; - NSArray<NSString*>* touchBarItems = @[ - kBackTouchId, kForwardTouchId, kReloadOrStopTouchId, kHomeTouchId, - kSearchTouchId, kStarTouchId, kNewTabTouchId + NSArray* touchBarItems = @[ + kBackForwardTouchId, kReloadOrStopTouchId, kHomeTouchId, kSearchTouchId, + kStarTouchId, kNewTabTouchId ]; for (NSString* item in touchBarItems) { @@ -334,41 +332,14 @@ if (!touchBar) return nil; - if ([identifier hasSuffix:kBackForwardTouchId]) { - auto* items = @[ - [touchBar itemForIdentifier:ui::GetTouchBarItemId( - kBrowserWindowTouchBarId, kBackTouchId)], - [touchBar - itemForIdentifier:ui::GetTouchBarItemId(kBrowserWindowTouchBarId, - kForwardTouchId)], - ]; - auto groupItem = [NSGroupTouchBarItem groupItemWithIdentifier:identifier - items:items]; - [groupItem setCustomizationLabel: - l10n_util::GetNSString( - IDS_TOUCH_BAR_BACK_FORWARD_CUSTOMIZATION_LABEL)]; - return groupItem; - } - base::scoped_nsobject<NSCustomTouchBarItem> touchBarItem( [[ui::NSCustomTouchBarItem() alloc] initWithIdentifier:identifier]); - if ([identifier hasSuffix:kBackTouchId]) { - auto* button = CreateTouchBarButton(vector_icons::kBackArrowIcon, self, - IDC_BACK, IDS_ACCNAME_BACK); - [button bind:@"enabled" toObject:self withKeyPath:@"canGoBack" options:nil]; - [touchBarItem setView:button]; - [touchBarItem - setCustomizationLabel:l10n_util::GetNSString(IDS_ACCNAME_BACK)]; - } else if ([identifier hasSuffix:kForwardTouchId]) { - auto* button = CreateTouchBarButton(vector_icons::kForwardArrowIcon, self, - IDC_FORWARD, IDS_ACCNAME_FORWARD); - [button bind:@"enabled" - toObject:self - withKeyPath:@"canGoForward" - options:nil]; - [touchBarItem setView:button]; - [touchBarItem - setCustomizationLabel:l10n_util::GetNSString(IDS_ACCNAME_FORWARD)]; + if ([identifier hasSuffix:kBackForwardTouchId]) { + [self updateBackForwardControl]; + [touchBarItem setView:backForwardControl_.get()]; + [touchBarItem setCustomizationLabel: + l10n_util::GetNSString( + IDS_TOUCH_BAR_BACK_FORWARD_CUSTOMIZATION_LABEL)]; } else if ([identifier hasSuffix:kReloadOrStopTouchId]) { [self updateReloadStopButton]; [touchBarItem setView:reloadStopButton_.get()]; @@ -427,8 +398,6 @@ [touchBarItem setView:[NSTextField labelWithAttributedString:attributedString.get()]]; - } else { - return nil; } return touchBarItem.autorelease(); @@ -443,10 +412,63 @@ return touchBar.autorelease(); } +- (void)setupBackForwardControl { + NSMutableArray* images = [NSMutableArray arrayWithArray:@[ + CreateNSImageFromIcon(vector_icons::kBackArrowIcon), + CreateNSImageFromIcon(vector_icons::kForwardArrowIcon) + ]]; + + // Offset the icons so that it matches the height of the other Touch Bar + // items. + const int kIconYOffset = 2; + for (NSUInteger i = 0; i < [images count]; i++) { + NSImage* image = [images objectAtIndex:i]; + NSSize size = [image size]; + size.height += kIconYOffset; + + NSImage* offsettedImage = [[[NSImage alloc] initWithSize:size] autorelease]; + [offsettedImage lockFocus]; + [image drawInRect:NSMakeRect(0, 0, size.width, size.height - kIconYOffset)]; + [offsettedImage unlockFocus]; + [images replaceObjectAtIndex:i withObject:offsettedImage]; + } + + NSSegmentedControl* control = [NSSegmentedControl + segmentedControlWithImages:images + trackingMode:NSSegmentSwitchTrackingMomentary + target:self + action:@selector(backOrForward:)]; + + // Use the accessibility protocol to get the children. + // Use NSAccessibilityUnignoredDescendant to be sure we start with + // the correct object. + id<NSAccessibility> segmentElement = + ToNSAccessibility(NSAccessibilityUnignoredDescendant(control)); + DCHECK(segmentElement); + NSArray<id<NSAccessibility>>* segments = segmentElement.accessibilityChildren; + ToNSAccessibility(segments[0]).accessibilityTitle = + l10n_util::GetNSString(IDS_ACCNAME_BACK); + ToNSAccessibility(segments[1]).accessibilityTitle = + l10n_util::GetNSString(IDS_ACCNAME_FORWARD); + + backForwardControl_.reset([control retain]); +} + - (void)updateWebContents:(content::WebContents*)contents { notificationBridge_->UpdateWebContents(contents); } +- (void)updateBackForwardControl { + if (!backForwardControl_) + [self setupBackForwardControl]; + + [backForwardControl_ setSegmentStyle:NSSegmentStyleSeparated]; + [backForwardControl_ setEnabled:commandUpdater_->IsCommandEnabled(IDC_BACK) + forSegment:kBackSegmentIndex]; + [backForwardControl_ setEnabled:commandUpdater_->IsCommandEnabled(IDC_FORWARD) + forSegment:kForwardSegmentIndex]; +} + - (void)updateStarredButton { const gfx::VectorIcon& icon = isStarred_ ? omnibox::kStarActiveIcon : omnibox::kStarIcon; @@ -510,6 +532,14 @@ return searchButton; } +- (void)backOrForward:(id)sender { + NSSegmentedControl* control = sender; + int command = + [control selectedSegment] == kBackSegmentIndex ? IDC_BACK : IDC_FORWARD; + LogTouchBarUMA(TouchBarActionFromCommand(command)); + commandUpdater_->ExecuteCommand(command); +} + - (void)executeCommand:(id)sender { int command = [sender tag]; ui::LogTouchBarUMA(TouchBarActionFromCommand(command)); @@ -531,23 +561,6 @@ // Private methods exposed for testing. @implementation BrowserWindowDefaultTouchBar (ExposedForTesting) -+ (NSString*)reloadOrStopItemIdentifier { - return ui::GetTouchBarItemId(kBrowserWindowTouchBarId, kReloadOrStopTouchId); -} - -+ (NSString*)backItemIdentifier { - return ui::GetTouchBarItemId(kBrowserWindowTouchBarId, kBackTouchId); -} - -+ (NSString*)forwardItemIdentifier { - return ui::GetTouchBarItemId(kBrowserWindowTouchBarId, kForwardTouchId); -} - -+ (NSString*)fullscreenOriginItemIdentifier { - return ui::GetTouchBarItemId(kTabFullscreenTouchBarId, - kFullscreenOriginLabelTouchId); -} - - (void)updateReloadStopButton { const gfx::VectorIcon& icon = isPageLoading_ ? kNavigateStopIcon : vector_icons::kReloadIcon; @@ -573,6 +586,13 @@ return reloadStopButton_.get(); } +- (NSSegmentedControl*)backForwardControl { + if (!backForwardControl_) + [self updateBackForwardControl]; + + return backForwardControl_.get(); +} + - (BookmarkTabHelperObserver*)bookmarkTabObserver { return notificationBridge_.get(); }
diff --git a/chrome/browser/ui/cocoa/touchbar/browser_window_default_touch_bar_unittest.mm b/chrome/browser/ui/cocoa/touchbar/browser_window_default_touch_bar_unittest.mm index 7f679ff..ec36da0d 100644 --- a/chrome/browser/ui/cocoa/touchbar/browser_window_default_touch_bar_unittest.mm +++ b/chrome/browser/ui/cocoa/touchbar/browser_window_default_touch_bar_unittest.mm
@@ -18,14 +18,33 @@ #include "chrome/common/pref_names.h" #include "components/prefs/pref_service.h" #include "components/strings/grit/components_strings.h" -#include "content/public/test/test_renderer_host.h" -#include "content/public/test/web_contents_tester.h" #include "testing/gtest/include/gtest/gtest.h" #include "testing/gtest_mac.h" #import "third_party/ocmock/OCMock/OCMock.h" #import "ui/base/cocoa/touch_bar_util.h" #include "ui/base/l10n/l10n_util_mac.h" +namespace { + +// Touch bar identifiers. +NSString* const kBrowserWindowTouchBarId = @"browser-window"; +NSString* const kTabFullscreenTouchBarId = @"tab-fullscreen"; + +// Touch bar items identifiers. +NSString* const kBackForwardTouchId = @"BACK-FWD"; +NSString* const kReloadOrStopTouchId = @"RELOAD-STOP"; +NSString* const kHomeTouchId = @"HOME"; +NSString* const kSearchTouchId = @"SEARCH"; +NSString* const kStarTouchId = @"BOOKMARK"; +NSString* const kNewTabTouchId = @"NEW-TAB"; +NSString* const kFullscreenOriginLabelTouchId = @"FULLSCREEN-ORIGIN-LABEL"; + +// The button indexes in the back and forward segment control. +const int kBackSegmentIndex = 0; +const int kForwardSegmentIndex = 1; + +} // namespace + class BrowserWindowDefaultTouchBarUnitTest : public CocoaProfileTest { public: void SetUp() override { @@ -34,10 +53,6 @@ command_updater_ = browser()->command_controller(); - browser()->tab_strip_model()->AppendWebContents( - content::WebContentsTester::CreateTestWebContents(profile(), nullptr), - true); - if (@available(macOS 10.12.2, *)) { touch_bar_.reset([[BrowserWindowDefaultTouchBar alloc] initWithBrowser:browser() @@ -45,107 +60,68 @@ } } + NSString* GetFullscreenTouchBarItemId(NSString* id) { + return ui::GetTouchBarItemId(kTabFullscreenTouchBarId, id); + } + + NSString* GetBrowserTouchBarItemId(NSString* id) { + return ui::GetTouchBarItemId(kBrowserWindowTouchBarId, id); + } + void UpdateCommandEnabled(int id, bool enabled) { command_updater_->UpdateCommandEnabled(id, enabled); } void TearDown() override { - if (@available(macOS 10.12.2, *)) { - [touch_bar_ updateWebContents:nullptr]; + if (@available(macOS 10.12.2, *)) touch_bar_.reset(); - } CocoaProfileTest::TearDown(); } CommandUpdater* command_updater_; // Weak, owned by Browser. - content::RenderViewHostTestEnabler rvh_test_enabler_; API_AVAILABLE(macos(10.12.2)) base::scoped_nsobject<BrowserWindowDefaultTouchBar> touch_bar_; }; -// Test if any known identifiers no longer work. See the message in the test; -// these identifiers may be written out to disk on users' computers if they -// customize the Touch Bar, and the corresponding items will disappear if they -// can no longer be created. -TEST_F(BrowserWindowDefaultTouchBarUnitTest, HistoricTouchBarItems) { - if (@available(macOS 10.12.2, *)) { - NSTouchBar* touch_bar = [touch_bar_ makeTouchBar]; - for (NSString* item_identifier : { - @"BACK-FWD", - @"BACK", - @"FORWARD", - @"RELOAD-STOP", - @"HOME", - @"SEARCH", - @"BOOKMARK", - @"NEW-TAB", - }) { - auto identifier = - ui::GetTouchBarItemId(@"browser-window", item_identifier); - EXPECT_NE(nil, [touch_bar itemForIdentifier:identifier]) - << "BrowserWindowDefaultTouchBar didn't return a Touch Bar item for " - "an identifier that was once available (" - << identifier.UTF8String - << "). If a user's customized Touch Bar includes this item, it will " - "disappear! Do not update or remove entries in this list just to " - "make the test pass; keep supporting old identifiers when " - "possible, even if they're no longer part of the set of " - "default/customizable items."; - } - } -} - -// Tests if BrowserWindowDefaultTouchBar can produce the items it says it can -// and, for each kind of bar, also verify that the advertised/customizable lists -// include some representative items (if not, the lists might be wrong.) +// Tests to check if the touch bar contains the correct items. TEST_F(BrowserWindowDefaultTouchBarUnitTest, TouchBarItems) { if (@available(macOS 10.12.2, *)) { - auto test_default_identifiers = - [&](NSSet* expected_identifiers) API_AVAILABLE(macos(10.12.2)) { - NSTouchBar* touch_bar = [touch_bar_ makeTouchBar]; - NSMutableSet<NSString*>* advertised_identifiers = [NSMutableSet set]; - [advertised_identifiers - addObjectsFromArray:touch_bar.defaultItemIdentifiers]; - [advertised_identifiers - addObjectsFromArray:touch_bar - .customizationAllowedItemIdentifiers]; - [advertised_identifiers - addObjectsFromArray:touch_bar - .customizationRequiredItemIdentifiers]; - EXPECT_TRUE( - [expected_identifiers isSubsetOfSet:advertised_identifiers]) - << "Didn't find the expected identifiers " - << expected_identifiers.description.UTF8String - << " in the set of advertised identifiers " - << advertised_identifiers.description.UTF8String << "."; - for (NSString* identifier in advertised_identifiers) { - EXPECT_NE(nil, [touch_bar itemForIdentifier:identifier]) - << "Didn't get a touch bar item for " << identifier.UTF8String; - } - }; - // Set to tab fullscreen. FullscreenController* fullscreen_controller = browser()->exclusive_access_manager()->fullscreen_controller(); fullscreen_controller->set_is_tab_fullscreen_for_testing(true); EXPECT_TRUE(fullscreen_controller->IsTabFullscreen()); - // The fullscreen Touch Bar should include *at least* these items. - test_default_identifiers([NSSet setWithArray:@[ - BrowserWindowDefaultTouchBar.fullscreenOriginItemIdentifier, - ]]); + // The touch bar should only contain an item that displays the origin of the + // tab content fullscreen. + NSTouchBar* touch_bar = [touch_bar_ makeTouchBar]; + NSArray* touch_bar_items = [touch_bar itemIdentifiers]; + EXPECT_TRUE( + [touch_bar_items containsObject:GetFullscreenTouchBarItemId( + kFullscreenOriginLabelTouchId)]); + EXPECT_EQ(1u, [touch_bar_items count]); // Exit fullscreen. fullscreen_controller->set_is_tab_fullscreen_for_testing(false); EXPECT_FALSE(fullscreen_controller->IsTabFullscreen()); - // The default Touch Bar should include *at least* these items. - test_default_identifiers([NSSet setWithArray:@[ - BrowserWindowDefaultTouchBar.backItemIdentifier, - BrowserWindowDefaultTouchBar.forwardItemIdentifier, - BrowserWindowDefaultTouchBar.reloadOrStopItemIdentifier, - ]]); + PrefService* prefs = profile()->GetPrefs(); + DCHECK(prefs); + prefs->SetBoolean(prefs::kShowHomeButton, true); + touch_bar_items = [[touch_bar_ makeTouchBar] itemIdentifiers]; + EXPECT_TRUE([touch_bar_items + containsObject:GetBrowserTouchBarItemId(kBackForwardTouchId)]); + EXPECT_TRUE([touch_bar_items + containsObject:GetBrowserTouchBarItemId(kReloadOrStopTouchId)]); + EXPECT_TRUE([touch_bar_items + containsObject:GetBrowserTouchBarItemId(kHomeTouchId)]); + EXPECT_TRUE([touch_bar_items + containsObject:GetBrowserTouchBarItemId(kSearchTouchId)]); + EXPECT_TRUE([touch_bar_items + containsObject:GetBrowserTouchBarItemId(kStarTouchId)]); + EXPECT_TRUE([touch_bar_items + containsObject:GetBrowserTouchBarItemId(kNewTabTouchId)]); } } @@ -155,69 +131,56 @@ NSTouchBar* touch_bar = [touch_bar_ makeTouchBar]; [touch_bar_ setIsPageLoading:NO]; - NSTouchBarItem* item = - [touch_bar itemForIdentifier:BrowserWindowDefaultTouchBar - .reloadOrStopItemIdentifier]; + NSTouchBarItem* item = [touch_bar_ + touchBar:touch_bar + makeItemForIdentifier:GetBrowserTouchBarItemId(kReloadOrStopTouchId)]; EXPECT_EQ(IDC_RELOAD, [[item view] tag]); [touch_bar_ setIsPageLoading:YES]; - item = [touch_bar itemForIdentifier:BrowserWindowDefaultTouchBar - .reloadOrStopItemIdentifier]; + item = [touch_bar_ touchBar:touch_bar + makeItemForIdentifier:GetBrowserTouchBarItemId(kReloadOrStopTouchId)]; EXPECT_EQ(IDC_STOP, [[item view] tag]); } } -// Tests if the back button on the touch bar is in sync with the back command. -TEST_F(BrowserWindowDefaultTouchBarUnitTest, BackCommandUpdate) { +// Tests to see if the back/forward items on the touch bar is in sync with the +// back and forward commands. +TEST_F(BrowserWindowDefaultTouchBarUnitTest, BackForwardCommandUpdate) { if (@available(macOS 10.12.2, *)) { - NSTouchBar* touch_bar = [touch_bar_ makeTouchBar]; - NSTouchBarItem* item = [touch_bar - itemForIdentifier:BrowserWindowDefaultTouchBar.backItemIdentifier]; - NSButton* button = base::mac::ObjCCast<NSButton>(item.view); + NSSegmentedControl* back_forward_control = [touch_bar_ backForwardControl]; UpdateCommandEnabled(IDC_BACK, true); - EXPECT_TRUE(button.enabled); - UpdateCommandEnabled(IDC_BACK, false); - EXPECT_FALSE(button.enabled); - } -} - -// Tests if the forward button on the touch bar is in sync with the forward -// command. -TEST_F(BrowserWindowDefaultTouchBarUnitTest, ForwardCommandUpdate) { - if (@available(macOS 10.12.2, *)) { - NSTouchBar* touch_bar = [touch_bar_ makeTouchBar]; - NSTouchBarItem* item = [touch_bar - itemForIdentifier:BrowserWindowDefaultTouchBar.forwardItemIdentifier]; - NSButton* button = base::mac::ObjCCast<NSButton>(item.view); - UpdateCommandEnabled(IDC_FORWARD, true); - EXPECT_TRUE(button.enabled); + EXPECT_TRUE([back_forward_control isEnabledForSegment:kBackSegmentIndex]); + EXPECT_TRUE( + [back_forward_control isEnabledForSegment:kForwardSegmentIndex]); + + UpdateCommandEnabled(IDC_BACK, false); + EXPECT_FALSE([back_forward_control isEnabledForSegment:kBackSegmentIndex]); + EXPECT_TRUE( + [back_forward_control isEnabledForSegment:kForwardSegmentIndex]); + UpdateCommandEnabled(IDC_FORWARD, false); - EXPECT_FALSE(button.enabled); + EXPECT_FALSE([back_forward_control isEnabledForSegment:kBackSegmentIndex]); + EXPECT_FALSE( + [back_forward_control isEnabledForSegment:kForwardSegmentIndex]); } } -TEST_F(BrowserWindowDefaultTouchBarUnitTest, BackAccessibilityLabel) { +TEST_F(BrowserWindowDefaultTouchBarUnitTest, BackForwardAccessibilityLabels) { if (@available(macOS 10.12.2, *)) { - NSTouchBar* touch_bar = [touch_bar_ makeTouchBar]; - NSTouchBarItem* item = [touch_bar - itemForIdentifier:BrowserWindowDefaultTouchBar.backItemIdentifier]; - id<NSAccessibility> view = item.view; - ASSERT_TRUE([view conformsToProtocol:@protocol(NSAccessibility)]); - EXPECT_NSEQ(view.accessibilityTitle, + NSSegmentedControl* control = touch_bar_.get().backForwardControl; + id<NSAccessibility> cell = NSAccessibilityUnignoredDescendant(control); + ASSERT_TRUE([cell conformsToProtocol:@protocol(NSAccessibility)]); + + id<NSAccessibility> back = cell.accessibilityChildren[0]; + EXPECT_TRUE([back conformsToProtocol:@protocol(NSAccessibility)]); + EXPECT_NSEQ(back.accessibilityTitle, l10n_util::GetNSString(IDS_ACCNAME_BACK)); - } -} -TEST_F(BrowserWindowDefaultTouchBarUnitTest, ForwardAccessibilityLabel) { - if (@available(macOS 10.12.2, *)) { - NSTouchBar* touch_bar = [touch_bar_ makeTouchBar]; - NSTouchBarItem* item = [touch_bar - itemForIdentifier:BrowserWindowDefaultTouchBar.forwardItemIdentifier]; - id<NSAccessibility> view = item.view; - ASSERT_TRUE([view conformsToProtocol:@protocol(NSAccessibility)]); - EXPECT_NSEQ(view.accessibilityTitle, + id<NSAccessibility> forward = cell.accessibilityChildren[1]; + EXPECT_TRUE([forward conformsToProtocol:@protocol(NSAccessibility)]); + EXPECT_NSEQ(forward.accessibilityTitle, l10n_util::GetNSString(IDS_ACCNAME_FORWARD)); } }
diff --git a/chrome/browser/ui/views/chrome_typography_provider.cc b/chrome/browser/ui/views/chrome_typography_provider.cc index c2655e4..ebb8c1049 100644 --- a/chrome/browser/ui/views/chrome_typography_provider.cc +++ b/chrome/browser/ui/views/chrome_typography_provider.cc
@@ -15,7 +15,6 @@ #if defined(OS_WIN) #include "base/win/windows_version.h" -#include "ui/gfx/platform_font_win.h" #include "ui/native_theme/native_theme_win.h" #endif @@ -111,20 +110,18 @@ #if defined(OS_WIN) // static int ChromeTypographyProvider::GetPlatformFontHeight(int font_context) { - const bool direct_write_enabled = - gfx::PlatformFontWin::IsDirectWriteEnabled(); const bool windows_10 = base::win::GetVersion() >= base::win::VERSION_WIN10; switch (font_context) { case CONTEXT_HEADLINE: - return windows_10 && direct_write_enabled ? 27 : 28; + return windows_10 ? 27 : 28; case views::style::CONTEXT_DIALOG_TITLE: - return windows_10 || !direct_write_enabled ? 20 : 21; + return windows_10 ? 20 : 21; case CONTEXT_BODY_TEXT_LARGE: case CONTEXT_TAB_HOVER_CARD_TITLE: case views::style::CONTEXT_MESSAGE_BOX_BODY_TEXT: - return direct_write_enabled ? 18 : 17; + return 18; case CONTEXT_BODY_TEXT_SMALL: - return windows_10 && direct_write_enabled ? 16 : 15; + return windows_10 ? 16 : 15; } NOTREACHED(); return 0;
diff --git a/chrome/browser/ui/views/passwords/password_bubble_interactive_uitest.cc b/chrome/browser/ui/views/passwords/password_bubble_interactive_uitest.cc index 1b6ddcd..23ec94e 100644 --- a/chrome/browser/ui/views/passwords/password_bubble_interactive_uitest.cc +++ b/chrome/browser/ui/views/passwords/password_bubble_interactive_uitest.cc
@@ -108,7 +108,7 @@ PasswordBubbleViewBase::manage_password_bubble()); // A pending password with empty username should initially focus on the // username field. - EXPECT_EQ(bubble->username_field(), + EXPECT_EQ(bubble->GetUsernameTextfieldForTest(), bubble->GetFocusManager()->GetFocusedView()); PasswordBubbleViewBase::CloseCurrentBubble(); EXPECT_FALSE(IsBubbleShowing());
diff --git a/chrome/browser/ui/views/passwords/password_items_view.cc b/chrome/browser/ui/views/passwords/password_items_view.cc index 6394d418..c44cf6e 100644 --- a/chrome/browser/ui/views/passwords/password_items_view.cc +++ b/chrome/browser/ui/views/passwords/password_items_view.cc
@@ -16,6 +16,7 @@ #include "chrome/grit/generated_resources.h" #include "components/password_manager/core/common/password_manager_ui.h" #include "ui/base/l10n/l10n_util.h" +#include "ui/base/models/simple_combobox_model.h" #include "ui/base/resource/resource_bundle.h" #include "ui/gfx/range/range.h" #include "ui/resources/grit/ui_resources.h" @@ -23,10 +24,10 @@ #include "ui/views/controls/button/image_button.h" #include "ui/views/controls/button/image_button_factory.h" #include "ui/views/controls/button/md_text_button.h" +#include "ui/views/controls/editable_combobox/editable_combobox.h" #include "ui/views/controls/label.h" #include "ui/views/controls/link.h" #include "ui/views/controls/link_listener.h" -#include "ui/views/controls/textfield/textfield.h" #include "ui/views/layout/fill_layout.h" #include "ui/views/layout/grid_layout.h" @@ -110,15 +111,23 @@ return label; } -std::unique_ptr<views::Textfield> CreateUsernameEditable( - const base::string16& initial_username) { - auto editable = std::make_unique<views::Textfield>(); - editable->SetText(initial_username); - editable->SetAccessibleName( +std::unique_ptr<views::EditableCombobox> CreateUsernameEditableCombobox( + const autofill::PasswordForm& form) { + std::vector<base::string16> usernames = {form.username_value}; + for (const autofill::ValueElementPair& other_possible_username_pair : + form.other_possible_usernames) { + if (other_possible_username_pair.first != form.username_value) + usernames.push_back(other_possible_username_pair.first); + } + auto combobox = std::make_unique<views::EditableCombobox>( + std::make_unique<ui::SimpleComboboxModel>(usernames), + /*filter_on_edit=*/false, /*show_on_empty=*/true); + combobox->SetText(form.username_value); + combobox->SetAccessibleName( l10n_util::GetStringUTF16(IDS_PASSWORD_MANAGER_USERNAME_LABEL)); // In case of long username, ensure that the beginning of value is visible. - editable->SelectRange(gfx::Range(0)); - return editable; + combobox->SelectRange(gfx::Range(0)); + return combobox; } std::unique_ptr<views::Label> CreatePasswordLabel(
diff --git a/chrome/browser/ui/views/passwords/password_items_view.h b/chrome/browser/ui/views/passwords/password_items_view.h index e0c43909..548287fc 100644 --- a/chrome/browser/ui/views/passwords/password_items_view.h +++ b/chrome/browser/ui/views/passwords/password_items_view.h
@@ -16,7 +16,7 @@ #include "ui/views/view.h" namespace views { -class Textfield; +class EditableCombobox; class Label; } // namespace views @@ -27,8 +27,8 @@ const autofill::PasswordForm& form, int federation_message_id, bool is_password_visible); -std::unique_ptr<views::Textfield> CreateUsernameEditable( - const base::string16& initial_username); +std::unique_ptr<views::EditableCombobox> CreateUsernameEditableCombobox( + const autofill::PasswordForm& form); // A dialog for managing stored password and federated login information for a // specific site. A user can remove managed credentials for the site via this
diff --git a/chrome/browser/ui/views/passwords/password_pending_view.cc b/chrome/browser/ui/views/passwords/password_pending_view.cc index 01bf5956..74ea2bfb 100644 --- a/chrome/browser/ui/views/passwords/password_pending_view.cc +++ b/chrome/browser/ui/views/passwords/password_pending_view.cc
@@ -35,6 +35,7 @@ #include "ui/views/layout/fill_layout.h" #include "ui/views/layout/grid_layout.h" #include "ui/views/layout/layout_provider.h" +#include "ui/views/view.h" #include "ui/views/window/dialog_client_view.h" namespace { @@ -170,8 +171,9 @@ return button; } -// Creates a dropdown from |PasswordForm.all_possible_passwords|. -std::unique_ptr<views::EditableCombobox> CreatePasswordDropdownView( +// Creates an EditableCombobox from |PasswordForm.all_possible_passwords| or +// even just |PasswordForm.password_value|. +std::unique_ptr<views::EditableCombobox> CreatePasswordEditableCombobox( const autofill::PasswordForm& form, bool are_passwords_revealed) { DCHECK(form.federation_origin.opaque()); @@ -200,7 +202,7 @@ is_update_bubble_(model()->state() == password_manager::ui::PENDING_PASSWORD_UPDATE_STATE), sign_in_promo_(nullptr), - username_field_(nullptr), + username_dropdown_(nullptr), password_view_button_(nullptr), initially_focused_view_(nullptr), password_dropdown_(nullptr), @@ -225,17 +227,12 @@ credential_view->SetEnabled(false); AddChildView(credential_view); } else { - if (model()->enable_editing()) { - views::Textfield* username_field = - CreateUsernameEditable(model()->GetCurrentUsername()).release(); - username_field->set_controller(this); - username_field_ = username_field; - } else { - username_field_ = CreateUsernameLabel(password_form).release(); - } - + username_dropdown_ = + CreateUsernameEditableCombobox(password_form).release(); + username_dropdown_->set_listener(this); + username_dropdown_->set_show_menu_on_next_focus(false); password_dropdown_ = - CreatePasswordDropdownView(password_form, are_passwords_revealed_) + CreatePasswordEditableCombobox(password_form, are_passwords_revealed_) .release(); password_view_button_ = @@ -244,15 +241,17 @@ views::GridLayout* layout = SetLayoutManager(std::make_unique<views::GridLayout>(this)); - BuildCredentialRows(layout, username_field_, password_dropdown_, + BuildCredentialRows(layout, username_dropdown_, password_dropdown_, password_view_button_); - if (model()->enable_editing() && - model()->pending_password().username_value.empty()) { - initially_focused_view_ = username_field_; - } + if (model()->pending_password().username_value.empty()) + initially_focused_view_ = username_dropdown_; } } +views::View* PasswordPendingView::GetUsernameTextfieldForTest() const { + return username_dropdown_->GetTextfieldForTest(); +} + PasswordPendingView::~PasswordPendingView() = default; bool PasswordPendingView::Accept() { @@ -289,8 +288,8 @@ TogglePasswordVisibility(); } -void PasswordPendingView::ContentsChanged(views::Textfield* sender, - const base::string16& new_contents) { +void PasswordPendingView::OnContentChanged( + views::EditableCombobox* editable_combobox) { bool is_update_before = model()->IsCurrentStateUpdate(); UpdateUsernameAndPasswordInModel(); // May be the buttons should be updated. @@ -379,18 +378,12 @@ } void PasswordPendingView::UpdateUsernameAndPasswordInModel() { - const bool username_editable = model()->enable_editing(); - if (!username_editable && !password_dropdown_) - return; - + DCHECK(username_dropdown_ && password_dropdown_); base::string16 new_username = model()->pending_password().username_value; base::string16 new_password = model()->pending_password().password_value; - if (username_editable) { - new_username = static_cast<views::Textfield*>(username_field_)->text(); - base::TrimString(new_username, base::ASCIIToUTF16(" "), &new_username); - } - if (password_dropdown_) - new_password = password_dropdown_->GetText(); + new_username = username_dropdown_->GetText(); + base::TrimString(new_username, base::ASCIIToUTF16(" "), &new_username); + new_password = password_dropdown_->GetText(); model()->OnCredentialEdited(std::move(new_username), std::move(new_password)); }
diff --git a/chrome/browser/ui/views/passwords/password_pending_view.h b/chrome/browser/ui/views/passwords/password_pending_view.h index 6b58ea0..194c58ac 100644 --- a/chrome/browser/ui/views/passwords/password_pending_view.h +++ b/chrome/browser/ui/views/passwords/password_pending_view.h
@@ -7,7 +7,7 @@ #include "chrome/browser/ui/views/passwords/password_bubble_view_base.h" #include "ui/views/controls/button/button.h" -#include "ui/views/controls/textfield/textfield_controller.h" +#include "ui/views/controls/editable_combobox/editable_combobox_listener.h" #include "ui/views/view.h" namespace views { @@ -22,16 +22,14 @@ // "Save"/"Update" button and a "Never"/"Nope" button. class PasswordPendingView : public PasswordBubbleViewBase, public views::ButtonListener, - public views::TextfieldController { + public views::EditableComboboxListener { public: PasswordPendingView(content::WebContents* web_contents, views::View* anchor_view, const gfx::Point& anchor_point, DisplayReason reason); -#if defined(UNIT_TEST) - const View* username_field() const { return username_field_; } -#endif + views::View* GetUsernameTextfieldForTest() const; private: ~PasswordPendingView() override; @@ -39,9 +37,8 @@ // views::ButtonListener: void ButtonPressed(views::Button* sender, const ui::Event& event) override; - // views::TextfieldController: - void ContentsChanged(views::Textfield* sender, - const base::string16& new_contents) override; + // views::EditableComboboxListener: + void OnContentChanged(views::EditableCombobox* editable_combobox) override; // PasswordBubbleViewBase: views::View* CreateFootnoteView() override; @@ -72,7 +69,7 @@ // active. PasswordSignInPromoView* sign_in_promo_; - views::View* username_field_; + views::EditableCombobox* username_dropdown_; views::ToggleImageButton* password_view_button_; views::View* initially_focused_view_;
diff --git a/chrome/browser/ui/views/tabs/tab_hover_card_bubble_view.cc b/chrome/browser/ui/views/tabs/tab_hover_card_bubble_view.cc index 6718746..8a65dc4 100644 --- a/chrome/browser/ui/views/tabs/tab_hover_card_bubble_view.cc +++ b/chrome/browser/ui/views/tabs/tab_hover_card_bubble_view.cc
@@ -9,6 +9,7 @@ #include "base/metrics/field_trial_params.h" #include "base/strings/utf_string_conversions.h" +#include "build/build_config.h" #include "chrome/app/vector_icons/vector_icons.h" #include "chrome/browser/themes/theme_properties.h" #include "chrome/browser/ui/tabs/tab_style.h" @@ -36,6 +37,10 @@ #include "ui/views/view_class_properties.h" #include "ui/views/widget/widget.h" +#if defined(OS_WIN) +#include "ui/base/win/shell.h" +#endif + namespace { // Hover card and preview image dimensions. @@ -86,6 +91,14 @@ } } +bool CustomShadowsSupported() { +#if defined(OS_WIN) + return ui::win::IsAeroGlassEnabled(); +#else + return true; +#endif +} + } // namespace // static @@ -239,8 +252,11 @@ GetBubbleFrameView()->set_preferred_arrow_adjustment( views::BubbleFrameView::PreferredArrowAdjustment::kOffset); - GetBubbleFrameView()->bubble_border()->SetCornerRadius( - ChromeLayoutProvider::Get()->GetCornerRadiusMetric(views::EMPHASIS_HIGH)); + + if (CustomShadowsSupported()) + GetBubbleFrameView()->bubble_border()->SetCornerRadius( + ChromeLayoutProvider::Get()->GetCornerRadiusMetric( + views::EMPHASIS_HIGH)); } TabHoverCardBubbleView::~TabHoverCardBubbleView() = default;
diff --git a/chrome/browser/ui/webui/chrome_web_ui_controller_factory.cc b/chrome/browser/ui/webui/chrome_web_ui_controller_factory.cc index 228d3150..f936e05 100644 --- a/chrome/browser/ui/webui/chrome_web_ui_controller_factory.cc +++ b/chrome/browser/ui/webui/chrome_web_ui_controller_factory.cc
@@ -53,7 +53,7 @@ #include "chrome/browser/ui/webui/policy_ui.h" #include "chrome/browser/ui/webui/predictors/predictors_ui.h" #include "chrome/browser/ui/webui/quota_internals/quota_internals_ui.h" -#include "chrome/browser/ui/webui/settings/md_settings_ui.h" +#include "chrome/browser/ui/webui/settings/settings_ui.h" #include "chrome/browser/ui/webui/settings_utils.h" #include "chrome/browser/ui/webui/signin_internals_ui.h" #include "chrome/browser/ui/webui/supervised_user_internals_ui.h" @@ -470,7 +470,7 @@ return &NewWebUI<NewTabUI>; // Settings are implemented with native UI elements on Android. if (url.host_piece() == chrome::kChromeUISettingsHost) - return &NewWebUI<settings::MdSettingsUI>; + return &NewWebUI<settings::SettingsUI>; if (url.host_piece() == chrome::kChromeUIExtensionsHost) return &NewWebUI<extensions::ExtensionsUI>; if (url.host_piece() == chrome::kChromeUIHistoryHost)
diff --git a/chrome/browser/ui/webui/chromeos/login/signin_screen_handler.cc b/chrome/browser/ui/webui/chromeos/login/signin_screen_handler.cc index 9918d89..d0fec1d 100644 --- a/chrome/browser/ui/webui/chromeos/login/signin_screen_handler.cc +++ b/chrome/browser/ui/webui/chromeos/login/signin_screen_handler.cc
@@ -428,15 +428,6 @@ IDS_ENTERPRISE_ENROLLMENT_AUTH_FATAL_ERROR); builder->Add("insecureURLEnrollmentError", IDS_ENTERPRISE_ENROLLMENT_AUTH_INSECURE_URL_ERROR); - - builder->Add("unrecoverableCryptohomeErrorMessageTitle", - IDS_LOGIN_UNRECOVERABLE_CRYPTOHOME_ERROR_TITLE); - builder->Add("unrecoverableCryptohomeErrorMessage", - IDS_LOGIN_UNRECOVERABLE_CRYPTOHOME_ERROR_MESSAGE); - builder->Add("unrecoverableCryptohomeErrorContinue", - IDS_LOGIN_UNRECOVERABLE_CRYPTOHOME_ERROR_CONTINUE); - builder->Add("unrecoverableCryptohomeErrorRecreatingProfile", - IDS_LOGIN_UNRECOVERABLE_CRYPTOHOME_ERROR_WAIT_MESSAGE); } void SigninScreenHandler::RegisterMessages() { @@ -483,8 +474,6 @@ AddCallback("maxIncorrectPasswordAttempts", &SigninScreenHandler::HandleMaxIncorrectPasswordAttempts); AddCallback("sendFeedback", &SigninScreenHandler::HandleSendFeedback); - AddCallback("sendFeedbackAndResyncUserData", - &SigninScreenHandler::HandleSendFeedbackAndResyncUserData); } void SigninScreenHandler::Show(const LoginScreenContext& context, @@ -978,10 +967,6 @@ gaia_screen_handler_->ShowWhitelistCheckFailedError(); } -void SigninScreenHandler::ShowUnrecoverableCrypthomeErrorDialog() { - CallJS("login.UnrecoverableCryptohomeErrorScreen.show"); -} - void SigninScreenHandler::Observe(int type, const content::NotificationSource& source, const content::NotificationDetails& details) { @@ -1416,21 +1401,6 @@ weak_factory_.GetWeakPtr())); } -void SigninScreenHandler::HandleSendFeedbackAndResyncUserData() { - const std::string description = base::StringPrintf( - "Auto generated feedback for http://crbug.com/547857.\n" - "(uniquifier:%s)", - base::NumberToString(base::Time::Now().ToInternalValue()).c_str()); - - login_feedback_ = - std::make_unique<LoginFeedback>(Profile::FromWebUI(web_ui())); - login_feedback_->Request( - description, - base::BindOnce( - &SigninScreenHandler::OnUnrecoverableCryptohomeFeedbackFinished, - weak_factory_.GetWeakPtr())); -} - bool SigninScreenHandler::AllWhitelistedUsersPresent() { CrosSettings* cros_settings = CrosSettings::Get(); bool allow_new_user = false; @@ -1494,15 +1464,6 @@ login_feedback_.reset(); } -void SigninScreenHandler::OnUnrecoverableCryptohomeFeedbackFinished() { - CallJS("login.UnrecoverableCryptohomeErrorScreen.resumeAfterFeedbackUI"); - - // Recreate user's cryptohome after the feedback is attempted. - HandleResyncUserData(); - - login_feedback_.reset(); -} - void SigninScreenHandler::OnAllowedInputMethodsChanged() { if (!webui_visible_) return;
diff --git a/chrome/browser/ui/webui/chromeos/login/signin_screen_handler.h b/chrome/browser/ui/webui/chromeos/login/signin_screen_handler.h index ea5ef06..9256d90 100644 --- a/chrome/browser/ui/webui/chromeos/login/signin_screen_handler.h +++ b/chrome/browser/ui/webui/chromeos/login/signin_screen_handler.h
@@ -92,7 +92,6 @@ virtual void ShowPasswordChangedDialog(bool show_password_error, const std::string& email) = 0; virtual void ShowWhitelistCheckFailedError() = 0; - virtual void ShowUnrecoverableCrypthomeErrorDialog() = 0; virtual void LoadUsers(const user_manager::UserList& users, const base::ListValue& users_list) = 0; @@ -295,7 +294,6 @@ const std::string& email) override; void ShowErrorScreen(LoginDisplay::SigninError error_id) override; void ShowWhitelistCheckFailedError() override; - void ShowUnrecoverableCrypthomeErrorDialog() override; void LoadUsers(const user_manager::UserList& users, const base::ListValue& users_list) override; @@ -371,7 +369,6 @@ void HandleFirstIncorrectPasswordAttempt(const AccountId& account_id); void HandleMaxIncorrectPasswordAttempts(const AccountId& account_id); void HandleSendFeedback(); - void HandleSendFeedbackAndResyncUserData(); // Implements user sign-in. void AuthenticateExistingUser(const AccountId& account_id,
diff --git a/chrome/browser/ui/webui/management_ui_handler.cc b/chrome/browser/ui/webui/management_ui_handler.cc index bff56ce..f87c2b67 100644 --- a/chrome/browser/ui/webui/management_ui_handler.cc +++ b/chrome/browser/ui/webui/management_ui_handler.cc
@@ -34,9 +34,9 @@ #if defined(OS_CHROMEOS) #include "chrome/browser/chromeos/policy/browser_policy_connector_chromeos.h" #include "chrome/browser/chromeos/policy/device_cloud_policy_manager_chromeos.h" -#include "chrome/browser/chromeos/policy/device_status_collector.h" #include "chrome/browser/chromeos/policy/policy_cert_service.h" #include "chrome/browser/chromeos/policy/policy_cert_service_factory.h" +#include "chrome/browser/chromeos/policy/status_collector/device_status_collector.h" #include "chrome/browser/chromeos/policy/status_uploader.h" #include "chrome/browser/chromeos/policy/system_log_uploader.h" #include "chrome/browser/chromeos/profiles/profile_helper.h" @@ -210,24 +210,24 @@ if (!manager) return; - const policy::DeviceStatusCollector* collector = - manager->GetStatusUploader()->device_status_collector(); + const policy::StatusCollector* collector = + manager->GetStatusUploader()->status_collector(); // Elements appear on the page in the order they are added. - if (collector->report_activity_times()) { + if (collector->ShouldReportActivityTimes()) { AddDeviceReportingElement(report_sources, kManagementReportActivityTimes, DeviceReportingType::kDeviceActivity); } else { - if (collector->report_users()) { + if (collector->ShouldReportUsers()) { AddDeviceReportingElement(report_sources, kManagementReportUsers, DeviceReportingType::kSupervisedUser); } } - if (collector->report_hardware_status()) { + if (collector->ShouldReportHardwareStatus()) { AddDeviceReportingElement(report_sources, kManagementReportHardwareStatus, DeviceReportingType::kDeviceStatistics); } - if (collector->report_network_interfaces()) { + if (collector->ShouldReportNetworkInterfaces()) { AddDeviceReportingElement(report_sources, kManagementReportNetworkInterfaces, DeviceReportingType::kDevice);
diff --git a/chrome/browser/ui/webui/print_preview/print_preview_ui.cc b/chrome/browser/ui/webui/print_preview/print_preview_ui.cc index 6523fdd3..3f7e7a60 100644 --- a/chrome/browser/ui/webui/print_preview/print_preview_ui.cc +++ b/chrome/browser/ui/webui/print_preview/print_preview_ui.cc
@@ -334,6 +334,11 @@ base::FeatureList::IsEnabled(features::kNewPrintPreviewLayout); source->AddBoolean("newPrintPreviewLayoutEnabled", new_print_preview_layout_enabled); + // The key for the string below needs to be all lowercase, since it is used + // as an attribute and attributes are lowercased when the page is bundled. + source->AddString("newprintpreviewlayout", new_print_preview_layout_enabled + ? "new-print-preview-layout" + : ""); } std::vector<std::string> SetupPrintPreviewPlugin(
diff --git a/chrome/browser/ui/webui/settings/chromeos/os_settings_ui.cc b/chrome/browser/ui/webui/settings/chromeos/os_settings_ui.cc index 93289559..f5ae795 100644 --- a/chrome/browser/ui/webui/settings/chromeos/os_settings_ui.cc +++ b/chrome/browser/ui/webui/settings/chromeos/os_settings_ui.cc
@@ -27,8 +27,6 @@ #include "chrome/browser/ui/webui/settings/extension_control_handler.h" #include "chrome/browser/ui/webui/settings/font_handler.h" #include "chrome/browser/ui/webui/settings/languages_handler.h" -#include "chrome/browser/ui/webui/settings/md_settings_localized_strings_provider.h" -#include "chrome/browser/ui/webui/settings/md_settings_ui.h" #include "chrome/browser/ui/webui/settings/on_startup_handler.h" #include "chrome/browser/ui/webui/settings/people_handler.h" #include "chrome/browser/ui/webui/settings/profile_info_handler.h" @@ -38,9 +36,11 @@ #include "chrome/browser/ui/webui/settings/settings_clear_browsing_data_handler.h" #include "chrome/browser/ui/webui/settings/settings_cookies_view_handler.h" #include "chrome/browser/ui/webui/settings/settings_import_data_handler.h" +#include "chrome/browser/ui/webui/settings/settings_localized_strings_provider.h" #include "chrome/browser/ui/webui/settings/settings_media_devices_selection_handler.h" #include "chrome/browser/ui/webui/settings/settings_security_key_handler.h" #include "chrome/browser/ui/webui/settings/settings_startup_pages_handler.h" +#include "chrome/browser/ui/webui/settings/settings_ui.h" #include "chrome/browser/ui/webui/settings/site_settings_handler.h" #include "chrome/browser/web_applications/system_web_app_manager.h" #include "chrome/common/pref_names.h" @@ -80,7 +80,7 @@ content::WebUIDataSource* html_source = content::WebUIDataSource::Create(chrome::kChromeUIOSSettingsHost); - ::settings::MdSettingsUI::InitOSWebUIHandlers(profile, web_ui, html_source); + ::settings::SettingsUI::InitOSWebUIHandlers(profile, web_ui, html_source); // TODO(jamescook): Remove after basic_page.html is forked for OS settings. html_source->AddBoolean("showOSSettings", true);
diff --git a/chrome/browser/ui/webui/settings/md_settings_localized_strings_provider.cc b/chrome/browser/ui/webui/settings/settings_localized_strings_provider.cc similarity index 99% rename from chrome/browser/ui/webui/settings/md_settings_localized_strings_provider.cc rename to chrome/browser/ui/webui/settings/settings_localized_strings_provider.cc index 830d0a854..44e8b940 100644 --- a/chrome/browser/ui/webui/settings/md_settings_localized_strings_provider.cc +++ b/chrome/browser/ui/webui/settings/settings_localized_strings_provider.cc
@@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "chrome/browser/ui/webui/settings/md_settings_localized_strings_provider.h" +#include "chrome/browser/ui/webui/settings/settings_localized_strings_provider.h" #include <string> @@ -2637,7 +2637,6 @@ html_source->AddBoolean( "enableExperimentalWebPlatformFeatures", cmd.HasSwitch(::switches::kEnableExperimentalWebPlatformFeatures)); - } #if defined(OS_CHROMEOS)
diff --git a/chrome/browser/ui/webui/settings/md_settings_localized_strings_provider.h b/chrome/browser/ui/webui/settings/settings_localized_strings_provider.h similarity index 73% rename from chrome/browser/ui/webui/settings/md_settings_localized_strings_provider.h rename to chrome/browser/ui/webui/settings/settings_localized_strings_provider.h index cfa3786..1642eb7 100644 --- a/chrome/browser/ui/webui/settings/md_settings_localized_strings_provider.h +++ b/chrome/browser/ui/webui/settings/settings_localized_strings_provider.h
@@ -2,8 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef CHROME_BROWSER_UI_WEBUI_SETTINGS_MD_SETTINGS_LOCALIZED_STRINGS_PROVIDER_H_ -#define CHROME_BROWSER_UI_WEBUI_SETTINGS_MD_SETTINGS_LOCALIZED_STRINGS_PROVIDER_H_ +#ifndef CHROME_BROWSER_UI_WEBUI_SETTINGS_SETTINGS_LOCALIZED_STRINGS_PROVIDER_H_ +#define CHROME_BROWSER_UI_WEBUI_SETTINGS_SETTINGS_LOCALIZED_STRINGS_PROVIDER_H_ class Profile; @@ -21,4 +21,4 @@ } // namespace settings -#endif // CHROME_BROWSER_UI_WEBUI_SETTINGS_MD_SETTINGS_LOCALIZED_STRINGS_PROVIDER_H_ +#endif // CHROME_BROWSER_UI_WEBUI_SETTINGS_SETTINGS_LOCALIZED_STRINGS_PROVIDER_H_
diff --git a/chrome/browser/ui/webui/settings/md_settings_ui.cc b/chrome/browser/ui/webui/settings/settings_ui.cc similarity index 94% rename from chrome/browser/ui/webui/settings/md_settings_ui.cc rename to chrome/browser/ui/webui/settings/settings_ui.cc index 9cddf44..3d8d904 100644 --- a/chrome/browser/ui/webui/settings/md_settings_ui.cc +++ b/chrome/browser/ui/webui/settings/settings_ui.cc
@@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "chrome/browser/ui/webui/settings/md_settings_ui.h" +#include "chrome/browser/ui/webui/settings/settings_ui.h" #include <stddef.h> @@ -31,7 +31,6 @@ #include "chrome/browser/ui/webui/settings/downloads_handler.h" #include "chrome/browser/ui/webui/settings/extension_control_handler.h" #include "chrome/browser/ui/webui/settings/font_handler.h" -#include "chrome/browser/ui/webui/settings/md_settings_localized_strings_provider.h" #include "chrome/browser/ui/webui/settings/metrics_reporting_handler.h" #include "chrome/browser/ui/webui/settings/on_startup_handler.h" #include "chrome/browser/ui/webui/settings/people_handler.h" @@ -42,6 +41,7 @@ #include "chrome/browser/ui/webui/settings/settings_clear_browsing_data_handler.h" #include "chrome/browser/ui/webui/settings/settings_cookies_view_handler.h" #include "chrome/browser/ui/webui/settings/settings_import_data_handler.h" +#include "chrome/browser/ui/webui/settings/settings_localized_strings_provider.h" #include "chrome/browser/ui/webui/settings/settings_media_devices_selection_handler.h" #include "chrome/browser/ui/webui/settings/settings_page_ui_handler.h" #include "chrome/browser/ui/webui/settings/settings_security_key_handler.h" @@ -145,7 +145,7 @@ namespace settings { // static -void MdSettingsUI::RegisterProfilePrefs( +void SettingsUI::RegisterProfilePrefs( user_prefs::PrefRegistrySyncable* registry) { registry->RegisterBooleanPref(prefs::kImportDialogAutofillFormData, true); registry->RegisterBooleanPref(prefs::kImportDialogBookmarks, true); @@ -154,7 +154,7 @@ registry->RegisterBooleanPref(prefs::kImportDialogSearchEngine, true); } -MdSettingsUI::MdSettingsUI(content::WebUI* web_ui) +SettingsUI::SettingsUI(content::WebUI* web_ui) : content::WebUIController(web_ui), WebContentsObserver(web_ui->GetWebContents()) { #if BUILDFLAG(OPTIMIZE_WEBUI) @@ -219,8 +219,7 @@ AddSettingsPageUIHandler( std::make_unique<chromeos::settings::AccountManagerUIHandler>( - account_manager, - IdentityManagerFactory::GetForProfile(profile))); + account_manager, IdentityManagerFactory::GetForProfile(profile))); html_source->AddBoolean( "secondaryGoogleAccountSigninAllowed", profile->GetPrefs()->GetBoolean( @@ -288,6 +287,8 @@ html_source->AddBoolean( "showOSSettings", !base::FeatureList::IsEnabled(chromeos::features::kSplitSettings)); +#else + html_source->AddBoolean("showOSSettings", false); #endif AddSettingsPageUIHandler( @@ -314,16 +315,16 @@ #if BUILDFLAG(OPTIMIZE_WEBUI) const bool use_polymer_2 = base::FeatureList::IsEnabled(features::kWebUIPolymer2); - html_source->AddResourcePath("crisper.js", IDR_MD_SETTINGS_CRISPER_JS); + html_source->AddResourcePath("crisper.js", IDR_SETTINGS_CRISPER_JS); html_source->AddResourcePath("lazy_load.crisper.js", - IDR_MD_SETTINGS_LAZY_LOAD_CRISPER_JS); - html_source->AddResourcePath( - "lazy_load.html", use_polymer_2 - ? IDR_MD_SETTINGS_LAZY_LOAD_VULCANIZED_P2_HTML - : IDR_MD_SETTINGS_LAZY_LOAD_VULCANIZED_HTML); + IDR_SETTINGS_LAZY_LOAD_CRISPER_JS); + html_source->AddResourcePath("lazy_load.html", + use_polymer_2 + ? IDR_SETTINGS_LAZY_LOAD_VULCANIZED_P2_HTML + : IDR_SETTINGS_LAZY_LOAD_VULCANIZED_HTML); html_source->SetDefaultResource(use_polymer_2 - ? IDR_MD_SETTINGS_VULCANIZED_P2_HTML - : IDR_MD_SETTINGS_VULCANIZED_HTML); + ? IDR_SETTINGS_VULCANIZED_P2_HTML + : IDR_SETTINGS_VULCANIZED_HTML); html_source->UseGzip(base::BindRepeating( [](const std::vector<std::string>& excluded_paths, const std::string& path) { @@ -331,7 +332,7 @@ }, std::move(exclude_from_gzip))); #if defined(OS_CHROMEOS) - html_source->AddResourcePath("manifest.json", IDR_MD_SETTINGS_MANIFEST); + html_source->AddResourcePath("manifest.json", IDR_SETTINGS_MANIFEST); #endif // defined (OS_CHROMEOS) #else // Add all settings resources. @@ -351,15 +352,15 @@ html_source); } -MdSettingsUI::~MdSettingsUI() {} +SettingsUI::~SettingsUI() {} -void MdSettingsUI::AddSettingsPageUIHandler( +void SettingsUI::AddSettingsPageUIHandler( std::unique_ptr<content::WebUIMessageHandler> handler) { DCHECK(handler); web_ui()->AddMessageHandler(std::move(handler)); } -void MdSettingsUI::DidStartNavigation( +void SettingsUI::DidStartNavigation( content::NavigationHandle* navigation_handle) { if (navigation_handle->IsSameDocument()) return; @@ -367,22 +368,22 @@ load_start_time_ = base::Time::Now(); } -void MdSettingsUI::DocumentLoadedInFrame( +void SettingsUI::DocumentLoadedInFrame( content::RenderFrameHost* render_frame_host) { UMA_HISTOGRAM_TIMES("Settings.LoadDocumentTime.MD", base::Time::Now() - load_start_time_); } -void MdSettingsUI::DocumentOnLoadCompletedInMainFrame() { +void SettingsUI::DocumentOnLoadCompletedInMainFrame() { UMA_HISTOGRAM_TIMES("Settings.LoadCompletedTime.MD", base::Time::Now() - load_start_time_); } #if defined(OS_CHROMEOS) // static -void MdSettingsUI::InitOSWebUIHandlers(Profile* profile, - content::WebUI* web_ui, - content::WebUIDataSource* html_source) { +void SettingsUI::InitOSWebUIHandlers(Profile* profile, + content::WebUI* web_ui, + content::WebUIDataSource* html_source) { web_ui->AddMessageHandler( std::make_unique<chromeos::settings::AccessibilityHandler>(web_ui)); web_ui->AddMessageHandler(
diff --git a/chrome/browser/ui/webui/settings/md_settings_ui.h b/chrome/browser/ui/webui/settings/settings_ui.h similarity index 74% rename from chrome/browser/ui/webui/settings/md_settings_ui.h rename to chrome/browser/ui/webui/settings/settings_ui.h index 73df051..c5c2fc9f 100644 --- a/chrome/browser/ui/webui/settings/md_settings_ui.h +++ b/chrome/browser/ui/webui/settings/settings_ui.h
@@ -2,8 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef CHROME_BROWSER_UI_WEBUI_SETTINGS_MD_SETTINGS_UI_H_ -#define CHROME_BROWSER_UI_WEBUI_SETTINGS_MD_SETTINGS_UI_H_ +#ifndef CHROME_BROWSER_UI_WEBUI_SETTINGS_SETTINGS_UI_H_ +#define CHROME_BROWSER_UI_WEBUI_SETTINGS_SETTINGS_UI_H_ #include "base/macros.h" #include "base/time/time.h" @@ -16,7 +16,7 @@ namespace content { class WebUIDataSource; class WebUIMessageHandler; -} +} // namespace content namespace user_prefs { class PrefRegistrySyncable; @@ -25,13 +25,13 @@ namespace settings { // The WebUI handler for chrome://settings. -class MdSettingsUI : public content::WebUIController, - public content::WebContentsObserver { +class SettingsUI : public content::WebUIController, + public content::WebContentsObserver { public: static void RegisterProfilePrefs(user_prefs::PrefRegistrySyncable* registry); - explicit MdSettingsUI(content::WebUI* web_ui); - ~MdSettingsUI() override; + explicit SettingsUI(content::WebUI* web_ui); + ~SettingsUI() override; #if defined(OS_CHROMEOS) // Initializes the WebUI message handlers for OS-specific settings. @@ -44,7 +44,7 @@ void DidStartNavigation( content::NavigationHandle* navigation_handle) override; void DocumentLoadedInFrame( - content::RenderFrameHost *render_frame_host) override; + content::RenderFrameHost* render_frame_host) override; void DocumentOnLoadCompletedInMainFrame() override; private: @@ -53,9 +53,9 @@ base::Time load_start_time_; - DISALLOW_COPY_AND_ASSIGN(MdSettingsUI); + DISALLOW_COPY_AND_ASSIGN(SettingsUI); }; } // namespace settings -#endif // CHROME_BROWSER_UI_WEBUI_SETTINGS_MD_SETTINGS_UI_H_ +#endif // CHROME_BROWSER_UI_WEBUI_SETTINGS_SETTINGS_UI_H_
diff --git a/chrome/browser/ui/webui/settings/md_settings_ui_browsertest.cc b/chrome/browser/ui/webui/settings/settings_ui_browsertest.cc similarity index 89% rename from chrome/browser/ui/webui/settings/md_settings_ui_browsertest.cc rename to chrome/browser/ui/webui/settings/settings_ui_browsertest.cc index da53ce1..459f2165 100644 --- a/chrome/browser/ui/webui/settings/md_settings_ui_browsertest.cc +++ b/chrome/browser/ui/webui/settings/settings_ui_browsertest.cc
@@ -15,11 +15,11 @@ #include "content/public/common/url_constants.h" #include "url/gurl.h" -typedef InProcessBrowserTest MdSettingsUITest; +typedef InProcessBrowserTest SettingsUITest; using ui_test_utils::NavigateToURL; -IN_PROC_BROWSER_TEST_F(MdSettingsUITest, ViewSourceDoesntCrash) { +IN_PROC_BROWSER_TEST_F(SettingsUITest, ViewSourceDoesntCrash) { NavigateToURL(browser(), GURL(content::kViewSourceScheme + std::string(":") + chrome::kChromeUISettingsURL + std::string("strings.js"))); @@ -27,7 +27,7 @@ // Catch lifetime issues in message handlers. There was previously a problem // with PrefMember calling Init again after Destroy. -IN_PROC_BROWSER_TEST_F(MdSettingsUITest, ToggleJavaScript) { +IN_PROC_BROWSER_TEST_F(SettingsUITest, ToggleJavaScript) { NavigateToURL(browser(), GURL(chrome::kChromeUISettingsURL)); const auto& handlers = *browser()
diff --git a/chrome/credential_provider/gaiacp/associated_user_validator.cc b/chrome/credential_provider/gaiacp/associated_user_validator.cc index ebef2f3f..dfb8c70 100644 --- a/chrome/credential_provider/gaiacp/associated_user_validator.cc +++ b/chrome/credential_provider/gaiacp/associated_user_validator.cc
@@ -153,6 +153,18 @@ last_update(update_time), pending_query_thread(thread_handle) {} +AssociatedUserValidator::ScopedBlockDenyAccessUpdate:: + ScopedBlockDenyAccessUpdate(AssociatedUserValidator* validator) + : validator_(validator) { + DCHECK(validator_); + validator_->BlockDenyAccessUpdate(); +} + +AssociatedUserValidator::ScopedBlockDenyAccessUpdate:: + ~ScopedBlockDenyAccessUpdate() { + validator_->UnblockDenyAccessUpdate(); +} + // static AssociatedUserValidator* AssociatedUserValidator::Get() { return *GetInstanceStorage(); @@ -215,14 +227,12 @@ return S_OK; } -std::set<base::string16> AssociatedUserValidator::GetUpdatedAssociatedSids() { +size_t AssociatedUserValidator::GetAssociatedUsersCount() { + base::AutoLock locker(validator_lock_); + UpdateAssociatedSids(nullptr); - std::set<base::string16> associated_sids; - for (const auto& it : user_to_token_handle_info_) - associated_sids.insert(it.first); - - return associated_sids; + return user_to_token_handle_info_.size(); } bool AssociatedUserValidator::IsUserAccessBlockingEnforced( @@ -239,37 +249,48 @@ return true; } -void AssociatedUserValidator::DenySigninForUsersWithInvalidTokenHandles( +bool AssociatedUserValidator::DenySigninForUsersWithInvalidTokenHandles( CREDENTIAL_PROVIDER_USAGE_SCENARIO cpus) { + base::AutoLock locker(validator_lock_); + + if (block_deny_access_update_) + return false; + if (!IsUserAccessBlockingEnforced(cpus)) - return; + return false; HRESULT hr = UpdateAssociatedSids(nullptr); if (FAILED(hr)) { LOGFN(ERROR) << "UpdateAssociatedSids hr=" << putHR(hr); - return; + return false; } auto policy = ScopedLsaPolicy::Create(POLICY_ALL_ACCESS); + bool user_denied_signin = false; for (const auto& user_info : user_to_token_handle_info_) { const base::string16& sid = user_info.first; if (locked_user_sids_.find(sid) != locked_user_sids_.end()) continue; - if (!IsTokenHandleValidForUser(sid)) { + if (!IsTokenHandleValidForUserInternal(sid)) { LOGFN(INFO) << "Revoking access for sid=" << sid; HRESULT hr = ModifyUserAccess(policy, sid, false); if (FAILED(hr)) { LOGFN(ERROR) << "ModifyUserAccess sid=" << sid << " hr=" << putHR(hr); } else { locked_user_sids_.insert(sid); + user_denied_signin = true; } } } + + return user_denied_signin; } HRESULT AssociatedUserValidator::RestoreUserAccess(const base::string16& sid) { + base::AutoLock locker(validator_lock_); + if (locked_user_sids_.erase(sid)) { auto policy = ScopedLsaPolicy::Create(POLICY_ALL_ACCESS); return ModifyUserAccess(policy, sid, true); @@ -280,6 +301,8 @@ void AssociatedUserValidator::AllowSigninForAllAssociatedUsers( CREDENTIAL_PROVIDER_USAGE_SCENARIO cpus) { + base::AutoLock locker(validator_lock_); + if (!MdmEnrollmentEnabled() || !CGaiaCredentialProvider::IsUsageScenarioSupported(cpus)) return; @@ -299,6 +322,8 @@ } void AssociatedUserValidator::AllowSigninForUsersWithInvalidTokenHandles() { + base::AutoLock locker(validator_lock_); + LOGFN(INFO); auto policy = ScopedLsaPolicy::Create(POLICY_ALL_ACCESS); for (auto& sid : locked_user_sids_) { @@ -310,6 +335,8 @@ } void AssociatedUserValidator::StartRefreshingTokenHandleValidity() { + base::AutoLock locker(validator_lock_); + std::map<base::string16, base::string16> sid_to_handle; HRESULT hr = UpdateAssociatedSids(&sid_to_handle); @@ -404,6 +431,12 @@ bool AssociatedUserValidator::IsTokenHandleValidForUser( const base::string16& sid) { + base::AutoLock locker(validator_lock_); + return IsTokenHandleValidForUserInternal(sid); +} + +bool AssociatedUserValidator::IsTokenHandleValidForUserInternal( + const base::string16& sid) { // All token handles are valid when no internet connection is available. if (!HasInternetConnection()) return true; @@ -469,4 +502,26 @@ return validity_it->second->is_valid; } +void AssociatedUserValidator::BlockDenyAccessUpdate() { + base::AutoLock locker(validator_lock_); + ++block_deny_access_update_; +} + +void AssociatedUserValidator::UnblockDenyAccessUpdate() { + base::AutoLock locker(validator_lock_); + DCHECK(block_deny_access_update_ > 0); + --block_deny_access_update_; +} + +bool AssociatedUserValidator::IsDenyAccessUpdateBlocked() const { + base::AutoLock locker(validator_lock_); + return block_deny_access_update_ > 0; +} + +bool AssociatedUserValidator::IsUserAccessBlocked( + const base::string16& sid) const { + base::AutoLock locker(validator_lock_); + return locked_user_sids_.find(sid) != locked_user_sids_.end(); +} + } // namespace credential_provider
diff --git a/chrome/credential_provider/gaiacp/associated_user_validator.h b/chrome/credential_provider/gaiacp/associated_user_validator.h index f2694874..0f5cb9d 100644 --- a/chrome/credential_provider/gaiacp/associated_user_validator.h +++ b/chrome/credential_provider/gaiacp/associated_user_validator.h
@@ -10,9 +10,10 @@ #include <map> #include <set> -#include <base/strings/string16.h> -#include <base/time/time.h> -#include <base/win/scoped_handle.h> +#include "base/strings/string16.h" +#include "base/synchronization/lock.h" +#include "base/time/time.h" +#include "base/win/scoped_handle.h" #include "chrome/credential_provider/gaiacp/gaia_credential_provider_i.h" @@ -20,8 +21,57 @@ // Caches the current validity of token handles and updates the validity if // it is older than a specified validity lifetime. +// NOTE: This class is thread safe. +// +// The following functions are called at a time when it is impossible for +// the valaditor to be accessed by multiple threads. The validator will only +// be accessed from another thread through the BackgroundTokenHandleUpdater +// that is created in CGaiaCredentialProvider::Advise and destroyed in +// CGaiaCredentialProvider::Unadvise: +// StartRefreshingTokenHandleValidity: Only called on the main thread during +// a call to DllGetClassObject. +// IsUserAccessBlockingEnforced: Only called on the main thread in +// CGaiaCredentialProvider::Advise and in +// CGaiaCredentialProviderFilter::UpdateRemoteCredential. +// AllowSigninForUsersWithInvalidTokenHandles: Only called on the main thread +// in CGaiaCredentialProvider::FinalRelease. +// AllowSigninForAllAssociatedUsers: Only called on the main thread in +// CGaiaCredentialProviderFilter::Filter. +// +// The following functions can be called while the validator can be accessed +// from another thread: +// IsTokenHandleValidForUser: Called on the main thread indirectly in +// CGaiaCredentialProvider::GetCredentialCount. Also called on the update +// thread while checking DenySigninForUsersWithInvalidTokenHandles. +// GetAssociatedUsersCount: Only called on the main thread indirectly in +// CGaiaCredentialProvider::GetCredentialCount. +// RestoreUserAccess: Only called on the main thread in +// CGaiaCredentialBase::HandleAutologon. +// +// Finally the one function that can be called on the update thread is +// DenySigninForUsersWithInvalidTokenHandles. If this function returns +// true, it will queue a credential update which will only be executed +// on the main thread. The update thread will then be dormant for +// |kTokenHandleValidityLifetime| seconds and in this time the expected +// update of the credentials on the main thread via a call to +// CGaiaCredentialProvider::GetCredentialCount should be able to complete +// before a new update is requested on the update thread. This timing will +// protect the two functions IsTokenHandleValidForUser and +// GetAssociatedUsersCount from being called by multiple threads at the same +// time. class AssociatedUserValidator { public: + // Prevent update of user access through the call to + // DenySigninForUsersWithInvalidTokenHandles. This will be used to prevent + // locking out users that are in the process of signing in. + class ScopedBlockDenyAccessUpdate { + public: + explicit ScopedBlockDenyAccessUpdate(AssociatedUserValidator* validator); + ~ScopedBlockDenyAccessUpdate(); + + private: + AssociatedUserValidator* validator_; + }; // Default timeout when querying token info for token handles. If a timeout // occurs the token handle is assumed to be valid. static const base::TimeDelta kDefaultTokenHandleValidationTimeout; @@ -54,8 +104,9 @@ CREDENTIAL_PROVIDER_USAGE_SCENARIO cpus) const; // Goes through all associated users found and denies their access to sign - // in to the system based on the validity of their token handle. - void DenySigninForUsersWithInvalidTokenHandles( + // in to the system based on the validity of their token handle. Returns true + // if a user has just been denied signin access. + bool DenySigninForUsersWithInvalidTokenHandles( CREDENTIAL_PROVIDER_USAGE_SCENARIO cpus); // Restores the access for a user that was denied access (if applicable). @@ -72,15 +123,27 @@ void AllowSigninForAllAssociatedUsers( CREDENTIAL_PROVIDER_USAGE_SCENARIO cpus); - // Fills |associated_sids| with the sids of all valid associated users - // found on this system. - std::set<base::string16> GetUpdatedAssociatedSids(); - size_t GetAssociatedUsersCount() { return GetUpdatedAssociatedSids().size(); } + // Gets the updated count of valid associated users that exist on this system. + size_t GetAssociatedUsersCount(); + + // Returns whether the user should be locked out of sign in (only used in + // tests). + bool IsDenyAccessUpdateBlocked() const; protected: + // Returns the storage used for the instance pointer. + static AssociatedUserValidator** GetInstanceStorage(); + explicit AssociatedUserValidator(base::TimeDelta validation_timeout); virtual ~AssociatedUserValidator(); + // Returns whether the user should be locked out of sign in (only used in + // tests). + bool IsUserAccessBlocked(const base::string16& sid) const; + + private: + bool IsTokenHandleValidForUserInternal(const base::string16& sid); + bool HasInternetConnection() const; void CheckTokenHandleValidity( const std::map<base::string16, base::string16>& handles_to_verify); @@ -90,9 +153,6 @@ HRESULT UpdateAssociatedSids( std::map<base::string16, base::string16>* sid_to_handle); - // Returns the storage used for the instance pointer. - static AssociatedUserValidator** GetInstanceStorage(); - // Stores information about the current state of a user's token handle. // This information includes: // * The last token handle found for the user. @@ -121,12 +181,27 @@ base::win::ScopedHandle pending_query_thread; }; + // Increments / decrements |block_deny_access_update_| to prevent denying + // user access when a token handle becomes invalid. Only called via a + // ScopedBlockDenyAccessUpdate object. + void BlockDenyAccessUpdate(); + void UnblockDenyAccessUpdate(); + // Maps a user's sid to the token handle info associated with this user (if // any). std::map<base::string16, std::unique_ptr<TokenHandleInfo>> user_to_token_handle_info_; base::TimeDelta validation_timeout_; std::set<base::string16> locked_user_sids_; + mutable base::Lock validator_lock_; + + // When |block_deny_access_update_| != 0, prevent users from being denied + // access when DenySigninForUsersWithInvalidTokenHandles is called. This + // prevents users from being locked out while signing is occurring but a token + // handle update is also being requested at the same time. The functions + // LockDenyAccessUpdate / UnlockDenyAccessUpdate are called in the lifetime of + // a ScopedBlockDenyAccessUpdate to update this member. + size_t block_deny_access_update_ = 0; }; } // namespace credential_provider
diff --git a/chrome/credential_provider/gaiacp/associated_user_validator_unittests.cc b/chrome/credential_provider/gaiacp/associated_user_validator_unittests.cc index 07c4ba2..6f12e4c 100644 --- a/chrome/credential_provider/gaiacp/associated_user_validator_unittests.cc +++ b/chrome/credential_provider/gaiacp/associated_user_validator_unittests.cc
@@ -254,6 +254,47 @@ EXPECT_EQ(1u, fake_http_url_fetcher_factory()->requests_created()); } +TEST_F(AssociatedUserValidatorTest, BlockDenyUserAccess) { + FakeAssociatedUserValidator validator; + + ASSERT_EQ(S_OK, SetGlobalFlagForTesting(kRegMdmUrl, L"https://mdm.com")); + + CComBSTR sid; + ASSERT_EQ(S_OK, fake_os_user_manager()->CreateTestOSUser( + L"username", L"password", L"fullname", L"comment", + L"gaia-id", base::string16(), &sid)); + + // Invalid token fetch result. + fake_http_url_fetcher_factory()->SetFakeResponse( + GURL(AssociatedUserValidator::kTokenInfoUrl), + FakeWinHttpUrlFetcher::Headers(), "{}"); + + validator.StartRefreshingTokenHandleValidity(); + + // Apply two levels blocks to deny access. This should prevent users from + // being blocked from accessing the system. + { + AssociatedUserValidator::ScopedBlockDenyAccessUpdate deny_blocker_outer( + &validator); + { + AssociatedUserValidator::ScopedBlockDenyAccessUpdate deny_blocker_inner( + &validator); + EXPECT_FALSE( + validator.DenySigninForUsersWithInvalidTokenHandles(CPUS_LOGON)); + EXPECT_FALSE(validator.IsUserAccessBlocked(OLE2W(sid))); + } + + EXPECT_FALSE( + validator.DenySigninForUsersWithInvalidTokenHandles(CPUS_LOGON)); + EXPECT_FALSE(validator.IsUserAccessBlocked(OLE2W(sid))); + } + // Unblock deny access. User should not be blocked. + EXPECT_TRUE(validator.DenySigninForUsersWithInvalidTokenHandles(CPUS_LOGON)); + EXPECT_TRUE(validator.IsUserAccessBlocked(OLE2W(sid))); + + EXPECT_EQ(1u, fake_http_url_fetcher_factory()->requests_created()); +} + // Tests various scenarios where user access is blocked. // Parameters are: // 1. CREDENTIAL_PROVIDER_USAGE_SCENARIO - Usage scenario.
diff --git a/chrome/credential_provider/gaiacp/gaia_credential_base.cc b/chrome/credential_provider/gaiacp/gaia_credential_base.cc index 86bfaf5..3011c1b 100644 --- a/chrome/credential_provider/gaiacp/gaia_credential_base.cc +++ b/chrome/credential_provider/gaiacp/gaia_credential_base.cc
@@ -649,6 +649,8 @@ events_->SetFieldSubmitButton(this, FID_SUBMIT, FID_DESCRIPTION); UpdateSubmitButtonInteractiveState(); } + + token_update_locker_.reset(); } HRESULT CGaiaCredentialBase::GetBaseGlsCommandline( @@ -810,19 +812,11 @@ } } - // Restore user's access so that they can sign in. - HRESULT hr = - AssociatedUserValidator::Get()->RestoreUserAccess(OLE2W(get_sid())); - if (FAILED(hr) && hr != HRESULT_FROM_NT(STATUS_OBJECT_NAME_NOT_FOUND)) { - LOGFN(ERROR) << "RestoreUserAccess hr=" << putHR(hr); - return hr; - } - // The OS user has already been created, so return all the information // needed to log them in. DWORD cpus = 0; provider()->GetUsageScenario(&cpus); - hr = BuildCredPackAuthenticationBuffer( + HRESULT hr = BuildCredPackAuthenticationBuffer( domain_, get_username(), get_password(), static_cast<CREDENTIAL_PROVIDER_USAGE_SCENARIO>(cpus), cpcs); if (FAILED(hr)) { @@ -830,6 +824,23 @@ return hr; } + // Prevent update of token handle validity until after sign in has completed + // so that a race condition doesn't end up locking out a user while they are + // in the process of signing in. The lock must occur before restoring access + // to the user below to prevent a race condition where the user would have + // their access restored but then the token handle update thread is + // immediately executed which causes the user to be locked again afterwards. + PreventDenyAccessUpdate(); + + // Restore user's access so that they can sign in. + hr = AssociatedUserValidator::Get()->RestoreUserAccess(OLE2W(get_sid())); + if (FAILED(hr) && hr != HRESULT_FROM_NT(STATUS_OBJECT_NAME_NOT_FOUND)) { + LOGFN(ERROR) << "RestoreUserAccess hr=" << putHR(hr); + ::CoTaskMemFree(cpcs->rgbSerialization); + cpcs->rgbSerialization = nullptr; + return hr; + } + cpcs->clsidCredentialProvider = CLSID_GaiaCredentialProvider; *cpgsr = CPGSR_RETURN_CREDENTIAL_FINISHED; @@ -857,6 +868,14 @@ #endif // defined(GOOGLE_CHROME_BUILD) } +void CGaiaCredentialBase::PreventDenyAccessUpdate() { + if (!token_update_locker_) { + token_update_locker_.reset( + new AssociatedUserValidator::ScopedBlockDenyAccessUpdate( + AssociatedUserValidator::Get())); + } +} + // static BSTR CGaiaCredentialBase::AllocErrorString(UINT id) { CComBSTR str(GetStringResource(id).c_str()); @@ -1063,6 +1082,7 @@ HRESULT hr = HandleAutologon(cpgsr, cpcs); + bool submit_button_enabled = false; // Don't clear the state of the credential on error. The error can occur // because the user is locked out or entered an incorrect old password when // trying to update their password. In these situations it may still be @@ -1104,7 +1124,7 @@ *status_icon = CPSI_NONE; *cpgsr = CPGSR_NO_CREDENTIAL_FINISHED; LOGFN(INFO) << "No internet connection"; - UpdateSubmitButtonInteractiveState(); + submit_button_enabled = UpdateSubmitButtonInteractiveState(); hr = S_OK; } else { @@ -1131,9 +1151,15 @@ this, FID_CURRENT_PASSWORD_FIELD, needs_windows_password_ ? CPFIS_FOCUSED : CPFIS_NONE); } - UpdateSubmitButtonInteractiveState(); + submit_button_enabled = UpdateSubmitButtonInteractiveState(); } - // Otherwise, keep the ui disable forever now. ReportResult will eventually + + // If user interaction is enabled that means we are not trying to do final + // sign in of the account so we can re-enable token updates. + if (submit_button_enabled) + token_update_locker_.reset(); + + // Otherwise, keep the ui disabled forever now. ReportResult will eventually // be called on success or failure and the reset of the state of the // credential will be done there. return hr; @@ -1746,6 +1772,11 @@ result_status_ = STATUS_SUCCESS; + // Prevent update of token handle validity until after sign in has completed + // so the list of credentials doesn't suddenly change between now and when the + // attempt to auto login occurs. + PreventDenyAccessUpdate(); + // When this function returns, winlogon will be told to logon to the newly // created account. This is important, as the save account info process // can't actually save the info until the user's profile is created, which @@ -1775,15 +1806,17 @@ CComBSTR(), FALSE); } -void CGaiaCredentialBase::UpdateSubmitButtonInteractiveState() { +bool CGaiaCredentialBase::UpdateSubmitButtonInteractiveState() { + bool should_enable = + logon_ui_process_ == INVALID_HANDLE_VALUE && + ((!needs_windows_password_ || current_windows_password_.Length()) || + (needs_windows_password_ && request_force_password_change_)); if (events_) { - bool should_enable = - logon_ui_process_ == INVALID_HANDLE_VALUE && - ((!needs_windows_password_ || current_windows_password_.Length()) || - (needs_windows_password_ && request_force_password_change_)); events_->SetFieldInteractiveState( this, FID_SUBMIT, should_enable ? CPFIS_NONE : CPFIS_DISABLED); } + + return should_enable; } void CGaiaCredentialBase::DisplayPasswordField(int password_message) {
diff --git a/chrome/credential_provider/gaiacp/gaia_credential_base.h b/chrome/credential_provider/gaiacp/gaia_credential_base.h index 44ca403..d4848a4 100644 --- a/chrome/credential_provider/gaiacp/gaia_credential_base.h +++ b/chrome/credential_provider/gaiacp/gaia_credential_base.h
@@ -13,6 +13,7 @@ #include "base/values.h" #include "base/win/scoped_handle.h" #include "base/win/scoped_process_information.h" +#include "chrome/credential_provider/gaiacp/associated_user_validator.h" #include "chrome/credential_provider/gaiacp/gaia_credential_provider_i.h" #include "chrome/credential_provider/gaiacp/gcp_utils.h" #include "chrome/credential_provider/gaiacp/scoped_handle.h" @@ -171,6 +172,11 @@ CREDENTIAL_PROVIDER_GET_SERIALIZATION_RESPONSE* pcpgsr, CREDENTIAL_PROVIDER_CREDENTIAL_SERIALIZATION* pcpcs); + // Instantiates |token_update_locker_| so that user access cannot be denied + // during this time. This function is called when we are about to really sign + // in the user to Windows. + void PreventDenyAccessUpdate(); + // Writes value to omaha registry to record that GCP has been used. static void TellOmahaDidRun(); @@ -233,8 +239,8 @@ // not require direct user input to the credential (user is entering // credentials in GLS) or a submit of the credential is not valid (user needs // to enter the old Windows password but currently nothing has been entered in - // the password field). - void UpdateSubmitButtonInteractiveState(); + // the password field). Returns true if the submit button is enabled. + bool UpdateSubmitButtonInteractiveState(); // Stops the GLS process in case it is still executing. Often called when user // switches credentials in the middle of a sign in through the GLS. @@ -293,6 +299,13 @@ // Holds information about the success or failure of the sign in. NTSTATUS result_status_ = STATUS_SUCCESS; + + // When we finally want to allow user sign in. This object is instantiated + // to prevent updates of token handle validity until after sign in has + // completed so the the user cannot be locked out while they are trying to + // sign in. + std::unique_ptr<AssociatedUserValidator::ScopedBlockDenyAccessUpdate> + token_update_locker_; }; } // namespace credential_provider
diff --git a/chrome/credential_provider/gaiacp/gaia_credential_base_unittests.cc b/chrome/credential_provider/gaiacp/gaia_credential_base_unittests.cc index 93b4930..0ede0ea 100644 --- a/chrome/credential_provider/gaiacp/gaia_credential_base_unittests.cc +++ b/chrome/credential_provider/gaiacp/gaia_credential_base_unittests.cc
@@ -723,6 +723,88 @@ EXPECT_EQ(2ul, fake_os_user_manager()->GetUserCount()); } +TEST_F(GcpGaiaCredentialBaseTest, DenySigninBlockedDuringSignin) { + USES_CONVERSION; + + FakeAssociatedUserValidator validator; + FakeInternetAvailabilityChecker internet_checker; + ASSERT_EQ(S_OK, SetGlobalFlagForTesting(kRegMdmUrl, L"https://mdm.com")); + ASSERT_EQ(S_OK, SetGlobalFlagForTesting(kRegMdmAllowConsumerAccounts, 1)); + GoogleMdmEnrollmentStatusForTesting force_success(true); + + // Create a fake user that has the same gaia id as the test gaia id. + CComBSTR first_sid; + base::string16 username(L"foo"); + ASSERT_EQ(S_OK, fake_os_user_manager()->CreateTestOSUser( + username, L"password", L"name", L"comment", + base::UTF8ToUTF16(kDefaultGaiaId), base::string16(), + &first_sid)); + ASSERT_EQ(2ul, fake_os_user_manager()->GetUserCount()); + FakeGaiaCredentialProvider provider; + + // Invalid token fetch result. + fake_http_url_fetcher_factory()->SetFakeResponse( + GURL(AssociatedUserValidator::kTokenInfoUrl), + FakeWinHttpUrlFetcher::Headers(), "{}"); + + validator.StartRefreshingTokenHandleValidity(); + + // Start logon. + CComPtr<IGaiaCredential> gaia_cred; + CComPtr<ICredentialProviderCredential> cred; + ASSERT_EQ(S_OK, CreateCredentialWithProvider(&provider, &gaia_cred, &cred)); + + CComPtr<ITestCredential> test; + ASSERT_EQ(S_OK, cred.QueryInterface(&test)); + + ASSERT_EQ(S_OK, run_helper()->StartLogonProcessAndWait(cred)); + + // Signin process has already started. User should not be locked even if their + // token handle is invalid. + EXPECT_FALSE(validator.DenySigninForUsersWithInvalidTokenHandles(CPUS_LOGON)); + EXPECT_FALSE(validator.IsUserAccessBlocked(OLE2W(first_sid))); + + // Now finish the logon. + CREDENTIAL_PROVIDER_GET_SERIALIZATION_RESPONSE cpgsr; + CREDENTIAL_PROVIDER_CREDENTIAL_SERIALIZATION cpcs; + wchar_t* status_text; + CREDENTIAL_PROVIDER_STATUS_ICON status_icon; + ASSERT_EQ(S_OK, + cred->GetSerialization(&cpgsr, &cpcs, &status_text, &status_icon)); + EXPECT_EQ(nullptr, status_text); + EXPECT_EQ(CPSI_SUCCESS, status_icon); + EXPECT_EQ(CPGSR_RETURN_CREDENTIAL_FINISHED, cpgsr); + EXPECT_LT(0u, cpcs.cbSerialization); + EXPECT_NE(nullptr, cpcs.rgbSerialization); + + // State was not reset. + EXPECT_TRUE(test->AreCredentialsValid()); + + // User should have been associated. + EXPECT_EQ(test->GetFinalUsername(), username); + // Email should be the same as the default one. + EXPECT_EQ(test->GetFinalEmail(), kDefaultEmail); + + // Result has not been reported yet, user signin should still not be denied. + EXPECT_FALSE(validator.DenySigninForUsersWithInvalidTokenHandles(CPUS_LOGON)); + EXPECT_FALSE(validator.IsUserAccessBlocked(OLE2W(first_sid))); + + wchar_t* report_status_text = nullptr; + CREDENTIAL_PROVIDER_STATUS_ICON report_icon; + EXPECT_EQ(S_OK, cred->ReportResult(0, 0, &report_status_text, &report_icon)); + // State was reset. + EXPECT_FALSE(test->AreCredentialsValid()); + + EXPECT_EQ(S_OK, gaia_cred->Terminate()); + + // Now signin can be denied for the user if their token handle is invalid. + EXPECT_TRUE(validator.DenySigninForUsersWithInvalidTokenHandles(CPUS_LOGON)); + EXPECT_TRUE(validator.IsUserAccessBlocked(OLE2W(first_sid))); + + // No new user should be created. + EXPECT_EQ(2ul, fake_os_user_manager()->GetUserCount()); +} + TEST_F(GcpGaiaCredentialBaseTest, StripEmailTLD_Gmail) { USES_CONVERSION; FakeGaiaCredentialProvider provider;
diff --git a/chrome/credential_provider/gaiacp/gaia_credential_provider.cc b/chrome/credential_provider/gaiacp/gaia_credential_provider.cc index ff0e21e..273faefc 100644 --- a/chrome/credential_provider/gaiacp/gaia_credential_provider.cc +++ b/chrome/credential_provider/gaiacp/gaia_credential_provider.cc
@@ -13,6 +13,7 @@ #include "base/stl_util.h" #include "base/strings/string16.h" #include "base/strings/stringprintf.h" +#include "base/time/time.h" #include "base/values.h" #include "chrome/common/chrome_version.h" #include "chrome/credential_provider/common/gcp_strings.h" @@ -96,6 +97,176 @@ } // namespace +// Class that when constructed automatically starts a thread that tries +// to update the validity of available token handles. If the background +// thread detects that any token handle has changed, then it will notify +// the provider |event_handler| object of this event. +class BackgroundTokenHandleUpdater { + public: + BackgroundTokenHandleUpdater(CREDENTIAL_PROVIDER_USAGE_SCENARIO cpus, + ICredentialUpdateEventsHandler* event_handler); + ~BackgroundTokenHandleUpdater(); + + private: + static unsigned __stdcall PeriodicTokenHandleUpdate(void* param); + + CREDENTIAL_PROVIDER_USAGE_SCENARIO cpus_; + + // Raw pointer to the interface on CGaiaCredentialProvider that is used + // to notify that token handle validity has changed. Any instance of this + // class should be owned by the CGaiaCredentialProvider to ensure that + // this pointer outlives the updater. + ICredentialUpdateEventsHandler* event_handler_; + + base::win::ScopedHandle token_update_thread_; + base::WaitableEvent token_update_quit_event_; +}; + +BackgroundTokenHandleUpdater::BackgroundTokenHandleUpdater( + CREDENTIAL_PROVIDER_USAGE_SCENARIO cpus, + ICredentialUpdateEventsHandler* event_handler) + : cpus_(cpus), event_handler_(event_handler) { + unsigned wait_thread_id; + uintptr_t wait_thread = + _beginthreadex(nullptr, 0, PeriodicTokenHandleUpdate, + reinterpret_cast<void*>(this), 0, &wait_thread_id); + if (wait_thread != 0) { + token_update_thread_.Set( + reinterpret_cast<base::win::ScopedHandle::Handle>(wait_thread)); + } +} + +BackgroundTokenHandleUpdater::~BackgroundTokenHandleUpdater() { + if (token_update_thread_.IsValid()) { + // Tell the background thread to quit and then make sure it does. This + // prevents it from accessing data members that have been freed. + token_update_quit_event_.Signal(); + ::WaitForSingleObject(token_update_thread_.Get(), INFINITE); + } +} + +unsigned __stdcall BackgroundTokenHandleUpdater::PeriodicTokenHandleUpdate( + void* param) { + BackgroundTokenHandleUpdater* updater = + reinterpret_cast<BackgroundTokenHandleUpdater*>(param); + ICredentialUpdateEventsHandler* event_handler = updater->event_handler_; + base::WaitableEvent& stop_event = updater->token_update_quit_event_; + + while (true) { + HRESULT hr = ::WaitForSingleObject( + stop_event.handle(), + AssociatedUserValidator::kTokenHandleValidityLifetime.InMilliseconds()); + + if (hr != WAIT_TIMEOUT) + break; + + bool user_access_changed = + AssociatedUserValidator::Get() + ->DenySigninForUsersWithInvalidTokenHandles(updater->cpus_); + if (user_access_changed) { + LOGFN(INFO) << "A user token handle has been invalidated. Refreshing " + "credentials"; + } + event_handler->UpdateCredentialsIfNeeded(user_access_changed); + } + + return 0; +} + +CGaiaCredentialProvider::ComPtrStorage::ComPtrStorage() = default; +CGaiaCredentialProvider::ComPtrStorage::~ComPtrStorage() = default; + +CGaiaCredentialProvider::ProviderConcurrentState::ProviderConcurrentState() = + default; +CGaiaCredentialProvider::ProviderConcurrentState::~ProviderConcurrentState() = + default; + +bool CGaiaCredentialProvider::ProviderConcurrentState:: + RequestUserRefreshIfNeeded(bool user_access_changed) { + base::AutoLock locker(state_update_lock_); + if (auto_logon_credential_) { + // Auto logon has precedence and has already signalled a credential changed, + // save the user refresh for a later update and don't signal the change + // again. + if (user_access_changed) + pending_users_refresh_needed_ = user_access_changed; + return false; + } + + // No auto logon present and there is a user access change or a pending + // refresh to be executed. We clear the pending the refresh and just set + // |users_need_to_be_refreshed_| to notify that a refresh is needed on the + // next GetCredentialCount. + if (user_access_changed || pending_users_refresh_needed_) { + users_need_to_be_refreshed_ = true; + pending_users_refresh_needed_ = false; + } + + return users_need_to_be_refreshed_; +} + +bool CGaiaCredentialProvider::ProviderConcurrentState::SetAutoLogonCredential( + const CComPtr<IGaiaCredential>& auto_logon_credential) { + base::AutoLock locker(state_update_lock_); + // Always update the credential. + auto_logon_credential_ = auto_logon_credential; + if (auto_logon_credential_) { + // If a previous user refresh was signalled, then the credential changed + // was already sent. Don't need to send it again, but we need save the + // user refresh for a later credential change event since auto logon has + // precedence. + if (users_need_to_be_refreshed_) { + pending_users_refresh_needed_ = users_need_to_be_refreshed_; + users_need_to_be_refreshed_ = false; + return false; + } + + return true; + } + + // No auto logon credential was set, no need to send credential changed event. + return false; +} + +void CGaiaCredentialProvider::ProviderConcurrentState::GetUpdatedState( + bool* needs_to_refresh_users, + ComPtrStorage* auto_logon_credential) { + DCHECK(needs_to_refresh_users); + DCHECK(auto_logon_credential); + base::AutoLock locker(state_update_lock_); + + // States need to be mutually exclusive. + DCHECK(!users_need_to_be_refreshed_ || !auto_logon_credential_); + + *needs_to_refresh_users = users_need_to_be_refreshed_; + auto_logon_credential->gaia_cred = auto_logon_credential_; + + // No specific state set this cycle, maybe there was a user update pending + // that should now be processed. + if (!users_need_to_be_refreshed_ && !auto_logon_credential_) { + *needs_to_refresh_users = pending_users_refresh_needed_; + // Can now reset the pending refresh since it has been processed. + pending_users_refresh_needed_ = false; + } + + // State has been extracted for use, reset back to the default state. + InternalReset(); +} + +void CGaiaCredentialProvider::ProviderConcurrentState::Reset() { + base::AutoLock locker(state_update_lock_); + InternalReset(); + + // This is a full explicit reset, we also need to reset any pending refresh + // that may be needed. + pending_users_refresh_needed_ = false; +} + +void CGaiaCredentialProvider::ProviderConcurrentState::InternalReset() { + users_need_to_be_refreshed_ = false; + auto_logon_credential_.Release(); +} + CGaiaCredentialProvider::CGaiaCredentialProvider() {} CGaiaCredentialProvider::~CGaiaCredentialProvider() {} @@ -108,6 +279,7 @@ void CGaiaCredentialProvider::FinalRelease() { LOGFN(INFO); + CHECK(!token_handle_updater_); ClearTransient(); // Unlock all the users that had their access locked due to invalid token // handles. @@ -125,11 +297,12 @@ void CGaiaCredentialProvider::ClearTransient() { LOGFN(INFO); + CHECK(!token_handle_updater_); // Reset event support. advise_context_ = 0; events_.Release(); - new_user_sid_.Empty(); - index_ = std::numeric_limits<size_t>::max(); + set_serialization_sid_.clear(); + concurrent_state_.Reset(); } void CGaiaCredentialProvider::CleanupOlderVersions() { @@ -155,7 +328,7 @@ if (SUCCEEDED(hr)) { hr = cred->Initialize(this); if (SUCCEEDED(hr)) { - AddCredentialAndCheckAutoLogon(cred, base::string16()); + AddCredentialAndCheckAutoLogon(cred, base::string16(), nullptr); } else { LOG(ERROR) << "Could not create credential hr=" << putHR(hr); } @@ -165,7 +338,8 @@ } HRESULT CGaiaCredentialProvider::CreateReauthCredentials( - ICredentialProviderUserArray* users) { + ICredentialProviderUserArray* users, + ComPtrStorage* auto_logon_credential) { std::map<base::string16, std::pair<base::string16, base::string16>> sid_to_username; @@ -240,7 +414,7 @@ return hr; } - AddCredentialAndCheckAutoLogon(cred, sid); + AddCredentialAndCheckAutoLogon(cred, sid, auto_logon_credential); } return S_OK; @@ -248,10 +422,14 @@ void CGaiaCredentialProvider::AddCredentialAndCheckAutoLogon( const CComPtr<IGaiaCredential>& cred, - const base::string16& sid) { + const base::string16& sid, + ComPtrStorage* auto_logon_credential) { USES_CONVERSION; users_.emplace_back(cred); + if (!auto_logon_credential) + return; + if (sid.empty()) return; @@ -267,8 +445,28 @@ if (set_serialization_sid_ != sid) return; - index_ = users_.size() - 1; - new_user_sid_ = W2COLE(sid.c_str()); + auto_logon_credential->gaia_cred = cred; + set_serialization_sid_.clear(); +} + +void CGaiaCredentialProvider::RecreateCredentials( + ComPtrStorage* auto_logon_credential) { + LOGFN(INFO); + DCHECK(user_array_); + + DestroyCredentials(); + + CREDENTIAL_PROVIDER_ACCOUNT_OPTIONS options; + HRESULT hr = user_array_->GetAccountOptions(&options); + bool showing_other_user = + SUCCEEDED(hr) && options != CPAO_EMPTY_CONNECTED && options != CPAO_NONE; + hr = CreateAnonymousCredentialIfNeeded(showing_other_user); + if (FAILED(hr)) + LOG(ERROR) << "Could not create anonymous credential hr=" << putHR(hr); + + hr = CreateReauthCredentials(user_array_, auto_logon_credential); + if (FAILED(hr)) + LOG(ERROR) << "CreateReauthCredentials hr=" << putHR(hr); } // Static. @@ -300,6 +498,24 @@ return true; } +// ICredentialUpdateEventsHandler ////////////////////////////////////////////// + +void CGaiaCredentialProvider::UpdateCredentialsIfNeeded( + bool user_access_changed) { + // Defer refresh of the users to the next GetCredentialCount that will be + // called after the credentials changed event. This prevents potential + // contention of the |users_| list in multiple places. If the call to + // RequestUserRefreshIfNeeded returns true, this means we are allowed to + // proceed with a credentials changed event. Otherwise either no credentials + // need to be updated (|user_access_changed| == false) or a higher priority + // update is pending (e.g. auto logon) and thus we cannot send a + // credentials changed event. + if (concurrent_state_.RequestUserRefreshIfNeeded(user_access_changed) && + events_) { + events_->CredentialsChanged(advise_context_); + } +} + // IGaiaCredentialProvider //////////////////////////////////////////////////// HRESULT CGaiaCredentialProvider::GetUsageScenario(DWORD* cpus) { @@ -316,31 +532,32 @@ BOOL fire_credentials_changed) { DCHECK(!credential || sid); - // |credential| should be in the |users_|. Find its index. - index_ = std::numeric_limits<size_t>::max(); - for (size_t i = 0; i < users_.size(); ++i) { - if (users_[i].IsEqualObject(credential)) { - index_ = i; - break; - } - } - if (index_ == std::numeric_limits<size_t>::max()) { - LOGFN(INFO) << "Could not find credential"; - return E_INVALIDARG; + if (!fire_credentials_changed) + return S_OK; + + // Ensure that user access cannot be denied at this time so that the user + // that is about to sign in won't be locked. If a ScopedLockDenyAccessUpdate + // is created before calling this function this should guarantee that + // situation because the call to BlockDenyAccessUpdate is locked with the + // same lock that is used in DenySigninForUsersWithInvalidTokenHandles. + // So either the call to Deny has finished and no new deny will occur + // afterwards or the Deny will be disabled because the block has been + // incremented first. + CHECK(!credential || + AssociatedUserValidator::Get()->IsDenyAccessUpdateBlocked()); + + CComPtr<IGaiaCredential> gaia_credential; + if (credential->QueryInterface(IID_IGaiaCredential, + reinterpret_cast<void**>(&gaia_credential)) == + S_OK) { + // Try to set the auto logon credential. If it succeeds we can raise a + // credential changed event. + if (concurrent_state_.SetAutoLogonCredential(gaia_credential) && events_) + events_->CredentialsChanged(advise_context_); } - new_user_sid_ = sid; - - // Tell winlogon.exe that credential info has changed. This provider will - // make the newly created user the default login credential with auto logon - // enabled. See GetCredentialCount() for more details. - HRESULT hr = S_OK; - if (events_ && fire_credentials_changed) - hr = events_->CredentialsChanged(advise_context_); - - LOGFN(INFO) << "hr=" << putHR(hr) << " sid=" << new_user_sid_.m_str - << " index=" << index_; - return hr; + LOGFN(INFO) << "Signing in authenticated sid=" << OLE2CW(sid); + return S_OK; } // ICredentialProviderSetUserArray //////////////////////////////////////////// @@ -348,6 +565,7 @@ HRESULT CGaiaCredentialProvider::SetUserArray( ICredentialProviderUserArray* users) { LOGFN(INFO); + CHECK(!token_handle_updater_); if (!IsUsageScenarioSupported(cpus_)) return S_OK; @@ -357,19 +575,11 @@ return E_UNEXPECTED; } - CREDENTIAL_PROVIDER_ACCOUNT_OPTIONS options; - HRESULT hr = users->GetAccountOptions(&options); - bool showing_other_user = - SUCCEEDED(hr) && options != CPAO_EMPTY_CONNECTED && options != CPAO_NONE; - hr = CreateAnonymousCredentialIfNeeded(showing_other_user); - if (FAILED(hr)) - LOG(ERROR) << "Could not create anonymous credential hr=" << putHR(hr); + user_array_ = users; + // Force refresh of all users on the next GetCredentialCount. + concurrent_state_.RequestUserRefreshIfNeeded(true); - hr = CreateReauthCredentials(users); - if (FAILED(hr)) - LOG(ERROR) << "CreateReauthCredentials hr=" << putHR(hr); - - return hr; + return S_OK; } // ICredentialProvider //////////////////////////////////////////////////////// @@ -378,6 +588,7 @@ CREDENTIAL_PROVIDER_USAGE_SCENARIO cpus, DWORD flags) { ClearTransient(); + CHECK(!token_handle_updater_); cpus_ = cpus; cpus_flags_ = flags; @@ -389,6 +600,7 @@ HRESULT CGaiaCredentialProvider::SetSerialization( const CREDENTIAL_PROVIDER_CREDENTIAL_SERIALIZATION* pcpcs) { DCHECK(pcpcs); + CHECK(!token_handle_updater_); if (pcpcs->clsidCredentialProvider != CLSID_GaiaCredentialProvider) return E_NOTIMPL; @@ -416,19 +628,25 @@ HRESULT CGaiaCredentialProvider::Advise(ICredentialProviderEvents* pcpe, UINT_PTR context) { DCHECK(pcpe); + CHECK(!token_handle_updater_); - bool had_previous = events_.p != nullptr; events_ = pcpe; advise_context_ = context; - LOGFN(INFO) << " had=" << had_previous; + + if (AssociatedUserValidator::Get()->IsUserAccessBlockingEnforced(cpus_)) { + token_handle_updater_ = + std::make_unique<BackgroundTokenHandleUpdater>(cpus_, this); + } return S_OK; } HRESULT CGaiaCredentialProvider::UnAdvise() { + // Kill the updater thread (if any). + token_handle_updater_.reset(); + ClearTransient(); - HRESULT hr = DestroyCredentials(); - LOGFN(INFO) << "hr=" << putHR(hr); + DestroyCredentials(); // Delete the startup sentinel file if any still exists. It can still exist in // 2 cases: @@ -436,16 +654,17 @@ // 1. The UnAdvise should only occur after the user has logged in, so if they // never selected any gaia credential and just used normal credentials this // function will be called in that situation and it is guaranteed that the - // user has at least been able provide some input to winlogon. 2. When no - // usage scenario is supported, none of the credentials will be selected and - // thus the gcpw startup sentinel file will not be deleted. So in the case - // where the user is asked for CPUS_CRED_UI enough times, the sentinel file - // size will keep growing without being deleted and eventually GCPW will be - // disabled completely. In the unsupported usage scenario, FinalRelease will - // be called shortly after SetUsageScenario if the function returns E_NOTIMPL - // so try to catch potential crashes of the destruction of the provider when - // it is not used because crashes in this case will prevent the cred ui from - // coming up and not allow the user to access their desired resource. + // user has at least been able provide some input to winlogon. + // 2. When no usage scenario is supported, none of the credentials will be + // selected and thus the gcpw startup sentinel file will not be deleted. So in + // the case where the user is asked for CPUS_CRED_UI enough times, the + // sentinel file size will keep growing without being deleted and eventually + // GCPW will be disabled completely. In the unsupported usage scenario, + // FinalRelease will be called shortly after SetUsageScenario if the function + // returns E_NOTIMPL so try to catch potential crashes of the destruction of + // the provider when it is not used because crashes in this case will prevent + // the cred ui from coming up and not allow the user to access their desired + // resource. DeleteStartupSentinel(); return S_OK; @@ -489,16 +708,35 @@ DWORD* count, DWORD* default_index, BOOL* autologin_with_default) { + bool needs_to_refresh_users = false; + ComPtrStorage local_auto_logon_credential; + + // Get the mutually exclusive state of the provider so that we can + // determine the correct next step (recreate credentials or auto logon). + concurrent_state_.GetUpdatedState(&needs_to_refresh_users, + &local_auto_logon_credential); + // NOTE: assumes SetUserArray() is called before this. + if (needs_to_refresh_users) + RecreateCredentials(&local_auto_logon_credential); + *count = users_.size(); *default_index = CREDENTIAL_PROVIDER_NO_DEFAULT; *autologin_with_default = false; // If a user was authenticated, winlogon was notified of credentials changes // and is re-enumerating the credentials. Make sure autologin is enabled. - if (index_ < users_.size() && new_user_sid_.Length() > 0) { - *default_index = index_; - *autologin_with_default = true; + if (local_auto_logon_credential.gaia_cred) { + // Find the index of the credential that should contain the authentication + // information. + for (size_t i = 0; + i < users_.size() && *default_index == CREDENTIAL_PROVIDER_NO_DEFAULT; + ++i) { + if (local_auto_logon_credential.gaia_cred.IsEqualObject(users_[i])) + *default_index = i; + } + + *autologin_with_default = *default_index != CREDENTIAL_PROVIDER_NO_DEFAULT; } LOGFN(INFO) << " count=" << *count << " default=" << *default_index
diff --git a/chrome/credential_provider/gaiacp/gaia_credential_provider.h b/chrome/credential_provider/gaiacp/gaia_credential_provider.h index 5f33b2a..2a6db40 100644 --- a/chrome/credential_provider/gaiacp/gaia_credential_provider.h +++ b/chrome/credential_provider/gaiacp/gaia_credential_provider.h
@@ -11,6 +11,7 @@ #include <vector> #include "base/strings/string16.h" +#include "base/synchronization/lock.h" #include "base/synchronization/waitable_event.h" #include "base/win/scoped_handle.h" #include "chrome/credential_provider/gaiacp/gaia_credential.h" @@ -19,6 +20,16 @@ namespace credential_provider { +class BackgroundTokenHandleUpdater; + +// Event handler that can be notified when a user's access has been revoked, +// allowing the credential provider to update the list of available credentials. +class ICredentialUpdateEventsHandler { + public: + virtual ~ICredentialUpdateEventsHandler() = default; + virtual void UpdateCredentialsIfNeeded(bool user_access_changed) = 0; +}; + // Implementation of ICredentialProvider backed by Gaia. class ATL_NO_VTABLE CGaiaCredentialProvider : public CComObjectRootEx<CComMultiThreadModel>, @@ -26,14 +37,15 @@ &CLSID_GaiaCredentialProvider>, public IGaiaCredentialProvider, public ICredentialProviderSetUserArray, - public ICredentialProvider { + public ICredentialProvider, + public ICredentialUpdateEventsHandler { public: // This COM object is registered with the rgs file. The rgs file is used by // CGaiaCredentialProviderModule class, see latter for details. DECLARE_NO_REGISTRY() CGaiaCredentialProvider(); - ~CGaiaCredentialProvider(); + ~CGaiaCredentialProvider() override; BEGIN_COM_MAP(CGaiaCredentialProvider) COM_INTERFACE_ENTRY(IGaiaCredentialProvider) @@ -46,6 +58,9 @@ HRESULT FinalConstruct(); void FinalRelease(); + // ICredentialUpdateEventsHandler: + void UpdateCredentialsIfNeeded(bool user_access_changed) override; + // Returns true if the given usage scenario is supported by GCPW. Currently // only CPUS_LOGON and CPUS_UNLOCK_WORKSTATION are supported. static bool IsUsageScenarioSupported(CREDENTIAL_PROVIDER_USAGE_SCENARIO cpus); @@ -58,6 +73,81 @@ private: HRESULT DestroyCredentials(); + // Struct to allow passing CComPtr by pointer without the implicit conversion + // to ** version of the CComPtr + struct ComPtrStorage { + ComPtrStorage(); + ~ComPtrStorage(); + CComPtr<IGaiaCredential> gaia_cred; + }; + + // Class used to store state information for the provider that may be accessed + // concurrently. This class is thread safe and ensures that the correct state + // can be set / queried at any moment. When modifying the state, the modifying + // function will return true to state that the operation was completed + // successfully to allow the caller to determine if they need to raise a + // credential changed event. + class ProviderConcurrentState { + public: + ProviderConcurrentState(); + ~ProviderConcurrentState(); + + // Checks if a user refresh can be performed on the next call to + // |GetCredentialCount|. Normally a user refresh is only needed if + // |user_access_changed|, but if an auto logon is pending the refresh will + // need to be deferred. In this case a pending refresh is queued and + // requested when possible on the next call to RequestUserRefreshIfNeeded. + // Returns true if a new credential changed event needs to be fired. + bool RequestUserRefreshIfNeeded(bool user_access_changed); + + // Sets the credential used to auto logon credential. This function will + // always set the credential as auto logon has precedence over a user + // refresh. If a user refresh was already requested then it will be changed + // to a pending refresh request. Returns true if a new credential changed + // event needs to be fired. A credential changed event is not always + // required if a previous call to RequestUserRefreshIfNeeded was made that + // requested a credential changed event. + bool SetAutoLogonCredential( + const CComPtr<IGaiaCredential>& auto_logon_credential); + + // Gets the current valid update state of the provider to determnie whether + // an auto logon needs to be done or a refresh of the credentials. The two + // update states are mutually exclusive. Only one of + // |needs_to_refresh_users| and |auto_logon_credential| can be set to a non + // default value. + void GetUpdatedState(bool* needs_to_refresh_users, + ComPtrStorage* auto_logon_credential); + + // Resets the state of the provider to be default one. On the next call to + // GetCredentialCount no auto logon should be performed and no refresh of + // credentials should be done. + void Reset(); + + private: + void InternalReset(); + + // Reference to the credential that authenticated the user. + CComPtr<IGaiaCredential> auto_logon_credential_; + + // Set in NotifyUserAccessDenied to notify the main thread that it will need + // to update credentials on the next call to GetCredentialCount. This + // ensures that |users_| is only accessed on the main thread. This member + // can only be accessed on multiple threads if |token_handle_updater_| is + // instantiated, which only occurs between a call to Advise and UnAdvise. + bool users_need_to_be_refreshed_ = false; + + // Used to queue up user refresh requests that had to be deferred because + // |auto_logon_credential_| has precedence over + // |users_need_to_be_refreshed_|. + bool pending_users_refresh_needed_ = false; + + // Locks access to |users_need_to_be_refreshed_| and + // |auto_logon_credential_| to prevent races between calls to + // OnUserAuthenticated / GetCredentialCount and calls to + // NotifyUserAccessDenied. + base::Lock state_update_lock_; + }; + // Functions to create credentials during the processing of SetUserArray. // Creates necessary anonymous credentials given the state of the sign in @@ -66,15 +156,26 @@ HRESULT CreateAnonymousCredentialIfNeeded(bool showing_other_user); // Creates all the reauth credentials from the users that is returned from - // |users|. Fills |reauth_sids| with the list of user sids for which a reauth - // credential was created. - HRESULT CreateReauthCredentials(ICredentialProviderUserArray* users); + // |users|. Fills the |gaia_cred| in |auto_logon_credential| with a reference + // to the credential that needs to perform auto logon (if any). + HRESULT CreateReauthCredentials(ICredentialProviderUserArray* users, + ComPtrStorage* auto_logon_credential); - // This function will always add |cred| to |users_| and will also try to - // check if the |sid| matches the one set in |set_serialization_sid_| to - // allow auto logon of remote connections. + // This function will always add |cred| to |users_| and will also try to check + // if the |sid| matches the one set in |set_serialization_sid_| to allow auto + // logon of remote connections. Fills the |gaia_cred| in + // |auto_logon_credential| with a reference to the credential that needs to + // perform auto logon (if any). void AddCredentialAndCheckAutoLogon(const CComPtr<IGaiaCredential>& cred, - const base::string16& sid); + const base::string16& sid, + ComPtrStorage* auto_logon_credential); + + // Destroys existing credentials and recreates them based on the contents of + // |user_array_|. This member must be set via a call to SetUserArray before + // RecreateCredentials is called. Fills the |gaia_cred| in + // |auto_logon_credential| with a reference to the credential that needs to + // perform auto logon (if any). + void RecreateCredentials(ComPtrStorage* auto_logon_credential); void ClearTransient(); void CleanupOlderVersions(); @@ -113,18 +214,27 @@ DWORD cpus_flags_ = 0; UINT_PTR advise_context_; CComPtr<ICredentialProviderEvents> events_; + CComPtr<ICredentialProviderUserArray> user_array_; + // List of credentials exposed by this provider. The first is always the // Gaia credential for creating new users. The rest are reauth credentials. std::vector<CComPtr<IGaiaCredential>> users_; - // SID of the user that was authenticated. - CComBSTR new_user_sid_; + // Reference to the credential that authenticated the user. + CComPtr<IGaiaCredential> auto_logon_credential_; - // Index in the |users_| array of the credential that performed the - // authentication. - size_t index_ = std::numeric_limits<size_t>::max(); + // Background thread updater of token handles that is created on startup to + // ensure that user must sign in through gaia if their token handle becomes + // invalid while idle on the sign in screen. The updater will also handle the + // situation where the machine becomes unenrolled from MDM during sign in. + std::unique_ptr<BackgroundTokenHandleUpdater> token_handle_updater_; + // The SID extracted from the serialization information passed into + // SetSerialization. This sid is used to determine which credential to try + // to auto logon when GetCredentialCount is called. base::string16 set_serialization_sid_; + + ProviderConcurrentState concurrent_state_; }; // OBJECT_ENTRY_AUTO() contains an extra semicolon.
diff --git a/chrome/credential_provider/gaiacp/gaia_credential_provider_unittests.cc b/chrome/credential_provider/gaiacp/gaia_credential_provider_unittests.cc index 2532902..d4a92c35 100644 --- a/chrome/credential_provider/gaiacp/gaia_credential_provider_unittests.cc +++ b/chrome/credential_provider/gaiacp/gaia_credential_provider_unittests.cc
@@ -172,6 +172,230 @@ ASSERT_EQ(S_OK, provider->UnAdvise()); } +TEST_F(GcpCredentialProviderTest, AutoLogonAfterUserRefresh) { + USES_CONVERSION; + CComPtr<ICredentialProvider> provider; + ASSERT_EQ(S_OK, + CComCreator<CComObject<CGaiaCredentialProvider>>::CreateInstance( + nullptr, IID_ICredentialProvider, (void**)&provider)); + + CComPtr<IGaiaCredentialProvider> gaia_provider; + ASSERT_EQ(S_OK, provider.QueryInterface(&gaia_provider)); + + CComBSTR sid; + ASSERT_EQ(S_OK, fake_os_user_manager()->CreateTestOSUser( + L"username", L"passowrd", L"Full Name", L"Comment", L"", + L"", &sid)); + // Start process for logon screen. + ASSERT_EQ(S_OK, provider->SetUsageScenario(CPUS_LOGON, 0)); + + // Give empty list of users so that only the anonymous credential is created. + CComPtr<ICredentialProviderSetUserArray> user_array; + ASSERT_EQ(S_OK, provider.QueryInterface(&user_array)); + FakeCredentialProviderUserArray array; + ASSERT_EQ(S_OK, user_array->SetUserArray(&array)); + + // Activate the CP. + FakeCredentialProviderEvents events; + ASSERT_EQ(S_OK, provider->Advise(&events, 0)); + + // Only the anonymous credential should exist. + DWORD count; + DWORD default_index; + BOOL autologon; + ASSERT_EQ(S_OK, + provider->GetCredentialCount(&count, &default_index, &autologon)); + ASSERT_EQ(1u, count); + EXPECT_EQ(CREDENTIAL_PROVIDER_NO_DEFAULT, default_index); + EXPECT_FALSE(autologon); + + // Get the anonymous credential. + CComPtr<ICredentialProviderCredential> cred; + ASSERT_EQ(S_OK, provider->GetCredentialAt(0, &cred)); + + // Notify that user access is denied to fake a forced recreation of the users. + ICredentialUpdateEventsHandler* update_handler = + static_cast<ICredentialUpdateEventsHandler*>( + static_cast<CGaiaCredentialProvider*>(provider.p)); + update_handler->UpdateCredentialsIfNeeded(true); + + // Credential changed event should have been received. + EXPECT_TRUE(events.CredentialsChangedReceived()); + events.ResetCredentialsChangedReceived(); + + // At the same time notify that a user has authenticated and requires a + // sign in. + { + // Temporary locker to prevent DCHECKs in OnUserAuthenticated + AssociatedUserValidator::ScopedBlockDenyAccessUpdate deny_update_locker( + AssociatedUserValidator::Get()); + ASSERT_EQ(S_OK, gaia_provider->OnUserAuthenticated( + cred, CComBSTR(L"username"), CComBSTR(L"password"), sid, + true)); + } + + // No credential changed should have been signalled here. + EXPECT_FALSE(events.CredentialsChangedReceived()); + + // GetCredentialCount should return back the same credential that was just + // auto logged on. + ASSERT_EQ(S_OK, + provider->GetCredentialCount(&count, &default_index, &autologon)); + ASSERT_EQ(1u, count); + EXPECT_EQ(0u, default_index); + EXPECT_TRUE(autologon); + + CComPtr<ICredentialProviderCredential> auto_logon_cred; + ASSERT_EQ(S_OK, provider->GetCredentialAt(0, &auto_logon_cred)); + EXPECT_TRUE(auto_logon_cred.IsEqualObject(cred)); + + // The next call to GetCredentialCount should return re-created credentials. + + // Fake an update request with no access changes. The pending user refresh + // should be queued. + update_handler->UpdateCredentialsIfNeeded(false); + + // Credential changed event should have been received. + EXPECT_TRUE(events.CredentialsChangedReceived()); + + // GetCredentialCount should return new credentials with no auto logon. + ASSERT_EQ(S_OK, + provider->GetCredentialCount(&count, &default_index, &autologon)); + ASSERT_EQ(1u, count); + EXPECT_EQ(CREDENTIAL_PROVIDER_NO_DEFAULT, default_index); + EXPECT_FALSE(autologon); + + CComPtr<ICredentialProviderCredential> new_cred; + ASSERT_EQ(S_OK, provider->GetCredentialAt(0, &new_cred)); + EXPECT_FALSE(new_cred.IsEqualObject(cred)); + + // Another request to refresh the credentials should yield no credential + // changed event or refresh of credentials. + events.ResetCredentialsChangedReceived(); + + update_handler->UpdateCredentialsIfNeeded(false); + + // No credential changed event should have been received. + EXPECT_FALSE(events.CredentialsChangedReceived()); + + // GetCredentialCount should return the same credentials with no change. + ASSERT_EQ(S_OK, + provider->GetCredentialCount(&count, &default_index, &autologon)); + ASSERT_EQ(1u, count); + EXPECT_EQ(CREDENTIAL_PROVIDER_NO_DEFAULT, default_index); + EXPECT_FALSE(autologon); + + CComPtr<ICredentialProviderCredential> unchanged_cred; + ASSERT_EQ(S_OK, provider->GetCredentialAt(0, &unchanged_cred)); + EXPECT_TRUE(new_cred.IsEqualObject(unchanged_cred)); + + // Deactivate the CP. + ASSERT_EQ(S_OK, provider->UnAdvise()); +} + +TEST_F(GcpCredentialProviderTest, AutoLogonBeforeUserRefresh) { + USES_CONVERSION; + CComPtr<ICredentialProvider> provider; + ASSERT_EQ(S_OK, + CComCreator<CComObject<CGaiaCredentialProvider>>::CreateInstance( + nullptr, IID_ICredentialProvider, (void**)&provider)); + + CComPtr<IGaiaCredentialProvider> gaia_provider; + ASSERT_EQ(S_OK, provider.QueryInterface(&gaia_provider)); + + CComBSTR sid; + ASSERT_EQ(S_OK, fake_os_user_manager()->CreateTestOSUser( + L"username", L"passowrd", L"Full Name", L"Comment", L"", + L"", &sid)); + // Start process for logon screen. + ASSERT_EQ(S_OK, provider->SetUsageScenario(CPUS_LOGON, 0)); + + // Give empty list of users so that only the anonymous credential is created. + CComPtr<ICredentialProviderSetUserArray> user_array; + ASSERT_EQ(S_OK, provider.QueryInterface(&user_array)); + FakeCredentialProviderUserArray array; + ASSERT_EQ(S_OK, user_array->SetUserArray(&array)); + + // Activate the CP. + FakeCredentialProviderEvents events; + ASSERT_EQ(S_OK, provider->Advise(&events, 0)); + + // Only the anonymous credential should exist. + DWORD count; + DWORD default_index; + BOOL autologon; + ASSERT_EQ(S_OK, + provider->GetCredentialCount(&count, &default_index, &autologon)); + ASSERT_EQ(1u, count); + EXPECT_EQ(CREDENTIAL_PROVIDER_NO_DEFAULT, default_index); + EXPECT_FALSE(autologon); + + // Get the anonymous credential. + CComPtr<ICredentialProviderCredential> cred; + ASSERT_EQ(S_OK, provider->GetCredentialAt(0, &cred)); + + ICredentialUpdateEventsHandler* update_handler = + static_cast<ICredentialUpdateEventsHandler*>( + static_cast<CGaiaCredentialProvider*>(provider.p)); + + // Notify user auto logon first and then notify user access denied to ensure + // that auto logon always has precedence over user access denied. + { + // Temporary locker to prevent DCHECKs in OnUserAuthenticated + AssociatedUserValidator::ScopedBlockDenyAccessUpdate deny_update_locker( + AssociatedUserValidator::Get()); + ASSERT_EQ(S_OK, gaia_provider->OnUserAuthenticated( + cred, CComBSTR(L"username"), CComBSTR(L"password"), sid, + true)); + } + + // Credential changed event should have been received. + EXPECT_TRUE(events.CredentialsChangedReceived()); + events.ResetCredentialsChangedReceived(); + + // Notify that user access is denied. This should not cause a credential + // changed since an event was already processed. + update_handler->UpdateCredentialsIfNeeded(true); + + // No credential changed should have been signalled here. + EXPECT_FALSE(events.CredentialsChangedReceived()); + + // GetCredentialCount should return back the same credential that was just + // auto logged on. + ASSERT_EQ(S_OK, + provider->GetCredentialCount(&count, &default_index, &autologon)); + ASSERT_EQ(1u, count); + EXPECT_EQ(0u, default_index); + EXPECT_TRUE(autologon); + + CComPtr<ICredentialProviderCredential> auto_logon_cred; + ASSERT_EQ(S_OK, provider->GetCredentialAt(0, &auto_logon_cred)); + EXPECT_TRUE(auto_logon_cred.IsEqualObject(cred)); + + // The next call to GetCredentialCount should return re-created credentials. + + // Fake an update request with no access changes. The pending user refresh + // should be queued. + update_handler->UpdateCredentialsIfNeeded(false); + + // Credential changed event should have been received. + EXPECT_TRUE(events.CredentialsChangedReceived()); + + // GetCredentialCount should return new credentials with no auto logon. + ASSERT_EQ(S_OK, + provider->GetCredentialCount(&count, &default_index, &autologon)); + ASSERT_EQ(1u, count); + EXPECT_EQ(CREDENTIAL_PROVIDER_NO_DEFAULT, default_index); + EXPECT_FALSE(autologon); + + CComPtr<ICredentialProviderCredential> new_cred; + ASSERT_EQ(S_OK, provider->GetCredentialAt(0, &new_cred)); + EXPECT_FALSE(new_cred.IsEqualObject(cred)); + + // Deactivate the CP. + ASSERT_EQ(S_OK, provider->UnAdvise()); +} + TEST_F(GcpCredentialProviderTest, AddPersonAfterUserRemove) { FakeAssociatedUserValidator associated_user_validator; FakeInternetAvailabilityChecker internet_checker;
diff --git a/chrome/credential_provider/gaiacp/os_user_manager.cc b/chrome/credential_provider/gaiacp/os_user_manager.cc index f6045e56..541f039 100644 --- a/chrome/credential_provider/gaiacp/os_user_manager.cc +++ b/chrome/credential_provider/gaiacp/os_user_manager.cc
@@ -8,8 +8,6 @@ #include <lm.h> -#include <Shellapi.h> // For <Shlobj.h> -#include <Shlobj.h> // For SHFileOperation() #include <sddl.h> // For ConvertSidToStringSid() #include <userenv.h> // For GetUserProfileDirectory() #include <wincrypt.h> // For CryptXXX() @@ -24,6 +22,7 @@ #include <memory> #include "base/files/file_path.h" +#include "base/files/file_util.h" #include "base/macros.h" #include "base/scoped_native_library.h" #include "base/stl_util.h" @@ -565,9 +564,6 @@ if (hr != HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND)) LOGFN(ERROR) << "GetUserProfileDirectory hr=" << putHR(hr); profiledir[0] = 0; - } else { - // Double null terminate the profile directory for SHFileOperation(). - profiledir[length] = 0; } } else { LOGFN(ERROR) << "CreateLogonToken hr=" << putHR(hr); @@ -579,17 +575,8 @@ LOGFN(ERROR) << "NetUserDel nsts=" << nsts; // Force delete the user's profile directory. - if (profiledir[0] != 0) { - SHFILEOPSTRUCT op; - memset(&op, 0, sizeof(op)); - op.wFunc = FO_DELETE; - op.pFrom = profiledir; // Double null terminated above. - op.fFlags = FOF_NOCONFIRMATION | FOF_NOERRORUI | FOF_NO_UI | FOF_SILENT; - - int ret = ::SHFileOperation(&op); - if (ret != 0) - LOGFN(ERROR) << "SHFileOperation ret=" << ret; - } + if (*profiledir && !base::DeleteFile(base::FilePath(profiledir), true)) + LOGFN(ERROR) << "base::DeleteFile"; return S_OK; }
diff --git a/chrome/credential_provider/test/com_fakes.h b/chrome/credential_provider/test/com_fakes.h index 4360c0a..c2dd482 100644 --- a/chrome/credential_provider/test/com_fakes.h +++ b/chrome/credential_provider/test/com_fakes.h
@@ -85,6 +85,9 @@ ULONG STDMETHODCALLTYPE Release(void) override; IFACEMETHODIMP CredentialsChanged(UINT_PTR upAdviseContext) override; + bool CredentialsChangedReceived() const { return did_change_; } + void ResetCredentialsChangedReceived() { did_change_ = false; } + private: bool did_change_ = false; };
diff --git a/chrome/credential_provider/test/gcp_fakes.cc b/chrome/credential_provider/test/gcp_fakes.cc index 5f588d72..f26f199 100644 --- a/chrome/credential_provider/test/gcp_fakes.cc +++ b/chrome/credential_provider/test/gcp_fakes.cc
@@ -599,11 +599,6 @@ *GetInstanceStorage() = original_validator_; } -bool FakeAssociatedUserValidator::IsUserAccessBlocked( - const base::string16& sid) const { - return locked_user_sids_.find(sid) != locked_user_sids_.end(); -} - /////////////////////////////////////////////////////////////////////////////// FakeInternetAvailabilityChecker::FakeInternetAvailabilityChecker(
diff --git a/chrome/credential_provider/test/gcp_fakes.h b/chrome/credential_provider/test/gcp_fakes.h index 2068364..d471c05c 100644 --- a/chrome/credential_provider/test/gcp_fakes.h +++ b/chrome/credential_provider/test/gcp_fakes.h
@@ -294,9 +294,7 @@ explicit FakeAssociatedUserValidator(base::TimeDelta validation_timeout); ~FakeAssociatedUserValidator() override; - // Returns whether the user should be locked out of sign in (only used in - // tests). - bool IsUserAccessBlocked(const base::string16& sid) const; + using AssociatedUserValidator::IsUserAccessBlocked; private: AssociatedUserValidator* original_validator_ = nullptr;
diff --git a/chrome/installer/util/BUILD.gn b/chrome/installer/util/BUILD.gn index 6c6c3f5..a3a803e 100644 --- a/chrome/installer/util/BUILD.gn +++ b/chrome/installer/util/BUILD.gn
@@ -210,7 +210,7 @@ ] extractor_datafile = "//chrome/installer/util/prebuild/create_installer_string_rc.py" - + grdfile_folder = "//chrome/app/" if (is_chrome_branded) { grdfile_name = "google_chrome_strings" @@ -218,14 +218,12 @@ grdfile_name = "chromium_strings" } xtb_relative_path = "resources" - grd_files_info = [ - [ - grdfile_folder, - grdfile_name, - xtb_relative_path, - default_embedded_i18_locales - ] - ] + grd_files_info = [ [ + grdfile_folder, + grdfile_name, + xtb_relative_path, + default_embedded_i18_locales, + ] ] output_file_name_base = "installer_util_strings" @@ -274,8 +272,6 @@ "experiment_unittest.cc", "google_update_settings_unittest.cc", "install_util_unittest.cc", - "installer_util_test_common.cc", - "installer_util_test_common.h", "l10n_string_util_unittest.cc", "logging_installer_unittest.cc", "lzma_file_allocator_unittest.cc",
diff --git a/chrome/installer/util/duplicate_tree_detector_unittest.cc b/chrome/installer/util/duplicate_tree_detector_unittest.cc index 7f1c909..bb34fec 100644 --- a/chrome/installer/util/duplicate_tree_detector_unittest.cc +++ b/chrome/installer/util/duplicate_tree_detector_unittest.cc
@@ -11,7 +11,6 @@ #include "base/strings/string16.h" #include "base/strings/string_util.h" #include "chrome/installer/util/duplicate_tree_detector.h" -#include "chrome/installer/util/installer_util_test_common.h" #include "testing/gtest/include/gtest/gtest.h" namespace { @@ -57,7 +56,7 @@ CreateTextFile(f2.MaybeAsASCII(), text_content_2_); ASSERT_TRUE(base::PathExists(f2)); - ASSERT_TRUE(installer::test::CopyFileHierarchy(d1, second_root)); + ASSERT_TRUE(base::CopyDirectory(d1, second_root, true)); } base::ScopedTempDir temp_source_dir_; @@ -148,7 +147,7 @@ // This file should be the same. base::FilePath dest_file(temp_dest_dir_.GetPath()); dest_file = dest_file.AppendASCII("F1"); - ASSERT_TRUE(installer::test::CopyFileHierarchy(source_file, dest_file)); + ASSERT_TRUE(base::CopyFile(source_file, dest_file)); // This file should be different. base::FilePath other_file(temp_dest_dir_.GetPath());
diff --git a/chrome/installer/util/installer_util_test_common.cc b/chrome/installer/util/installer_util_test_common.cc deleted file mode 100644 index 6c15537..0000000 --- a/chrome/installer/util/installer_util_test_common.cc +++ /dev/null
@@ -1,36 +0,0 @@ -// Copyright (c) 2011 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 "chrome/installer/util/installer_util_test_common.h" - -#include <windows.h> -#include <shellapi.h> - -#include "base/files/file_path.h" -#include "base/strings/string16.h" - -namespace installer { - -namespace test { - -bool CopyFileHierarchy(const base::FilePath& from, const base::FilePath& to) { - // In SHFILEOPSTRUCT below, |pFrom| and |pTo| have to be double-null - // terminated: http://msdn.microsoft.com/library/bb759795.aspx - base::string16 double_null_from(from.value()); - double_null_from.push_back(L'\0'); - base::string16 double_null_to(to.value()); - double_null_to.push_back(L'\0'); - - SHFILEOPSTRUCT file_op = {}; - file_op.wFunc = FO_COPY; - file_op.pFrom = double_null_from.c_str(); - file_op.pTo = double_null_to.c_str(); - file_op.fFlags = FOF_NO_UI; - - return (SHFileOperation(&file_op) == 0 && !file_op.fAnyOperationsAborted); -} - -} // namespace test - -} // namespace installer
diff --git a/chrome/installer/util/installer_util_test_common.h b/chrome/installer/util/installer_util_test_common.h deleted file mode 100644 index 0a40968..0000000 --- a/chrome/installer/util/installer_util_test_common.h +++ /dev/null
@@ -1,24 +0,0 @@ -// 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 CHROME_INSTALLER_UTIL_INSTALLER_UTIL_TEST_COMMON_H_ -#define CHROME_INSTALLER_UTIL_INSTALLER_UTIL_TEST_COMMON_H_ - -namespace base { -class FilePath; -} - -namespace installer { - -namespace test { - -// Copies the hierarcy in |from| to |to|. -// Keeps all file properties identical (creation time, etc.). -bool CopyFileHierarchy(const base::FilePath& from, const base::FilePath& to); - -} // namespace test - -} // namespace installer - -#endif // CHROME_INSTALLER_UTIL_INSTALLER_UTIL_TEST_COMMON_H_
diff --git a/chrome/installer/util/move_tree_work_item_unittest.cc b/chrome/installer/util/move_tree_work_item_unittest.cc index f5a5e6c2..952be9f 100644 --- a/chrome/installer/util/move_tree_work_item_unittest.cc +++ b/chrome/installer/util/move_tree_work_item_unittest.cc
@@ -15,7 +15,6 @@ #include "base/stl_util.h" #include "base/strings/string_util.h" #include "base/strings/utf_string_conversions.h" -#include "chrome/installer/util/installer_util_test_common.h" #include "chrome/installer/util/work_item.h" #include "testing/gtest/include/gtest/gtest.h" @@ -388,10 +387,10 @@ CreateTextFile(from_file.value(), kTextContent1); ASSERT_TRUE(base::PathExists(from_file)); - // // Create a file hierarchy identical to the one in the source directory. + // Create a file hierarchy identical to the one in the source directory. base::FilePath to_dir(temp_from_dir_.GetPath()); to_dir = to_dir.AppendASCII("To_Dir"); - ASSERT_TRUE(installer::test::CopyFileHierarchy(from_dir1, to_dir)); + ASSERT_TRUE(base::CopyDirectory(from_dir1, to_dir, true)); // Lock one of the files in the to destination directory to prevent moves. base::FilePath orig_to_file(
diff --git a/chrome/renderer/searchbox/searchbox_extension.cc b/chrome/renderer/searchbox/searchbox_extension.cc index 4da93d2f..4ca1e0a 100644 --- a/chrome/renderer/searchbox/searchbox_extension.cc +++ b/chrome/renderer/searchbox/searchbox_extension.cc
@@ -55,11 +55,16 @@ // Returns an array with the RGBA color components. v8::Local<v8::Value> RGBAColorToArray(v8::Isolate* isolate, const RGBAColor& color) { + v8::Local<v8::Context> context = isolate->GetCurrentContext(); v8::Local<v8::Array> color_array = v8::Array::New(isolate, 4); - color_array->Set(0, v8::Int32::New(isolate, color.r)); - color_array->Set(1, v8::Int32::New(isolate, color.g)); - color_array->Set(2, v8::Int32::New(isolate, color.b)); - color_array->Set(3, v8::Int32::New(isolate, color.a)); + color_array->CreateDataProperty(context, 0, v8::Int32::New(isolate, color.r)) + .Check(); + color_array->CreateDataProperty(context, 1, v8::Int32::New(isolate, color.g)) + .Check(); + color_array->CreateDataProperty(context, 2, v8::Int32::New(isolate, color.b)) + .Check(); + color_array->CreateDataProperty(context, 3, v8::Int32::New(isolate, color.a)) + .Check(); return color_array; } @@ -725,12 +730,17 @@ std::vector<InstantMostVisitedItemIDPair> instant_mv_items; search_box->GetMostVisitedItems(&instant_mv_items); + v8::Local<v8::Context> context = isolate->GetCurrentContext(); v8::Local<v8::Object> v8_mv_items = v8::Array::New(isolate, instant_mv_items.size()); for (size_t i = 0; i < instant_mv_items.size(); ++i) { InstantRestrictedID rid = instant_mv_items[i].first; - v8_mv_items->Set(i, GenerateMostVisitedItem(isolate, device_pixel_ratio, - render_view_id, rid)); + v8_mv_items + ->CreateDataProperty( + context, i, + GenerateMostVisitedItem(isolate, device_pixel_ratio, render_view_id, + rid)) + .Check(); } return v8_mv_items; }
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn index 81cdc9ea..4cb4e01 100644 --- a/chrome/test/BUILD.gn +++ b/chrome/test/BUILD.gn
@@ -1062,7 +1062,7 @@ "../browser/ui/webui/prefs_internals_browsertest.cc", "../browser/ui/webui/profile_helper_browsertest.cc", "../browser/ui/webui/set_as_default_browser_ui_browsertest_win.cc", - "../browser/ui/webui/settings/md_settings_ui_browsertest.cc", + "../browser/ui/webui/settings/settings_ui_browsertest.cc", "../browser/ui/webui/signin/user_manager_ui_browsertest.cc", "../browser/ui/webui/webui_browsertest.cc", "../browser/ui/webui/webui_webview_browsertest.cc", @@ -1859,11 +1859,13 @@ "../browser/chromeos/login/oobe_browsertest.cc", "../browser/chromeos/login/oobe_interactive_ui_test.cc", "../browser/chromeos/login/oobe_localization_browsertest.cc", + "../browser/chromeos/login/password_change_browsertest.cc", "../browser/chromeos/login/proxy_auth_dialog_browsertest.cc", "../browser/chromeos/login/quick_unlock/pin_migration_browsertest.cc", "../browser/chromeos/login/reset_browsertest.cc", "../browser/chromeos/login/saml/saml_browsertest.cc", "../browser/chromeos/login/screens/app_downloading_screen_browsertest.cc", + "../browser/chromeos/login/screens/fingerprint_setup_browsertest.cc", "../browser/chromeos/login/screens/hid_detection_screen_browsertest.cc", "../browser/chromeos/login/screens/mock_arc_terms_of_service_screen.cc", "../browser/chromeos/login/screens/mock_arc_terms_of_service_screen.h", @@ -1885,6 +1887,8 @@ "../browser/chromeos/login/session_login_browsertest.cc", "../browser/chromeos/login/signin/device_id_browsertest.cc", "../browser/chromeos/login/signin/oauth2_browsertest.cc", + "../browser/chromeos/login/test/active_directory_login_mixin.cc", + "../browser/chromeos/login/test/active_directory_login_mixin.h", "../browser/chromeos/login/test/enrollment_helper_mixin.cc", "../browser/chromeos/login/test/enrollment_helper_mixin.h", "../browser/chromeos/login/test/enrollment_ui_mixin.cc", @@ -1897,6 +1901,8 @@ "../browser/chromeos/login/test/https_forwarder.h", "../browser/chromeos/login/test/local_policy_test_server_mixin.cc", "../browser/chromeos/login/test/local_policy_test_server_mixin.h", + "../browser/chromeos/login/test/login_manager_mixin.cc", + "../browser/chromeos/login/test/login_manager_mixin.h", "../browser/chromeos/login/test/login_screen_tester.cc", "../browser/chromeos/login/test/login_screen_tester.h", "../browser/chromeos/login/test/network_portal_detector_mixin.cc", @@ -1930,7 +1936,6 @@ "../browser/chromeos/policy/device_policy_cros_browser_test.cc", "../browser/chromeos/policy/device_policy_cros_browser_test.h", "../browser/chromeos/policy/device_quirks_policy_browsertest.cc", - "../browser/chromeos/policy/device_status_collector_browsertest.cc", "../browser/chromeos/policy/device_system_use_24hour_clock_browsertest.cc", "../browser/chromeos/policy/display_resolution_handler_browsertest.cc", "../browser/chromeos/policy/display_rotation_default_handler_browsertest.cc", @@ -1942,6 +1947,7 @@ "../browser/chromeos/policy/restore_on_startup_browsertest_chromeos.cc", "../browser/chromeos/policy/signin_profile_apps_policy_browsertest.cc", "../browser/chromeos/policy/site_isolation_flag_handling_browsertest.cc", + "../browser/chromeos/policy/status_collector/device_status_collector_browsertest.cc", "../browser/chromeos/policy/unaffiliated_arc_allowed_browsertest.cc", "../browser/chromeos/policy/user_affiliation_browsertest.cc", "../browser/chromeos/policy/user_cloud_external_data_manager_browsertest.cc", @@ -2773,6 +2779,7 @@ "../browser/password_manager/password_store_mac_unittest.cc", "../browser/password_manager/password_store_x_unittest.cc", "../browser/payments/payment_handler_permission_context_unittest.cc", + "../browser/performance_manager/decorators/frozen_frame_aggregator_unittest.cc", "../browser/performance_manager/decorators/page_almost_idle_decorator_unittest.cc", "../browser/performance_manager/graph/frame_node_impl_unittest.cc", "../browser/performance_manager/graph/graph_test_harness.cc",
diff --git a/chrome/test/data/extensions/api_test/content_scripts/about_blank_iframes/end.js b/chrome/test/data/extensions/api_test/content_scripts/about_blank_iframes/end.js new file mode 100644 index 0000000..acef814 --- /dev/null +++ b/chrome/test/data/extensions/api_test/content_scripts/about_blank_iframes/end.js
@@ -0,0 +1,10 @@ +// Copyright (c) 2014 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. + +if (typeof hasRunContentScriptAtDocumentStart == 'undefined') { + chrome.extension.sendRequest('document_start script has not run!'); +} else if (window.parent !== window) { + // Assume iframe + chrome.extension.sendRequest('jsresult/' + document.body.textContent.trim()); +}
diff --git a/chrome/test/data/extensions/api_test/content_scripts/about_blank_iframes/manifest.json b/chrome/test/data/extensions/api_test/content_scripts/about_blank_iframes/manifest.json index 4bbfe99..5f33dcd9 100644 --- a/chrome/test/data/extensions/api_test/content_scripts/about_blank_iframes/manifest.json +++ b/chrome/test/data/extensions/api_test/content_scripts/about_blank_iframes/manifest.json
@@ -11,6 +11,7 @@ { "run_at": "document_end", "matches": ["http://*/*"], + "exclude_matches": ["http://*/*test_file_with_javascript_url*"], "js": ["content_script.js"], "all_frames": true }, @@ -20,6 +21,20 @@ "match_about_blank": true, "js": ["content_script2.js"], "all_frames": true + }, + { + "run_at": "document_start", + "matches": ["http://*/*test_file_with_javascript_url*"], + "match_about_blank": true, + "js": ["start.js"], + "all_frames": true + }, + { + "run_at": "document_end", + "matches": ["http://*/*test_file_with_javascript_url*"], + "match_about_blank": true, + "js": ["end.js"], + "all_frames": true } ] }
diff --git a/chrome/test/data/extensions/api_test/content_scripts/about_blank_iframes/start.js b/chrome/test/data/extensions/api_test/content_scripts/about_blank_iframes/start.js new file mode 100644 index 0000000..d15c38a9 --- /dev/null +++ b/chrome/test/data/extensions/api_test/content_scripts/about_blank_iframes/start.js
@@ -0,0 +1,6 @@ +// Copyright (c) 2014 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. + +// This variable will be checked in end.js +var hasRunContentScriptAtDocumentStart = true;
diff --git a/chrome/test/data/extensions/api_test/content_scripts/about_blank_iframes/test.js b/chrome/test/data/extensions/api_test/content_scripts/about_blank_iframes/test.js index 832e4ea..099b0b5 100644 --- a/chrome/test/data/extensions/api_test/content_scripts/about_blank_iframes/test.js +++ b/chrome/test/data/extensions/api_test/content_scripts/about_blank_iframes/test.js
@@ -43,7 +43,13 @@ chrome.tabs.create({ url: test_url }); }, function testDocumentStartRunsInSameWorldAsDocumentEndOfJavaScriptUrl() { - chrome.test.listenOnce(onRequest, checkFirstMessageEquals('parent')); + onRequest.addListener(function listener(request) { + onRequest.removeListener(listener); + // The empty document was replaced with the result of the evaluated + // JavaScript code. + checkFirstMessageEquals('jsresult/something')(request); + chrome.test.succeed(); + }); chrome.test.log('Creating tab...'); var test_url = ('http://localhost:PORT/extensions/' +
diff --git a/chrome/test/data/webui/extensions/activity_log_history_test.js b/chrome/test/data/webui/extensions/activity_log_history_test.js index 51746a38..5a06f5c 100644 --- a/chrome/test/data/webui/extensions/activity_log_history_test.js +++ b/chrome/test/data/webui/extensions/activity_log_history_test.js
@@ -329,7 +329,7 @@ expectEquals(activityLogItems.length, 3); proxyDelegate.resetResolver('getExtensionActivityLog'); - activityLogItems[0].$$('#activity-delete-button').click(); + activityLogItems[0].$$('#activity-delete').click(); // We delete the first item so we should only have one item left. This // chaining reflects the API calls made from activity_log.js.
diff --git a/chrome/test/data/webui/extensions/error_page_test.js b/chrome/test/data/webui/extensions/error_page_test.js index 7f1eaef..0f53afc 100644 --- a/chrome/test/data/webui/extensions/error_page_test.js +++ b/chrome/test/data/webui/extensions/error_page_test.js
@@ -118,7 +118,7 @@ expectTrue(error.querySelector('iron-icon').icon == 'cr:warning'); mockDelegate.testClickingCalls( - error.querySelector('.icon-delete-gray button'), 'deleteErrors', + error.querySelector('.icon-delete-gray'), 'deleteErrors', [extensionId, [manifestError.id]]); });
diff --git a/chrome/test/data/webui/extensions/kiosk_mode_test.js b/chrome/test/data/webui/extensions/kiosk_mode_test.js index 24cefac..b7a634cc5 100644 --- a/chrome/test/data/webui/extensions/kiosk_mode_test.js +++ b/chrome/test/data/webui/extensions/kiosk_mode_test.js
@@ -106,8 +106,7 @@ // disabled. expectTrue(dialog.$$('cr-checkbox').hidden); - MockInteractions.tap( - items[0].querySelector('.icon-delete-gray button')); + MockInteractions.tap(items[0].querySelector('.icon-delete-gray')); Polymer.dom.flush(); return browserProxy.whenCalled('removeKioskApp'); })
diff --git a/chrome/test/data/webui/polymer_browser_test_base.js b/chrome/test/data/webui/polymer_browser_test_base.js index 95b2baa..9569f2df 100644 --- a/chrome/test/data/webui/polymer_browser_test_base.js +++ b/chrome/test/data/webui/polymer_browser_test_base.js
@@ -86,13 +86,6 @@ throw e; } }; - - // Import Polymer before running tests. - suiteSetup(function() { - if (!window.Polymer) { - return PolymerTest.importHtml('chrome://resources/html/polymer.html'); - } - }); }, /** @override */
diff --git a/chromeos/dbus/cups_proxy/BUILD.gn b/chromeos/dbus/cups_proxy/BUILD.gn new file mode 100644 index 0000000..2561595 --- /dev/null +++ b/chromeos/dbus/cups_proxy/BUILD.gn
@@ -0,0 +1,21 @@ +# Copyright 2019 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. + +assert(is_chromeos, "Non-ChromeOS builds cannot depend on //chromeos") + +component("cups_proxy") { + defines = [ "IS_CUPS_PROXY_IMPL" ] + + deps = [ + "//base", + "//dbus", + ] + + sources = [ + "cups_proxy_client.cc", + "cups_proxy_client.h", + "fake_cups_proxy_client.cc", + "fake_cups_proxy_client.h", + ] +}
diff --git a/chromeos/dbus/cups_proxy/OWNERS b/chromeos/dbus/cups_proxy/OWNERS new file mode 100644 index 0000000..5a872c7 --- /dev/null +++ b/chromeos/dbus/cups_proxy/OWNERS
@@ -0,0 +1,2 @@ +luum@chromium.org +skau@chromium.org
diff --git a/chromeos/dbus/cups_proxy/cups_proxy_client.cc b/chromeos/dbus/cups_proxy/cups_proxy_client.cc new file mode 100644 index 0000000..7287262 --- /dev/null +++ b/chromeos/dbus/cups_proxy/cups_proxy_client.cc
@@ -0,0 +1,101 @@ +// Copyright 2019 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 "chromeos/dbus/cups_proxy/cups_proxy_client.h" + +#include <memory> +#include <utility> + +#include "base/bind.h" +#include "base/macros.h" +#include "base/memory/weak_ptr.h" +#include "chromeos/dbus/cups_proxy/fake_cups_proxy_client.h" +#include "dbus/bus.h" +#include "dbus/message.h" +#include "dbus/object_proxy.h" +#include "third_party/cros_system_api/dbus/service_constants.h" + +namespace chromeos { + +namespace { + +CupsProxyClient* g_instance = nullptr; + +class CupsProxyClientImpl : public CupsProxyClient { + public: + CupsProxyClientImpl() = default; + ~CupsProxyClientImpl() override = default; + + // CupsProxyClient override. + void BootstrapMojoConnection( + base::ScopedFD fd, + base::OnceCallback<void(bool success)> result_callback) override { + dbus::MethodCall method_call(::printing::kCupsProxyDaemonInterface, + ::printing::kBootstrapMojoConnectionMethod); + dbus::MessageWriter writer(&method_call); + writer.AppendFileDescriptor(fd.get()); + daemon_proxy_->CallMethod( + &method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT, + base::BindOnce(&CupsProxyClientImpl::OnBootstrapMojoConnectionResponse, + weak_ptr_factory_.GetWeakPtr(), + std::move(result_callback))); + } + + void Init(dbus::Bus* const bus) { + daemon_proxy_ = + bus->GetObjectProxy(::printing::kCupsProxyDaemonName, + dbus::ObjectPath(::printing::kCupsProxyDaemonPath)); + } + + private: + dbus::ObjectProxy* daemon_proxy_ = nullptr; + + // Passes the success/failure of |dbus_response| on to |result_callback|. + void OnBootstrapMojoConnectionResponse( + base::OnceCallback<void(bool success)> result_callback, + dbus::Response* const dbus_response) { + const bool success = dbus_response != nullptr; + std::move(result_callback).Run(success); + } + + // Must be last class member. + base::WeakPtrFactory<CupsProxyClientImpl> weak_ptr_factory_{this}; + DISALLOW_COPY_AND_ASSIGN(CupsProxyClientImpl); +}; + +} // namespace + +CupsProxyClient::CupsProxyClient() { + DCHECK(!g_instance); + g_instance = this; +} + +CupsProxyClient::~CupsProxyClient() { + DCHECK_EQ(this, g_instance); + g_instance = nullptr; +} + +// static +void CupsProxyClient::Initialize(dbus::Bus* bus) { + DCHECK(bus); + (new CupsProxyClientImpl())->Init(bus); +} + +// static +void CupsProxyClient::InitializeFake() { + new FakeCupsProxyClient(); +} + +// static +void CupsProxyClient::Shutdown() { + DCHECK(g_instance); + delete g_instance; +} + +// static +CupsProxyClient* CupsProxyClient::Get() { + return g_instance; +} + +} // namespace chromeos
diff --git a/chromeos/dbus/cups_proxy/cups_proxy_client.h b/chromeos/dbus/cups_proxy/cups_proxy_client.h new file mode 100644 index 0000000..85115897 --- /dev/null +++ b/chromeos/dbus/cups_proxy/cups_proxy_client.h
@@ -0,0 +1,60 @@ +// Copyright 2019 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 CHROMEOS_DBUS_CUPS_PROXY_CUPS_PROXY_CLIENT_H_ +#define CHROMEOS_DBUS_CUPS_PROXY_CUPS_PROXY_CLIENT_H_ + +#include <memory> + +#include "base/callback_forward.h" +#include "base/component_export.h" +#include "base/files/scoped_file.h" + +namespace dbus { +class Bus; +} // namespace dbus + +namespace chromeos { + +// D-Bus client for the CupsProxyDaemon. +// +// Sole method bootstraps a Mojo connection between the daemon and Chrome, +// allowing Chrome to serve printing requests proxied by the daemon. +class COMPONENT_EXPORT(CUPS_PROXY) CupsProxyClient { + public: + // Creates and initializes the global instance. |bus| must not be null. + static void Initialize(dbus::Bus* bus); + + // Creates and initializes a fake global instance if not already created. + static void InitializeFake(); + + // Destroys the global instance. + static void Shutdown(); + + // Returns the global instance which may be null if not initialized. + static CupsProxyClient* Get(); + + // Passes the file descriptor |fd| over D-Bus to the CupsProxyDaemon. + // * The daemon expects a Mojo invitation in |fd| with an attached Mojo pipe. + // * The daemon will bind the Mojo pipe to an + // chromeos::printing::mojom::CupsProxierPtr. + // * Upon completion of the D-Bus call, |result_callback| will be invoked to + // indicate success or failure. + // * This method will first wait for the CupsProxyService to become available. + virtual void BootstrapMojoConnection( + base::ScopedFD fd, + base::OnceCallback<void(bool success)> result_callback) = 0; + + protected: + // Initialize/Shutdown should be used instead. + CupsProxyClient(); + virtual ~CupsProxyClient(); + + private: + DISALLOW_COPY_AND_ASSIGN(CupsProxyClient); +}; + +} // namespace chromeos + +#endif // CHROMEOS_DBUS_CUPS_PROXY_CUPS_PROXY_CLIENT_H_
diff --git a/chromeos/dbus/cups_proxy/fake_cups_proxy_client.cc b/chromeos/dbus/cups_proxy/fake_cups_proxy_client.cc new file mode 100644 index 0000000..105d82d3 --- /dev/null +++ b/chromeos/dbus/cups_proxy/fake_cups_proxy_client.cc
@@ -0,0 +1,23 @@ +// Copyright 2019 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 "chromeos/dbus/cups_proxy/fake_cups_proxy_client.h" + +#include <utility> + +#include "base/callback.h" + +namespace chromeos { + +FakeCupsProxyClient::FakeCupsProxyClient() = default; +FakeCupsProxyClient::~FakeCupsProxyClient() = default; + +void FakeCupsProxyClient::BootstrapMojoConnection( + base::ScopedFD fd, + base::OnceCallback<void(bool success)> result_callback) { + const bool success = true; + std::move(result_callback).Run(success); +} + +} // namespace chromeos
diff --git a/chromeos/dbus/cups_proxy/fake_cups_proxy_client.h b/chromeos/dbus/cups_proxy/fake_cups_proxy_client.h new file mode 100644 index 0000000..dbb5589 --- /dev/null +++ b/chromeos/dbus/cups_proxy/fake_cups_proxy_client.h
@@ -0,0 +1,32 @@ +// Copyright 2019 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 CHROMEOS_DBUS_CUPS_PROXY_FAKE_CUPS_PROXY_CLIENT_H_ +#define CHROMEOS_DBUS_CUPS_PROXY_FAKE_CUPS_PROXY_CLIENT_H_ + +#include "base/callback_forward.h" +#include "base/files/scoped_file.h" +#include "base/macros.h" +#include "chromeos/dbus/cups_proxy/cups_proxy_client.h" + +namespace chromeos { + +// Fake implementation of CupsProxyClient. This is currently a no-op fake. +class FakeCupsProxyClient : public CupsProxyClient { + public: + FakeCupsProxyClient(); + ~FakeCupsProxyClient() override; + + // CupsProxyClient override. + void BootstrapMojoConnection( + base::ScopedFD fd, + base::OnceCallback<void(bool success)> result_callback) override; + + private: + DISALLOW_COPY_AND_ASSIGN(FakeCupsProxyClient); +}; + +} // namespace chromeos + +#endif // CHROMEOS_DBUS_CUPS_PROXY_FAKE_CUPS_PROXY_CLIENT_H_
diff --git a/chromeos/login/auth/BUILD.gn b/chromeos/login/auth/BUILD.gn index b02ac08c..cf994f3 100644 --- a/chromeos/login/auth/BUILD.gn +++ b/chromeos/login/auth/BUILD.gn
@@ -59,6 +59,8 @@ "login_performer.h", "stub_authenticator.cc", "stub_authenticator.h", + "stub_authenticator_builder.cc", + "stub_authenticator_builder.h", "test_attempt_state.cc", "test_attempt_state.h", "user_context.cc",
diff --git a/chromeos/login/auth/stub_authenticator.cc b/chromeos/login/auth/stub_authenticator.cc index 338b3cc..cd94f9f 100644 --- a/chromeos/login/auth/stub_authenticator.cc +++ b/chromeos/login/auth/stub_authenticator.cc
@@ -40,8 +40,17 @@ // during non-online re-auth |user_context| does not have a gaia id. if (expected_user_context_.GetAccountId() == user_context.GetAccountId() && *expected_user_context_.GetKey() == *user_context.GetKey()) { - task_runner_->PostTask( - FROM_HERE, base::BindOnce(&StubAuthenticator::OnAuthSuccess, this)); + switch (auth_action_) { + case AuthAction::kAuthSuccess: + task_runner_->PostTask( + FROM_HERE, base::BindOnce(&StubAuthenticator::OnAuthSuccess, this)); + break; + case AuthAction::kPasswordChange: + task_runner_->PostTask( + FROM_HERE, + base::BindOnce(&StubAuthenticator::OnPasswordChangeDetected, this)); + break; + } return; } GoogleServiceAuthError error = @@ -111,6 +120,7 @@ expected_user_context_.GetAccountId().GetUserEmail() + kUserIdHashSuffix); user_context.GetKey()->Transform(Key::KEY_TYPE_SALTED_SHA256_TOP_HALF, "some-salt"); + consumer_->OnAuthSuccess(user_context); } @@ -119,9 +129,26 @@ } void StubAuthenticator::RecoverEncryptedData(const std::string& old_password) { + if (old_password_ != old_password) { + if (data_recovery_notifier_) + data_recovery_notifier_.Run(DataRecoveryStatus::kRecoveryFailed); + task_runner_->PostTask( + FROM_HERE, + base::BindOnce(&StubAuthenticator::OnPasswordChangeDetected, this)); + return; + } + + if (data_recovery_notifier_) + data_recovery_notifier_.Run(DataRecoveryStatus::kRecovered); + task_runner_->PostTask( + FROM_HERE, base::BindOnce(&StubAuthenticator::OnAuthSuccess, this)); } void StubAuthenticator::ResyncEncryptedData() { + if (data_recovery_notifier_) + data_recovery_notifier_.Run(DataRecoveryStatus::kResynced); + task_runner_->PostTask( + FROM_HERE, base::BindOnce(&StubAuthenticator::OnAuthSuccess, this)); } void StubAuthenticator::SetExpectedCredentials( @@ -131,4 +158,8 @@ StubAuthenticator::~StubAuthenticator() = default; +void StubAuthenticator::OnPasswordChangeDetected() { + consumer_->OnPasswordChangeDetected(); +} + } // namespace chromeos
diff --git a/chromeos/login/auth/stub_authenticator.h b/chromeos/login/auth/stub_authenticator.h index 7c4e48ed2..08bc0c5c 100644 --- a/chromeos/login/auth/stub_authenticator.h +++ b/chromeos/login/auth/stub_authenticator.h
@@ -7,6 +7,7 @@ #include <string> +#include "base/callback.h" #include "base/component_export.h" #include "base/macros.h" #include "base/single_thread_task_runner.h" @@ -22,10 +23,20 @@ namespace chromeos { class AuthStatusConsumer; +class StubAuthenticatorBuilder; class COMPONENT_EXPORT(CHROMEOS_LOGIN_AUTH) StubAuthenticator : public Authenticator { public: + enum class DataRecoveryStatus { + kNone, + kRecovered, + kRecoveryFailed, + kResynced + }; + using DataRecoveryNotifier = + base::RepeatingCallback<void(DataRecoveryStatus status)>; + StubAuthenticator(AuthStatusConsumer* consumer, const UserContext& expected_user_context); @@ -46,15 +57,31 @@ void RecoverEncryptedData(const std::string& old_password) override; void ResyncEncryptedData() override; - virtual void SetExpectedCredentials(const UserContext& user_context); + void SetExpectedCredentials(const UserContext& user_context); protected: ~StubAuthenticator() override; private: + friend class StubAuthenticatorBuilder; + + enum class AuthAction { kAuthSuccess, kPasswordChange }; + + void OnPasswordChangeDetected(); + UserContext expected_user_context_; scoped_refptr<base::SingleThreadTaskRunner> task_runner_; + // The action taken for login requests that match the expected context. + AuthAction auth_action_ = AuthAction::kAuthSuccess; + + // For password change requests - the old user password. + std::string old_password_; + + // If set, the callback that will be called as authenticator handles user + // encrypted data recovery during password change flow. + DataRecoveryNotifier data_recovery_notifier_; + DISALLOW_COPY_AND_ASSIGN(StubAuthenticator); };
diff --git a/chromeos/login/auth/stub_authenticator_builder.cc b/chromeos/login/auth/stub_authenticator_builder.cc new file mode 100644 index 0000000..287beed --- /dev/null +++ b/chromeos/login/auth/stub_authenticator_builder.cc
@@ -0,0 +1,35 @@ +// Copyright 2019 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 "chromeos/login/auth/stub_authenticator_builder.h" + +namespace chromeos { + +StubAuthenticatorBuilder::StubAuthenticatorBuilder( + const UserContext& expected_user_context) + : expected_user_context_(expected_user_context) {} + +StubAuthenticatorBuilder::~StubAuthenticatorBuilder() = default; + +scoped_refptr<Authenticator> StubAuthenticatorBuilder::Create( + AuthStatusConsumer* consumer) { + scoped_refptr<StubAuthenticator> authenticator = + new StubAuthenticator(consumer, expected_user_context_); + authenticator->auth_action_ = auth_action_; + if (auth_action_ == StubAuthenticator::AuthAction::kPasswordChange) + authenticator->old_password_ = old_password_; + if (data_recovery_notifier_) + authenticator->data_recovery_notifier_ = data_recovery_notifier_; + return authenticator; +} + +void StubAuthenticatorBuilder::SetUpPasswordChange( + const std::string& old_password, + const StubAuthenticator::DataRecoveryNotifier& notifier) { + auth_action_ = StubAuthenticator::AuthAction::kPasswordChange; + old_password_ = old_password; + data_recovery_notifier_ = notifier; +} + +} // namespace chromeos
diff --git a/chromeos/login/auth/stub_authenticator_builder.h b/chromeos/login/auth/stub_authenticator_builder.h new file mode 100644 index 0000000..198fe6f --- /dev/null +++ b/chromeos/login/auth/stub_authenticator_builder.h
@@ -0,0 +1,56 @@ +// Copyright 2019 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 CHROMEOS_LOGIN_AUTH_STUB_AUTHENTICATOR_BUILDER_H_ +#define CHROMEOS_LOGIN_AUTH_STUB_AUTHENTICATOR_BUILDER_H_ + +#include <string> + +#include "base/component_export.h" +#include "base/macros.h" +#include "base/memory/ref_counted.h" +#include "chromeos/login/auth/stub_authenticator.h" +#include "chromeos/login/auth/user_context.h" + +namespace chromeos { + +// Helper class for creating a StubAuthenticator with certain configuration. +// Useful in tests for injecting StubAuthenticators to be used during user +// login. +class COMPONENT_EXPORT(CHROMEOS_LOGIN_AUTH) StubAuthenticatorBuilder { + public: + explicit StubAuthenticatorBuilder(const UserContext& expected_user_context); + ~StubAuthenticatorBuilder(); + + scoped_refptr<Authenticator> Create(AuthStatusConsumer* consumer); + + // Sets up the stub Authenticator to report password changed. + // |old_password| - the expected old user password. The authenticator will use + // it to handle data migration requests (it will report failure if the + // provided old password does not match this one). + // |notifier| - can be empty. If set called when a user data recovery is + // attempted. + void SetUpPasswordChange( + const std::string& old_password, + const StubAuthenticator::DataRecoveryNotifier& notifier); + + private: + const UserContext expected_user_context_; + + // Action to be performed on successful auth against expected user context.. + StubAuthenticator::AuthAction auth_action_ = + StubAuthenticator::AuthAction::kAuthSuccess; + + // For kPasswordChange action, the old user password. + std::string old_password_; + // For kPasswordChange action, the callback to be called to report user data + // recovery result. + StubAuthenticator::DataRecoveryNotifier data_recovery_notifier_; + + DISALLOW_COPY_AND_ASSIGN(StubAuthenticatorBuilder); +}; + +} // namespace chromeos + +#endif // CHROMEOS_LOGIN_AUTH_STUB_AUTHENTICATOR_BUILDER_H_
diff --git a/chromeos/services/assistant/assistant_settings_manager_impl.cc b/chromeos/services/assistant/assistant_settings_manager_impl.cc index 41d3ec41..555e1fe8 100644 --- a/chromeos/services/assistant/assistant_settings_manager_impl.cc +++ b/chromeos/services/assistant/assistant_settings_manager_impl.cc
@@ -167,8 +167,25 @@ void AssistantSettingsManagerImpl::SyncSpeakerIdEnrollmentStatus() { DCHECK(service_->main_task_runner()->RunsTasksInCurrentSequence()); - // Disable status check on M74 since we do not have the API available. - return; + if (service_->assistant_state()->allowed_state() != + ash::mojom::AssistantAllowedState::ALLOWED || + !base::FeatureList::IsEnabled( + assistant::features::kAssistantVoiceMatch)) { + return; + } + + assistant_manager_service_->assistant_manager_internal() + ->GetSpeakerIdEnrollmentStatus( + kUserID, + [weak_ptr = weak_factory_.GetWeakPtr(), + task_runner = service_->main_task_runner()]( + const assistant_client::SpeakerIdEnrollmentStatus& status) { + task_runner->PostTask( + FROM_HERE, + base::BindOnce(&AssistantSettingsManagerImpl:: + HandleSpeakerIdEnrollmentStatusSync, + weak_ptr, status)); + }); } void AssistantSettingsManagerImpl::HandleSpeakerIdEnrollmentUpdate( @@ -200,6 +217,24 @@ } } +void AssistantSettingsManagerImpl::HandleSpeakerIdEnrollmentStatusSync( + const assistant_client::SpeakerIdEnrollmentStatus& status) { + DCHECK(service_->main_task_runner()->RunsTasksInCurrentSequence()); + + speaker_id_enrollment_done_ = status.user_model_exists; + + if (speaker_id_enrollment_done_) { + assistant_manager_service_->UpdateInternalOptions( + assistant_manager_service_->assistant_manager_internal()); + + } else { + // If hotword is enabled but there is no voice model found, launch the + // enrollment flow. + if (service_->assistant_state()->hotword_enabled().value()) + service_->assistant_controller()->StartSpeakerIdEnrollmentFlow(); + } +} + void AssistantSettingsManagerImpl::HandleStopSpeakerIdEnrollment( base::RepeatingCallback<void()> callback) { DCHECK(service_->main_task_runner()->RunsTasksInCurrentSequence());
diff --git a/chromeos/services/assistant/assistant_settings_manager_impl.h b/chromeos/services/assistant/assistant_settings_manager_impl.h index 9f8edba9..0f30b604 100644 --- a/chromeos/services/assistant/assistant_settings_manager_impl.h +++ b/chromeos/services/assistant/assistant_settings_manager_impl.h
@@ -14,6 +14,7 @@ #include "mojo/public/cpp/bindings/interface_ptr_set.h" namespace assistant_client { +struct SpeakerIdEnrollmentStatus; struct SpeakerIdEnrollmentUpdate; } // namespace assistant_client @@ -53,6 +54,8 @@ void HandleSpeakerIdEnrollmentUpdate( const assistant_client::SpeakerIdEnrollmentUpdate& update); void HandleStopSpeakerIdEnrollment(base::RepeatingCallback<void()> callback); + void HandleSpeakerIdEnrollmentStatusSync( + const assistant_client::SpeakerIdEnrollmentStatus& status); Service* const service_; AssistantManagerServiceImpl* const assistant_manager_service_;
diff --git a/components/autofill/core/browser/payments/autofill_wallet_model_type_controller.cc b/components/autofill/core/browser/payments/autofill_wallet_model_type_controller.cc index 87033ee..c415ccf 100644 --- a/components/autofill/core/browser/payments/autofill_wallet_model_type_controller.cc +++ b/components/autofill/core/browser/payments/autofill_wallet_model_type_controller.cc
@@ -10,7 +10,9 @@ #include "base/bind_helpers.h" #include "components/autofill/core/common/autofill_prefs.h" #include "components/prefs/pref_service.h" +#include "components/sync/driver/sync_auth_util.h" #include "components/sync/driver/sync_service.h" +#include "google_apis/gaia/google_service_auth_error.h" namespace browser_sync { @@ -24,8 +26,10 @@ sync_service_(sync_service) { DCHECK(type == syncer::AUTOFILL_WALLET_DATA || type == syncer::AUTOFILL_WALLET_METADATA); - currently_enabled_ = IsEnabled(); SubscribeToPrefChanges(); + // TODO(crbug.com/906995): remove this observing mechanism once all sync + // datatypes are stopped by ProfileSyncService, when sync is paused. + sync_service_->AddObserver(this); } AutofillWalletModelTypeController::AutofillWalletModelTypeController( @@ -41,11 +45,15 @@ sync_service_(sync_service) { DCHECK(type == syncer::AUTOFILL_WALLET_DATA || type == syncer::AUTOFILL_WALLET_METADATA); - currently_enabled_ = IsEnabled(); SubscribeToPrefChanges(); + // TODO(crbug.com/906995): remove this observing mechanism once all sync + // datatypes are stopped by ProfileSyncService, when sync is paused. + sync_service_->AddObserver(this); } -AutofillWalletModelTypeController::~AutofillWalletModelTypeController() {} +AutofillWalletModelTypeController::~AutofillWalletModelTypeController() { + sync_service_->RemoveObserver(this); +} void AutofillWalletModelTypeController::Stop( syncer::ShutdownReason shutdown_reason, @@ -66,30 +74,18 @@ bool AutofillWalletModelTypeController::ReadyForStart() const { DCHECK(CalledOnValidThread()); - return currently_enabled_; + return pref_service_->GetBoolean( + autofill::prefs::kAutofillWalletImportEnabled) && + pref_service_->GetBoolean( + autofill::prefs::kAutofillCreditCardEnabled) && + !syncer::IsWebSignout(sync_service_->GetAuthError()); } void AutofillWalletModelTypeController::OnUserPrefChanged() { DCHECK(CalledOnValidThread()); - - bool newly_enabled = IsEnabled(); - if (currently_enabled_ == newly_enabled) { - return; // No change to sync state. - } - - currently_enabled_ = newly_enabled; sync_service_->ReadyForStartChanged(type()); } -bool AutofillWalletModelTypeController::IsEnabled() const { - DCHECK(CalledOnValidThread()); - - // Require two user-visible prefs to be enabled to sync Wallet data/metadata. - return pref_service_->GetBoolean( - autofill::prefs::kAutofillWalletImportEnabled) && - pref_service_->GetBoolean(autofill::prefs::kAutofillCreditCardEnabled); -} - void AutofillWalletModelTypeController::SubscribeToPrefChanges() { pref_registrar_.Init(pref_service_); pref_registrar_.Add( @@ -102,4 +98,10 @@ base::Unretained(this))); } +void AutofillWalletModelTypeController::OnStateChanged( + syncer::SyncService* sync) { + DCHECK(CalledOnValidThread()); + sync_service_->ReadyForStartChanged(type()); +} + } // namespace browser_sync
diff --git a/components/autofill/core/browser/payments/autofill_wallet_model_type_controller.h b/components/autofill/core/browser/payments/autofill_wallet_model_type_controller.h index ecfd8ec..cee6054e 100644 --- a/components/autofill/core/browser/payments/autofill_wallet_model_type_controller.h +++ b/components/autofill/core/browser/payments/autofill_wallet_model_type_controller.h
@@ -11,6 +11,7 @@ #include "base/macros.h" #include "components/prefs/pref_change_registrar.h" #include "components/sync/driver/model_type_controller.h" +#include "components/sync/driver/sync_service_observer.h" class PrefService; @@ -21,7 +22,8 @@ namespace browser_sync { // Controls syncing of AUTOFILL_WALLET_DATA and AUTOFILL_WALLET_METADATA. -class AutofillWalletModelTypeController : public syncer::ModelTypeController { +class AutofillWalletModelTypeController : public syncer::ModelTypeController, + public syncer::SyncServiceObserver { public: // The delegates and |sync_client| must not be null. Furthermore, // |sync_client| must outlive this object. @@ -43,6 +45,9 @@ StopCallback callback) override; bool ReadyForStart() const override; + // syncer::SyncServiceObserver implementation. + void OnStateChanged(syncer::SyncService* sync) override; + private: // Callback for changes to the autofill pref. void OnUserPrefChanged(); @@ -55,8 +60,6 @@ PrefChangeRegistrar pref_registrar_; - bool currently_enabled_; - DISALLOW_COPY_AND_ASSIGN(AutofillWalletModelTypeController); };
diff --git a/components/data_reduction_proxy/core/common/BUILD.gn b/components/data_reduction_proxy/core/common/BUILD.gn index 8923dfb..6c121fa6 100644 --- a/components/data_reduction_proxy/core/common/BUILD.gn +++ b/components/data_reduction_proxy/core/common/BUILD.gn
@@ -10,7 +10,6 @@ template("common_tmpl") { static_library(target_name) { sources = [ - "data_reduction_proxy_bypass_action_list.h", "data_reduction_proxy_bypass_protocol.cc", "data_reduction_proxy_bypass_protocol.h", "data_reduction_proxy_bypass_type_list.h",
diff --git a/components/data_reduction_proxy/core/common/data_reduction_proxy_bypass_action_list.h b/components/data_reduction_proxy/core/common/data_reduction_proxy_bypass_action_list.h deleted file mode 100644 index cce40d2..0000000 --- a/components/data_reduction_proxy/core/common/data_reduction_proxy_bypass_action_list.h +++ /dev/null
@@ -1,30 +0,0 @@ -// Copyright 2015 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. - -// This is the list of data reduction proxy bypass actions and their values. -// These actions are specified in the Chrome-Proxy header. For the enum values, -// include the file -// "components/data_reduction_proxy/core/common/data_reduction_proxy_headers.h". -// -// Here we define the values using a macro BYPASS_ACTION_TYPE, so it can be -// expanded differently in some places (for example, to automatically -// map a bypass type value to its symbolic name). As such, new values must be -// appended and cannot be inserted in the middle as there are instances where -// we will load data between different builds. - -// No action type specified. -BYPASS_ACTION_TYPE(NONE, 0) - -// Attempt to retry the current request while bypassing all Data Reduction -// Proxies; it does not cause other requests to be bypassed. -BYPASS_ACTION_TYPE(BLOCK_ONCE, 1) - -// Bypass all Data Reduction Proxies for a specified period of time. -BYPASS_ACTION_TYPE(BLOCK, 2) - -// Bypass the current Data Reduction Proxy for a specified period of time. -BYPASS_ACTION_TYPE(BYPASS, 3) - -// This must always be last. -BYPASS_ACTION_TYPE(MAX, 4)
diff --git a/components/data_reduction_proxy/core/common/data_reduction_proxy_bypass_protocol.cc b/components/data_reduction_proxy/core/common/data_reduction_proxy_bypass_protocol.cc index 4bdb409..1301e2ba7 100644 --- a/components/data_reduction_proxy/core/common/data_reduction_proxy_bypass_protocol.cc +++ b/components/data_reduction_proxy/core/common/data_reduction_proxy_bypass_protocol.cc
@@ -226,7 +226,6 @@ data_reduction_proxy_info->bypass_all = false; data_reduction_proxy_info->mark_proxies_as_bad = true; data_reduction_proxy_info->bypass_duration = base::TimeDelta::FromMinutes(5); - data_reduction_proxy_info->bypass_action = BYPASS_ACTION_TYPE_BYPASS; *bypass_type = BYPASS_EVENT_TYPE_MEDIUM; return true;
diff --git a/components/data_reduction_proxy/core/common/data_reduction_proxy_headers.cc b/components/data_reduction_proxy/core/common/data_reduction_proxy_headers.cc index 0a9b7ad..505ea5e 100644 --- a/components/data_reduction_proxy/core/common/data_reduction_proxy_headers.cc +++ b/components/data_reduction_proxy/core/common/data_reduction_proxy_headers.cc
@@ -314,7 +314,6 @@ headers, kChromeProxyActionBlock, &proxy_info->bypass_duration)) { proxy_info->bypass_all = true; proxy_info->mark_proxies_as_bad = true; - proxy_info->bypass_action = BYPASS_ACTION_TYPE_BLOCK; return true; } @@ -323,7 +322,6 @@ headers, kChromeProxyActionBypass, &proxy_info->bypass_duration)) { proxy_info->bypass_all = false; proxy_info->mark_proxies_as_bad = true; - proxy_info->bypass_action = BYPASS_ACTION_TYPE_BYPASS; return true; } @@ -336,7 +334,6 @@ proxy_info->bypass_all = true; proxy_info->mark_proxies_as_bad = false; proxy_info->bypass_duration = base::TimeDelta(); - proxy_info->bypass_action = BYPASS_ACTION_TYPE_BLOCK_ONCE; return true; } @@ -390,7 +387,6 @@ data_reduction_proxy_info->bypass_all = true; data_reduction_proxy_info->mark_proxies_as_bad = false; data_reduction_proxy_info->bypass_duration = base::TimeDelta(); - data_reduction_proxy_info->bypass_action = BYPASS_ACTION_TYPE_BLOCK_ONCE; return BYPASS_EVENT_TYPE_URL_REDIRECT_CYCLE; }
diff --git a/components/data_reduction_proxy/core/common/data_reduction_proxy_headers.h b/components/data_reduction_proxy/core/common/data_reduction_proxy_headers.h index 45ace3b..a72b270e 100644 --- a/components/data_reduction_proxy/core/common/data_reduction_proxy_headers.h +++ b/components/data_reduction_proxy/core/common/data_reduction_proxy_headers.h
@@ -49,23 +49,9 @@ #undef BYPASS_EVENT_TYPE }; -// Values for the bypass actions that can be specified by the Data Reduction -// Proxy in response to a client request. These are explicit bypass actions -// specified by the Data Reduction Proxy in the Chrome-Proxy header, block-once, -// bypass=1, block=300, etc. These are not used for Chrome initiated bypasses -// due to a server error, missing Via header, etc. -enum DataReductionProxyBypassAction { -#define BYPASS_ACTION_TYPE(label, value) BYPASS_ACTION_TYPE_##label = value, -#include "components/data_reduction_proxy/core/common/data_reduction_proxy_bypass_action_list.h" -#undef BYPASS_ACTION_TYPE -}; - // Contains instructions contained in the Chrome-Proxy header. struct DataReductionProxyInfo { - DataReductionProxyInfo() - : bypass_all(false), - mark_proxies_as_bad(false), - bypass_action(BYPASS_ACTION_TYPE_NONE) {} + DataReductionProxyInfo() : bypass_all(false), mark_proxies_as_bad(false) {} // True if Chrome should bypass all available data reduction proxies. False // if only the currently connected data reduction proxy should be bypassed. @@ -78,9 +64,6 @@ // Amount of time to bypass the data reduction proxy or proxies. This value is // ignored if |mark_proxies_as_bad| is false. base::TimeDelta bypass_duration; - - // The bypass action specified by the data reduction proxy. - DataReductionProxyBypassAction bypass_action; }; // Gets the header used for data reduction proxy requests and responses.
diff --git a/components/download/internal/common/base_file_win.cc b/components/download/internal/common/base_file_win.cc index 97a8417..cdaf9d4 100644 --- a/components/download/internal/common/base_file_win.cc +++ b/components/download/internal/common/base_file_win.cc
@@ -6,262 +6,307 @@ #include <windows.h> -#include <cguid.h> #include <objbase.h> #include <shellapi.h> +#include <shobjidl.h> +#include <wrl/client.h> -#include "base/files/file.h" -#include "base/files/file_util.h" -#include "base/guid.h" -#include "base/macros.h" -#include "base/strings/utf_string_conversions.h" #include "base/threading/scoped_blocking_call.h" +#include "base/win/com_init_util.h" +#include "base/win/iunknown_impl.h" #include "components/download/public/common/download_interrupt_reasons_utils.h" -#include "components/download/public/common/download_stats.h" namespace download { namespace { -// Maps the result of a call to |SHFileOperation()| onto a -// |DownloadInterruptReason|. -// -// These return codes are *old* (as in, DOS era), and specific to -// |SHFileOperation()|. -// They do not appear in any windows header. -// -// See http://msdn.microsoft.com/en-us/library/bb762164(VS.85).aspx. -DownloadInterruptReason MapShFileOperationCodes(int code) { - DownloadInterruptReason result = DOWNLOAD_INTERRUPT_REASON_NONE; +// By and large the errors seen here are listed in sherrors.h, included from +// shobjidl.h. +DownloadInterruptReason HRESULTToDownloadInterruptReason(HRESULT hr) { + // S_OK, other success values are aggregated here. + if (SUCCEEDED(hr) && HRESULT_FACILITY(hr) != FACILITY_SHELL) + return DOWNLOAD_INTERRUPT_REASON_NONE; - // Check these pre-Win32 error codes first, then check for matches - // in Winerror.h. - // This switch statement should be kept in sync with the list of codes - // above. - switch (code) { - // Not a pre-Win32 error code; here so that this particular case shows up in - // our histograms. Unfortunately, it is used not just to signal actual - // ACCESS_DENIED errors, but many other errors as well. So we treat it as a - // transient error. - case ERROR_ACCESS_DENIED: // Access is denied. - result = DOWNLOAD_INTERRUPT_REASON_FILE_TRANSIENT_ERROR; - break; + // All of the remaining HRESULTs to be considered are either from the copy + // engine, or are unknown; we've got handling for all the copy engine errors, + // and otherwise we'll just return the generic error reason. + switch (hr) { + case COPYENGINE_S_YES: + case COPYENGINE_S_NOT_HANDLED: + case COPYENGINE_S_USER_RETRY: + case COPYENGINE_S_MERGE: + case COPYENGINE_S_DONT_PROCESS_CHILDREN: + case COPYENGINE_S_ALREADY_DONE: + case COPYENGINE_S_PENDING: + case COPYENGINE_S_KEEP_BOTH: + case COPYENGINE_S_COLLISIONRESOLVED: + case COPYENGINE_S_PROGRESS_PAUSE: + return DOWNLOAD_INTERRUPT_REASON_NONE; - // This isn't documented but returned from SHFileOperation. Sharing - // violations indicate that another process had the file open while we were - // trying to rename. Anti-virus is believed to be the cause of this error in - // the wild. Treated as a transient error on the assumption that the file - // will be made available for renaming at a later time. - case ERROR_SHARING_VIOLATION: - result = DOWNLOAD_INTERRUPT_REASON_FILE_TRANSIENT_ERROR; - break; + case COPYENGINE_S_CLOSE_PROGRAM: + // Like sharing violations, another process is using the file we want to + // touch, so wait for it to close. + case COPYENGINE_E_SHARING_VIOLATION_SRC: + case COPYENGINE_E_SHARING_VIOLATION_DEST: + // Sharing violations are encountered when some other process has a file + // open; often it's antivirus scanning, and this error can be treated as + // transient, as we assume eventually the other process will close its + // handle. + return DOWNLOAD_INTERRUPT_REASON_FILE_TRANSIENT_ERROR; - // This is also not a documented return value of SHFileOperation, but has - // been observed in the wild. We are treating it as a transient error based - // on the cases we have seen so far. See http://crbug.com/368455. - case ERROR_INVALID_PARAMETER: - result = DOWNLOAD_INTERRUPT_REASON_FILE_TRANSIENT_ERROR; - break; + case COPYENGINE_E_PATH_TOO_DEEP_DEST: + case COPYENGINE_E_PATH_TOO_DEEP_SRC: + case COPYENGINE_E_NEWFILE_NAME_TOO_LONG: + case COPYENGINE_E_NEWFOLDER_NAME_TOO_LONG: + // Any of these errors can be encountered if MAXPATH is hit while writing + // out a filename. This can happen really just about anywhere. + return DOWNLOAD_INTERRUPT_REASON_FILE_NAME_TOO_LONG; - // The source and destination files are the same file. - // DE_SAMEFILE == 0x71 - case 0x71: - result = DOWNLOAD_INTERRUPT_REASON_FILE_FAILED; - break; + case COPYENGINE_S_USER_IGNORED: + // On Windows 7, inability to access a file may return "user ignored" + // instead of correctly reporting the failure. + case COPYENGINE_E_ACCESS_DENIED_DEST: + case COPYENGINE_E_ACCESS_DENIED_SRC: + // There's a security problem, or the file is otherwise inaccessible. + case COPYENGINE_E_DEST_IS_RO_CD: + case COPYENGINE_E_DEST_IS_RW_CD: + case COPYENGINE_E_DEST_IS_R_CD: + case COPYENGINE_E_DEST_IS_RO_DVD: + case COPYENGINE_E_DEST_IS_RW_DVD: + case COPYENGINE_E_DEST_IS_R_DVD: + case COPYENGINE_E_SRC_IS_RO_CD: + case COPYENGINE_E_SRC_IS_RW_CD: + case COPYENGINE_E_SRC_IS_R_CD: + case COPYENGINE_E_SRC_IS_RO_DVD: + case COPYENGINE_E_SRC_IS_RW_DVD: + case COPYENGINE_E_SRC_IS_R_DVD: + // When the source is actually a disk, and a Move is attempted, it can't + // delete the source. This is unlikely to be encountered in our scenario. + return DOWNLOAD_INTERRUPT_REASON_FILE_ACCESS_DENIED; - // The operation was canceled by the user, or silently canceled if the - // appropriate flags were supplied to SHFileOperation. - // DE_OPCANCELLED == 0x75 - case 0x75: - result = DOWNLOAD_INTERRUPT_REASON_FILE_FAILED; - break; + case COPYENGINE_E_FILE_TOO_LARGE: + case COPYENGINE_E_DISK_FULL: + case COPYENGINE_E_REMOVABLE_FULL: + case COPYENGINE_E_DISK_FULL_CLEAN: + // No room for the file in the destination location. + return DOWNLOAD_INTERRUPT_REASON_FILE_TOO_LARGE; - // Security settings denied access to the source. - // DE_ACCESSDENIEDSRC == 0x78 - case 0x78: - result = DOWNLOAD_INTERRUPT_REASON_FILE_ACCESS_DENIED; - break; - - // The source or destination path exceeded or would exceed MAX_PATH. - // DE_PATHTOODEEP == 0x79 - case 0x79: - result = DOWNLOAD_INTERRUPT_REASON_FILE_NAME_TOO_LONG; - break; - - // The path in the source or destination or both was invalid. - // DE_INVALIDFILES == 0x7C - case 0x7C: - result = DOWNLOAD_INTERRUPT_REASON_FILE_FAILED; - break; - - // The destination path is an existing file. - // DE_FLDDESTISFILE == 0x7E - case 0x7E: - result = DOWNLOAD_INTERRUPT_REASON_FILE_FAILED; - break; - - // The destination path is an existing folder. - // DE_FILEDESTISFLD == 0x80 - case 0x80: - result = DOWNLOAD_INTERRUPT_REASON_FILE_FAILED; - break; - - // The name of the file exceeds MAX_PATH. - // DE_FILENAMETOOLONG == 0x81 - case 0x81: - result = DOWNLOAD_INTERRUPT_REASON_FILE_NAME_TOO_LONG; - break; - - // The destination is a read-only CD-ROM, possibly unformatted. - // DE_DEST_IS_CDROM == 0x82 - case 0x82: - result = DOWNLOAD_INTERRUPT_REASON_FILE_ACCESS_DENIED; - break; - - // The destination is a read-only DVD, possibly unformatted. - // DE_DEST_IS_DVD == 0x83 - case 0x83: - result = DOWNLOAD_INTERRUPT_REASON_FILE_ACCESS_DENIED; - break; - - // The destination is a writable CD-ROM, possibly unformatted. - // DE_DEST_IS_CDRECORD == 0x84 - case 0x84: - result = DOWNLOAD_INTERRUPT_REASON_FILE_ACCESS_DENIED; - break; - - // The file involved in the operation is too large for the destination - // media or file system. - // DE_FILE_TOO_LARGE == 0x85 - case 0x85: - result = DOWNLOAD_INTERRUPT_REASON_FILE_TOO_LARGE; - break; - - // The source is a read-only CD-ROM, possibly unformatted. - // DE_SRC_IS_CDROM == 0x86 - case 0x86: - result = DOWNLOAD_INTERRUPT_REASON_FILE_ACCESS_DENIED; - break; - - // The source is a read-only DVD, possibly unformatted. - // DE_SRC_IS_DVD == 0x87 - case 0x87: - result = DOWNLOAD_INTERRUPT_REASON_FILE_ACCESS_DENIED; - break; - - // The source is a writable CD-ROM, possibly unformatted. - // DE_SRC_IS_CDRECORD == 0x88 - case 0x88: - result = DOWNLOAD_INTERRUPT_REASON_FILE_ACCESS_DENIED; - break; - - // MAX_PATH was exceeded during the operation. - // DE_ERROR_MAX == 0xB7 - case 0xB7: - result = DOWNLOAD_INTERRUPT_REASON_FILE_NAME_TOO_LONG; - break; - - // An unspecified error occurred on the destination. - // XE_ERRORONDEST == 0x10000 - case 0x10000: - result = DOWNLOAD_INTERRUPT_REASON_FILE_FAILED; - break; - - // Multiple file paths were specified in the source buffer, but only one - // destination file path. - // DE_MANYSRC1DEST == 0x72 - case 0x72: - result = DOWNLOAD_INTERRUPT_REASON_FILE_FAILED; - break; - - // Rename operation was specified but the destination path is - // a different directory. Use the move operation instead. - // DE_DIFFDIR == 0x73 - case 0x73: - result = DOWNLOAD_INTERRUPT_REASON_FILE_FAILED; - break; - - // The source is a root directory, which cannot be moved or renamed. - // DE_ROOTDIR == 0x74 - case 0x74: - result = DOWNLOAD_INTERRUPT_REASON_FILE_FAILED; - break; - - // The destination is a subtree of the source. - // DE_DESTSUBTREE == 0x76 - case 0x76: - result = DOWNLOAD_INTERRUPT_REASON_FILE_FAILED; - break; - - // The operation involved multiple destination paths, - // which can fail in the case of a move operation. - // DE_MANYDEST == 0x7A - case 0x7A: - result = DOWNLOAD_INTERRUPT_REASON_FILE_FAILED; - break; - - // The source and destination have the same parent folder. - // DE_DESTSAMETREE == 0x7D - case 0x7D: - result = DOWNLOAD_INTERRUPT_REASON_FILE_FAILED; - break; - - // An unknown error occurred. This is typically due to an invalid path in - // the source or destination. This error does not occur on Windows Vista - // and later. - // DE_UNKNOWN_ERROR == 0x402 - case 0x402: - result = DOWNLOAD_INTERRUPT_REASON_FILE_FAILED; - break; - - // Destination is a root directory and cannot be renamed. - // DE_ROOTDIR | ERRORONDEST == 0x10074 - case 0x10074: - result = DOWNLOAD_INTERRUPT_REASON_FILE_FAILED; - break; + case COPYENGINE_E_ALREADY_EXISTS_NORMAL: + case COPYENGINE_E_ALREADY_EXISTS_READONLY: + case COPYENGINE_E_ALREADY_EXISTS_SYSTEM: + case COPYENGINE_E_ALREADY_EXISTS_FOLDER: + // The destination already exists and can't be replaced. + case COPYENGINE_E_INVALID_FILES_SRC: + case COPYENGINE_E_INVALID_FILES_DEST: + // Either the source or destination file was invalid. + case COPYENGINE_E_STREAM_LOSS: + case COPYENGINE_E_EA_LOSS: + case COPYENGINE_E_PROPERTY_LOSS: + case COPYENGINE_E_PROPERTIES_LOSS: + case COPYENGINE_E_ENCRYPTION_LOSS: + // The destination can't support some functionality that the file needs. + // The interesting one here is E_STREAM_LOSS, especially with MOTW. + case COPYENGINE_E_FLD_IS_FILE_DEST: + case COPYENGINE_E_FILE_IS_FLD_DEST: + // There is an existing file with the same name as a new folder, and + // vice versa. + case COPYENGINE_E_ROOT_DIR_DEST: + case COPYENGINE_E_ROOT_DIR_SRC: + case COPYENGINE_E_DIFF_DIR: + case COPYENGINE_E_SAME_FILE: + case COPYENGINE_E_MANY_SRC_1_DEST: + case COPYENGINE_E_DEST_SUBTREE: + case COPYENGINE_E_DEST_SAME_TREE: + case COPYENGINE_E_USER_CANCELLED: + case COPYENGINE_E_CANCELLED: + case COPYENGINE_E_REQUIRES_ELEVATION: + return DOWNLOAD_INTERRUPT_REASON_FILE_FAILED; } - if (result != DOWNLOAD_INTERRUPT_REASON_NONE) - return result; + // Copy operations may still return Win32 error codes, so handle those here. + if (HRESULT_FACILITY(hr) == FACILITY_WIN32) { + return ConvertFileErrorToInterruptReason( + base::File::OSErrorToFileError(HRESULT_CODE(hr))); + } - // If not one of the above codes, it should be a standard Windows error code. - return ConvertFileErrorToInterruptReason( - base::File::OSErrorToFileError(code)); + return DOWNLOAD_INTERRUPT_REASON_FILE_FAILED; } +class FileOperationProgressSink : public base::win::IUnknownImpl, + public IFileOperationProgressSink { + public: + FileOperationProgressSink() = default; + + HRESULT GetOperationResult() { return result_; } + + // base::win::IUnknownImpl: + STDMETHODIMP QueryInterface(REFIID riid, PVOID* ppv) override { + if (riid == IID_IFileOperationProgressSink) { + *ppv = static_cast<IFileOperationProgressSink*>(this); + IUnknownImpl::AddRef(); + return S_OK; + } + + return IUnknownImpl::QueryInterface(riid, ppv); + } + + ULONG STDMETHODCALLTYPE AddRef() override { return IUnknownImpl::AddRef(); } + ULONG STDMETHODCALLTYPE Release() override { return IUnknownImpl::Release(); } + + // IFileOperationProgressSink: + HRESULT STDMETHODCALLTYPE FinishOperations(HRESULT hr) override { + // If a failure has already been captured, don't bother overriding it. That + // way, the original failure can be propagated; in the event that the new + // HRESULT is also a success, overwriting will not harm anything and + // captures the final state of the whole operation. + if (SUCCEEDED(result_)) + result_ = hr; + return S_OK; + } + + HRESULT STDMETHODCALLTYPE PauseTimer() override { return S_OK; } + HRESULT STDMETHODCALLTYPE PostCopyItem(DWORD, + IShellItem*, + IShellItem*, + PCWSTR, + HRESULT, + IShellItem*) override { + return E_NOTIMPL; + } + HRESULT STDMETHODCALLTYPE PostDeleteItem(DWORD, + IShellItem*, + HRESULT, + IShellItem*) override { + return E_NOTIMPL; + } + HRESULT STDMETHODCALLTYPE PostMoveItem(DWORD, + IShellItem*, + IShellItem*, + PCWSTR, + HRESULT hr, + IShellItem*) override { + // Like in FinishOperations, overwriting with a different success value + // does not have a negative impact, but replacing an existing failure will + // cause issues. + if (SUCCEEDED(result_)) + result_ = hr; + return S_OK; + } + HRESULT STDMETHODCALLTYPE PostNewItem(DWORD, + IShellItem*, + PCWSTR, + PCWSTR, + DWORD, + HRESULT, + IShellItem*) override { + return E_NOTIMPL; + } + HRESULT STDMETHODCALLTYPE + PostRenameItem(DWORD, IShellItem*, PCWSTR, HRESULT, IShellItem*) override { + return E_NOTIMPL; + } + HRESULT STDMETHODCALLTYPE PreCopyItem(DWORD, + IShellItem*, + IShellItem*, + PCWSTR) override { + return E_NOTIMPL; + } + HRESULT STDMETHODCALLTYPE PreDeleteItem(DWORD, IShellItem*) override { + return E_NOTIMPL; + } + HRESULT STDMETHODCALLTYPE PreMoveItem(DWORD, + IShellItem*, + IShellItem*, + PCWSTR) override { + return S_OK; + } + HRESULT STDMETHODCALLTYPE PreNewItem(DWORD, IShellItem*, PCWSTR) override { + return E_NOTIMPL; + } + HRESULT STDMETHODCALLTYPE PreRenameItem(DWORD, IShellItem*, PCWSTR) override { + return E_NOTIMPL; + } + HRESULT STDMETHODCALLTYPE ResetTimer() override { return S_OK; } + HRESULT STDMETHODCALLTYPE ResumeTimer() override { return S_OK; } + HRESULT STDMETHODCALLTYPE StartOperations() override { return S_OK; } + HRESULT STDMETHODCALLTYPE UpdateProgress(UINT, UINT) override { return S_OK; } + + protected: + ~FileOperationProgressSink() override = default; + + private: + HRESULT result_ = S_OK; + + DISALLOW_COPY_AND_ASSIGN(FileOperationProgressSink); +}; + } // namespace -// Renames a file using the SHFileOperation API to ensure that the target file -// gets the correct default security descriptor in the new path. +// Renames a file using IFileOperation::MoveItem() to ensure that the target +// file gets the correct default security descriptor in the new path. // Returns a network error, or net::OK for success. DownloadInterruptReason BaseFile::MoveFileAndAdjustPermissions( const base::FilePath& new_path) { base::ScopedBlockingCall scoped_blocking_call(FROM_HERE, base::BlockingType::MAY_BLOCK); - // The parameters to SHFileOperation must be terminated with 2 NULL chars. - base::FilePath::StringType source = full_path_.value(); - base::FilePath::StringType target = new_path.value(); + base::win::AssertComInitialized(); + Microsoft::WRL::ComPtr<IShellItem> original_path; + HRESULT hr = SHCreateItemFromParsingName(full_path_.value().c_str(), nullptr, + IID_PPV_ARGS(&original_path)); - source.append(1, L'\0'); - target.append(1, L'\0'); + // |new_path| can be broken down to provide the new folder, as well as the + // new filename. We'll start with the folder, which the caller should ensure + // exists. + Microsoft::WRL::ComPtr<IShellItem> destination_folder; + if (SUCCEEDED(hr)) { + hr = + SHCreateItemFromParsingName(new_path.DirName().value().c_str(), nullptr, + IID_PPV_ARGS(&destination_folder)); + } - SHFILEOPSTRUCT move_info = {nullptr}; - move_info.wFunc = FO_MOVE; - move_info.pFrom = source.c_str(); - move_info.pTo = target.c_str(); - move_info.fFlags = FOF_SILENT | FOF_NOCONFIRMATION | FOF_NOERRORUI | - FOF_NOCONFIRMMKDIR | FOF_NOCOPYSECURITYATTRIBS; + Microsoft::WRL::ComPtr<IFileOperation> file_operation; + if (SUCCEEDED(hr)) { + hr = CoCreateInstance(CLSID_FileOperation, nullptr, CLSCTX_INPROC_SERVER, + IID_PPV_ARGS(&file_operation)); + } - int result = SHFileOperation(&move_info); - DownloadInterruptReason interrupt_reason = DOWNLOAD_INTERRUPT_REASON_NONE; + if (SUCCEEDED(hr)) { + // Don't show any UI, don't migrate security attributes (use the + // destination's attributes), and stop on first error-retaining the original + // failure reason. + hr = file_operation->SetOperationFlags( + FOF_NO_UI | FOF_NOCOPYSECURITYATTRIBS | FOFX_EARLYFAILURE); + } - if (result == 0 && move_info.fAnyOperationsAborted) - interrupt_reason = DOWNLOAD_INTERRUPT_REASON_FILE_FAILED; - else if (result != 0) - interrupt_reason = MapShFileOperationCodes(result); + scoped_refptr<FileOperationProgressSink> sink(new FileOperationProgressSink); + if (SUCCEEDED(hr)) { + hr = file_operation->MoveItem(original_path.Get(), destination_folder.Get(), + new_path.BaseName().value().c_str(), + sink.get()); + } + + if (SUCCEEDED(hr)) + hr = file_operation->PerformOperations(); + + if (SUCCEEDED(hr)) + hr = sink->GetOperationResult(); + + // Convert HRESULT to DownloadInterruptReason. + DownloadInterruptReason interrupt_reason = + HRESULTToDownloadInterruptReason(hr); + + if (interrupt_reason == DOWNLOAD_INTERRUPT_REASON_NONE) { + // The operation could still have been aborted; we can't get a better reason + // at this point, but we've got more information to go by. + BOOL any_operations_aborted = TRUE; + file_operation->GetAnyOperationsAborted(&any_operations_aborted); + if (any_operations_aborted) + interrupt_reason = DOWNLOAD_INTERRUPT_REASON_FILE_FAILED; + } if (interrupt_reason != DOWNLOAD_INTERRUPT_REASON_NONE) - return LogInterruptReason("SHFileOperation", result, interrupt_reason); + return LogInterruptReason("IFileOperation::MoveItem", hr, interrupt_reason); + return interrupt_reason; }
diff --git a/components/feedback/feedback_common.cc b/components/feedback/feedback_common.cc index 58bc46c7..41f4149 100644 --- a/components/feedback/feedback_common.cc +++ b/components/feedback/feedback_common.cc
@@ -42,6 +42,8 @@ constexpr char kPngMimeType[] = "image/png"; constexpr char kArbitraryMimeType[] = "application/octet-stream"; +constexpr char kGoogleDotCom[] = "@google.com"; + // Determine if the given feedback value is small enough to not need to // be compressed. bool BelowCompressionThreshold(const std::string& content) { @@ -66,7 +68,8 @@ // We must avoid adding the crash IDs to the system_logs.txt file for // privacy reasons. They should just be part of the product specific data. - if (key == feedback::FeedbackReport::kCrashReportIdsKey) + if (key == feedback::FeedbackReport::kCrashReportIdsKey || + key == feedback::FeedbackReport::kAllCrashReportIdsKey) continue; if (value.find("\n") != std::string::npos) { @@ -242,6 +245,15 @@ for (const auto& iter : logs_) { if (BelowCompressionThreshold(iter.second)) { + // We only send the list of all the crash report IDs if the user has a + // @google.com email. We do this also in feedback_private_api, but not all + // code paths go through that so we need to check again here. + if (iter.first == feedback::FeedbackReport::kAllCrashReportIdsKey && + !base::EndsWith(user_email(), kGoogleDotCom, + base::CompareCase::INSENSITIVE_ASCII)) { + continue; + } + // Small enough logs should end up in the report data itself. However, // they're still added as part of the system_logs.zip file. AddFeedbackData(feedback_data, iter.first, iter.second);
diff --git a/components/feedback/feedback_common_unittest.cc b/components/feedback/feedback_common_unittest.cc index 539bd8f7..3d9a78ec 100644 --- a/components/feedback/feedback_common_unittest.cc +++ b/components/feedback/feedback_common_unittest.cc
@@ -5,6 +5,7 @@ #include "components/feedback/feedback_common.h" #include "base/bind.h" +#include "components/feedback/feedback_report.h" #include "components/feedback/proto/common.pb.h" #include "components/feedback/proto/dom.pb.h" #include "components/feedback/proto/extension.pb.h" @@ -101,3 +102,19 @@ EXPECT_EQ(kLogsAttachmentName, report_.product_specific_binary_data(0).name()); } + +TEST_F(FeedbackCommonTest, TestAllCrashIdsRemoval) { + feedback_->AddLog(feedback::FeedbackReport::kAllCrashReportIdsKey, kOne); + feedback_->set_user_email("nobody@example.com"); + feedback_->PrepareReport(&report_); + + EXPECT_EQ(0, report_.web_data().product_specific_data_size()); +} + +TEST_F(FeedbackCommonTest, TestAllCrashIdsRetention) { + feedback_->AddLog(feedback::FeedbackReport::kAllCrashReportIdsKey, kOne); + feedback_->set_user_email("nobody@google.com"); + feedback_->PrepareReport(&report_); + + EXPECT_EQ(1, report_.web_data().product_specific_data_size()); +}
diff --git a/components/feedback/feedback_report.cc b/components/feedback/feedback_report.cc index 4b0d804..c1f362f 100644 --- a/components/feedback/feedback_report.cc +++ b/components/feedback/feedback_report.cc
@@ -61,6 +61,9 @@ const char FeedbackReport::kCrashReportIdsKey[] = "crash_report_ids"; // static +const char FeedbackReport::kAllCrashReportIdsKey[] = "all_crash_report_ids"; + +// static void FeedbackReport::LoadReportsAndQueue(const base::FilePath& user_dir, const QueueCallback& callback) { if (user_dir.empty())
diff --git a/components/feedback/feedback_report.h b/components/feedback/feedback_report.h index 1e330cab..413e03b 100644 --- a/components/feedback/feedback_report.h +++ b/components/feedback/feedback_report.h
@@ -38,6 +38,10 @@ // the feedback server. static const char kCrashReportIdsKey[]; + // The ID of the product specific data for the list of all crash report IDs as + // stored by the feedback server. Only used for @google.com emails. + static const char kAllCrashReportIdsKey[]; + // Loads the reports still on disk and queues then using the given callback. // This call blocks on the file reads. static void LoadReportsAndQueue(const base::FilePath& user_dir,
diff --git a/components/feedback/system_logs/system_logs_fetcher.cc b/components/feedback/system_logs/system_logs_fetcher.cc index efeba4b..b493738f 100644 --- a/components/feedback/system_logs/system_logs_fetcher.cc +++ b/components/feedback/system_logs/system_logs_fetcher.cc
@@ -23,7 +23,6 @@ // not be anonymized. constexpr const char* const kWhitelistedKeysOfUUIDs[] = { "CHROMEOS_BOARD_APPID", "CHROMEOS_CANARY_APPID", "CHROMEOS_RELEASE_APPID", - "CLIENT_ID", }; // Returns true if the given |key| is anonymizer-whitelisted and whose
diff --git a/components/password_manager/core/browser/form_parsing/form_parser.cc b/components/password_manager/core/browser/form_parsing/form_parser.cc index f77ab0cd..51bea67 100644 --- a/components/password_manager/core/browser/form_parsing/form_parser.cc +++ b/components/password_manager/core/browser/form_parsing/form_parser.cc
@@ -223,13 +223,22 @@ significant_fields.confirmation_password == field; } -// Returns the first element of |fields| which has the specified -// |unique_renderer_id|, or null if there is no such element. -ProcessedField* FindFieldWithUniqueRendererId( - std::vector<ProcessedField>* processed_fields, - uint32_t unique_renderer_id) { +bool DoesPredictionCorrespondToField( + const FormFieldData& field, + const PasswordFieldPrediction& prediction) { +#if defined(OS_IOS) + return field.unique_id == prediction.unique_id; +#else + return field.unique_renderer_id == prediction.renderer_id; +#endif +} + +// Returns the first element of |fields| which corresponds to |prediction|, or +// null if there is no such element. +ProcessedField* FindField(std::vector<ProcessedField>* processed_fields, + const PasswordFieldPrediction& prediction) { for (ProcessedField& processed_field : *processed_fields) { - if (processed_field.field->unique_renderer_id == unique_renderer_id) + if (DoesPredictionCorrespondToField(*processed_field.field, prediction)) return &processed_field; } return nullptr; @@ -266,14 +275,12 @@ switch (field_type) { case CredentialFieldType::kUsername: if (!result->username) { - processed_field = FindFieldWithUniqueRendererId( - processed_fields, prediction.renderer_id); + processed_field = FindField(processed_fields, prediction); if (processed_field) { result->username = processed_field->field; } } else if (!second_username) { - processed_field = FindFieldWithUniqueRendererId( - processed_fields, prediction.renderer_id); + processed_field = FindField(processed_fields, prediction); if (processed_field) { second_username = processed_field->field; } @@ -285,8 +292,7 @@ if (result->password) { prevent_handling_two_usernames = true; } else { - processed_field = FindFieldWithUniqueRendererId( - processed_fields, prediction.renderer_id); + processed_field = FindField(processed_fields, prediction); if (processed_field) { if (!processed_field->is_password) continue; @@ -306,8 +312,7 @@ // before the user has thought of and typed their new password // elsewhere. See https://crbug.com/902700 for more details. if (!result->new_password) { - processed_field = FindFieldWithUniqueRendererId( - processed_fields, prediction.renderer_id); + processed_field = FindField(processed_fields, prediction); if (processed_field) { if (!processed_field->is_password) continue; @@ -316,8 +321,7 @@ } break; case CredentialFieldType::kConfirmationPassword: - processed_field = FindFieldWithUniqueRendererId(processed_fields, - prediction.renderer_id); + processed_field = FindField(processed_fields, prediction); if (processed_field) { if (!processed_field->is_password) continue; @@ -357,8 +361,7 @@ for (const PasswordFieldPrediction& prediction : predictions) { if (prediction.type == autofill::CREDIT_CARD_VERIFICATION_CODE || prediction.type == autofill::NOT_PASSWORD) { - ProcessedField* processed_field = FindFieldWithUniqueRendererId( - processed_fields, prediction.renderer_id); + ProcessedField* processed_field = FindField(processed_fields, prediction); if (processed_field) processed_field->server_hints_not_password = true; }
diff --git a/components/password_manager/core/browser/form_parsing/form_parser_unittest.cc b/components/password_manager/core/browser/form_parsing/form_parser_unittest.cc index 894dc06..de0a845 100644 --- a/components/password_manager/core/browser/form_parsing/form_parser_unittest.cc +++ b/components/password_manager/core/browser/form_parsing/form_parser_unittest.cc
@@ -183,6 +183,9 @@ field.name = ASCIIToUTF16(field_description.name); } field.name_attribute = field.name; +#if defined(OS_IOS) + field.unique_id = StampUniqueSuffix("unique_id"); +#endif field.form_control_type = field_description.form_control_type; field.is_focusable = field_description.is_focusable; field.is_enabled = field_description.is_enabled; @@ -212,6 +215,9 @@ if (field_description.prediction.type != autofill::MAX_VALID_FIELD_TYPE) { predictions->push_back(field_description.prediction); predictions->back().renderer_id = renderer_id; +#if defined(OS_IOS) + predictions->back().unique_id = field.unique_id; +#endif } if (field_description.predicted_username >= 0) { size_t index = static_cast<size_t>(field_description.predicted_username);
diff --git a/components/password_manager/core/browser/form_parsing/password_field_prediction.cc b/components/password_manager/core/browser/form_parsing/password_field_prediction.cc index 3cae5de..7f46f57 100644 --- a/components/password_manager/core/browser/form_parsing/password_field_prediction.cc +++ b/components/password_manager/core/browser/form_parsing/password_field_prediction.cc
@@ -90,6 +90,9 @@ {.renderer_id = field->unique_renderer_id, .type = server_type, .may_use_prefilled_placeholder = may_use_prefilled_placeholder}); +#if defined(OS_IOS) + result.back().unique_id = field->unique_id; +#endif } }
diff --git a/components/password_manager/core/browser/form_parsing/password_field_prediction.h b/components/password_manager/core/browser/form_parsing/password_field_prediction.h index 50adf03e..d92f876 100644 --- a/components/password_manager/core/browser/form_parsing/password_field_prediction.h +++ b/components/password_manager/core/browser/form_parsing/password_field_prediction.h
@@ -8,6 +8,7 @@ #include <stdint.h> #include <vector> +#include "build/build_config.h" #include "components/autofill/core/browser/field_types.h" namespace autofill { @@ -30,7 +31,11 @@ // Contains server predictions for a field. struct PasswordFieldPrediction { + // Field identifier generated in Blink on non-iOS platforms. uint32_t renderer_id; +#if defined(OS_IOS) + base::string16 unique_id; +#endif autofill::ServerFieldType type; bool may_use_prefilled_placeholder = false; };
diff --git a/components/policy/core/common/cloud/cloud_policy_client.cc b/components/policy/core/common/cloud/cloud_policy_client.cc index c6dc10b..94f2c6c 100644 --- a/components/policy/core/common/cloud/cloud_policy_client.cc +++ b/components/policy/core/common/cloud/cloud_policy_client.cc
@@ -556,10 +556,11 @@ void CloudPolicyClient::UploadDeviceStatus( const em::DeviceStatusReportRequest* device_status, const em::SessionStatusReportRequest* session_status, + const em::ChildStatusReportRequest* child_status, const CloudPolicyClient::StatusCallback& callback) { CHECK(is_registered()); // Should pass in at least one type of status. - DCHECK(device_status || session_status); + DCHECK(device_status || session_status || child_status); std::unique_ptr<DeviceManagementRequestJob> request_job(service_->CreateJob( DeviceManagementRequestJob::TYPE_UPLOAD_STATUS, GetURLLoaderFactory())); request_job->SetAuthData(DMAuth::FromDMToken(dm_token_)); @@ -572,6 +573,8 @@ *request->mutable_device_status_report_request() = *device_status; if (session_status) *request->mutable_session_status_report_request() = *session_status; + if (child_status) + *request->mutable_child_status_report_request() = *child_status; const DeviceManagementRequestJob::Callback job_callback = base::AdaptCallbackForRepeating(base::BindOnce(
diff --git a/components/policy/core/common/cloud/cloud_policy_client.h b/components/policy/core/common/cloud/cloud_policy_client.h index f8487a72..04dc912 100644 --- a/components/policy/core/common/cloud/cloud_policy_client.h +++ b/components/policy/core/common/cloud/cloud_policy_client.h
@@ -224,13 +224,13 @@ virtual void UploadEnterpriseEnrollmentId(const std::string& enrollment_id, const StatusCallback& callback); - // Uploads device/session status to the server. As above, the client must be - // in a registered state. If non-null, |device_status| and |session_status| - // will be included in the upload status request. The |callback| will be - // called when the operation completes. + // Uploads status to the server. The client must be in a registered state. + // Only non-null statuses will be included in the upload status request. The + // |callback| will be called when the operation completes. virtual void UploadDeviceStatus( const enterprise_management::DeviceStatusReportRequest* device_status, const enterprise_management::SessionStatusReportRequest* session_status, + const enterprise_management::ChildStatusReportRequest* child_status, const StatusCallback& callback); // Uploads Chrome Desktop report to the server. As above, the client must be
diff --git a/components/policy/core/common/cloud/cloud_policy_client_unittest.cc b/components/policy/core/common/cloud/cloud_policy_client_unittest.cc index e00ff4b..378cb59 100644 --- a/components/policy/core/common/cloud/cloud_policy_client_unittest.cc +++ b/components/policy/core/common/cloud/cloud_policy_client_unittest.cc
@@ -206,6 +206,7 @@ upload_status_request_.mutable_device_status_report_request(); upload_status_request_.mutable_session_status_report_request(); + upload_status_request_.mutable_child_status_report_request(); chrome_desktop_report_request_.mutable_chrome_desktop_report_request(); @@ -1160,7 +1161,9 @@ base::Unretained(&callback_observer_)); em::DeviceStatusReportRequest device_status; em::SessionStatusReportRequest session_status; - client_->UploadDeviceStatus(&device_status, &session_status, callback); + em::ChildStatusReportRequest child_status; + client_->UploadDeviceStatus(&device_status, &session_status, &child_status, + callback); EXPECT_EQ(DM_STATUS_SUCCESS, client_->status()); } @@ -1177,7 +1180,9 @@ base::Unretained(&callback_observer_)); em::DeviceStatusReportRequest device_status; em::SessionStatusReportRequest session_status; - client_->UploadDeviceStatus(&device_status, &session_status, callback); + em::ChildStatusReportRequest child_status; + client_->UploadDeviceStatus(&device_status, &session_status, &child_status, + callback); EXPECT_EQ(DM_STATUS_SUCCESS, client_->status()); // Tests that previous OAuth token is no longer sent in status upload after @@ -1186,7 +1191,8 @@ ExpectUploadStatus(); EXPECT_CALL(callback_observer_, OnCallbackComplete(true)).Times(1); - client_->UploadDeviceStatus(&device_status, &session_status, callback); + client_->UploadDeviceStatus(&device_status, &session_status, &child_status, + callback); EXPECT_EQ(DM_STATUS_SUCCESS, client_->status()); } @@ -1204,7 +1210,9 @@ base::Unretained(&callback_observer_)); em::DeviceStatusReportRequest device_status; em::SessionStatusReportRequest session_status; - client_->UploadDeviceStatus(&device_status, &session_status, callback); + em::ChildStatusReportRequest child_status; + client_->UploadDeviceStatus(&device_status, &session_status, &child_status, + callback); // Now initiate a policy fetch - this should not cancel the upload job. ExpectPolicyFetch(kDMToken); @@ -1259,7 +1267,9 @@ base::Unretained(&callback_observer_)); em::DeviceStatusReportRequest device_status; em::SessionStatusReportRequest session_status; - client_->UploadDeviceStatus(&device_status, &session_status, callback); + em::ChildStatusReportRequest child_status; + client_->UploadDeviceStatus(&device_status, &session_status, &child_status, + callback); // Set up pending upload certificate job. MockDeviceManagementJob* upload_certificate_job = nullptr; @@ -1304,7 +1314,9 @@ em::DeviceStatusReportRequest device_status; em::SessionStatusReportRequest session_status; - client_->UploadDeviceStatus(&device_status, &session_status, callback); + em::ChildStatusReportRequest child_status; + client_->UploadDeviceStatus(&device_status, &session_status, &child_status, + callback); EXPECT_EQ(DM_STATUS_REQUEST_FAILED, client_->status()); } @@ -1323,7 +1335,9 @@ base::Unretained(&callback_observer_)); em::DeviceStatusReportRequest device_status; em::SessionStatusReportRequest session_status; - client_->UploadDeviceStatus(&device_status, &session_status, callback); + em::ChildStatusReportRequest child_status; + client_->UploadDeviceStatus(&device_status, &session_status, &child_status, + callback); EXPECT_EQ(1, client_->GetActiveRequestCountForTest()); EXPECT_CALL(observer_, OnRegistrationStateChanged(_)); ExpectUnregistration(kDMToken);
diff --git a/components/policy/core/common/cloud/mock_cloud_policy_client.h b/components/policy/core/common/cloud/mock_cloud_policy_client.h index 9de7d08f..7de1e5ca 100644 --- a/components/policy/core/common/cloud/mock_cloud_policy_client.h +++ b/components/policy/core/common/cloud/mock_cloud_policy_client.h
@@ -48,9 +48,10 @@ void(const std::string&, const StatusCallback&)); MOCK_METHOD2(UploadEnterpriseEnrollmentId, void(const std::string&, const StatusCallback&)); - MOCK_METHOD3(UploadDeviceStatus, + MOCK_METHOD4(UploadDeviceStatus, void(const enterprise_management::DeviceStatusReportRequest*, const enterprise_management::SessionStatusReportRequest*, + const enterprise_management::ChildStatusReportRequest*, const StatusCallback&)); MOCK_METHOD2(UploadAppInstallReport, void(const enterprise_management::AppInstallReportRequest*,
diff --git a/components/policy/proto/device_management_backend.proto b/components/policy/proto/device_management_backend.proto index cb3ea15..c2544df9 100644 --- a/components/policy/proto/device_management_backend.proto +++ b/components/policy/proto/device_management_backend.proto
@@ -1972,6 +1972,58 @@ // account. message RefreshAccountResponse {} +// Models a window for screen time. +message ScreenTimeSpan { + optional TimePeriod time_period = 1; + + // The actual activity duration during a particular time period window + // (in milliseconds). + optional int64 active_duration_ms = 2; +} + +// Informs the server about the current state of a child user's session, to +// allow parent supervision. +message ChildStatusReportRequest { + // The user's DMToken. + optional string user_dm_token = 1; + + // Timestamp of this status report in milliseconds since epoch. + optional int64 timestamp_ms = 2; + + // Time zone id of the active user (e.g. America/Sao_Paulo). + // For more details check `third_party/icu/source/i18n/unicode/timezone.h`. + optional string time_zone = 3; + + // A list of time spans when the screen was on during the user's session. + repeated ScreenTimeSpan screen_time_span = 4; + + // Information about ARC status. + optional AndroidStatus android_status = 5; + + // The OS version reported by the device is a platform version + // e.g. 1435.0.2011_12_16_1635. + optional string os_version = 6; + + // "Verified", "Dev". Same as verified mode. + // If the mode is unknown, this field should not be set. + optional string boot_mode = 7; + + // Next id: 8. +} + +// Response from DMServer to update user devices' status. +// It is possible that status report fails but policy request succeed. In such +// case, the ChildStatusReportResponse will contain an error code and the +// device should re-send status report data in the next policy request. The +// device should re-send report data if policy request fails, even if +// ChildStatusReportResponse contains no error code. +message ChildStatusReportResponse { + optional int32 error_code = 1; + + // Human readable error message for customer support purpose. + optional string error_message = 2; +} + // Request from the DMAgent on the device to the DMServer. This is // container for all requests from device to server. The overall HTTP // request MUST be in the following format: @@ -2044,7 +2096,8 @@ // ping: policy_request // policy: policy_request // register: register_request -// status: device_status_report_request or session_status_report_request +// status: device_status_report_request or session_status_report_request or +// child_status_report_request // unregister: unregister_request // remote_commands: remote_command_request // attribute_update_permission: device_attribute_update_permission_request @@ -2062,7 +2115,6 @@ // policy_validation_report: policy_validation_report_request // device_initial_enrollment_state: device_initial_enrollment_state_request // refresh_account: refresh_account_request -// message DeviceManagementRequest { reserved 24; // unused previous version of chrome_desktop_report_request. @@ -2078,6 +2130,7 @@ // Update status. optional DeviceStatusReportRequest device_status_report_request = 4; optional SessionStatusReportRequest session_status_report_request = 5; + optional ChildStatusReportRequest child_status_report_request = 30; // Auto-enrollment detection. optional DeviceAutoEnrollmentRequest auto_enrollment_request = 6; @@ -2154,6 +2207,8 @@ // Request from device to wipe an old account and get a new account. optional RefreshAccountRequest refresh_account_request = 29; + + // Next id: 31. } // Response from server to device. @@ -2197,11 +2252,10 @@ // Policy response. optional DevicePolicyResponse policy_response = 5; - // Device status report response. + // Update status report response. optional DeviceStatusReportResponse device_status_report_response = 6; - - // Session status report response. optional SessionStatusReportResponse session_status_report_response = 7; + optional ChildStatusReportResponse child_status_report_response = 29; // Auto-enrollment detection response. optional DeviceAutoEnrollmentResponse auto_enrollment_response = 8; @@ -2269,4 +2323,6 @@ // Response to refresh account request. optional RefreshAccountResponse refresh_account_response = 28; + + // Next id: 30. }
diff --git a/components/previews/content/hint_cache.cc b/components/previews/content/hint_cache.cc index 29ad4cfc..83e68dc 100644 --- a/components/previews/content/hint_cache.cc +++ b/components/previews/content/hint_cache.cc
@@ -87,9 +87,9 @@ } std::unique_ptr<HintCacheStore::ComponentUpdateData> -HintCache::CreateUpdateDataForFetchedHints() const { +HintCache::CreateUpdateDataForFetchedHints(base::Time update_time) const { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - return hint_store_->CreateUpdateDataForFetchedHints(); + return hint_store_->CreateUpdateDataForFetchedHints(update_time); } void HintCache::UpdateComponentData( @@ -108,16 +108,18 @@ bool HintCache::StoreFetchedHints( std::unique_ptr<optimization_guide::proto::GetHintsResponse> - get_hints_response) { + get_hints_response, + base::Time update_time, + base::OnceClosure callback) { std::unique_ptr<HintCacheStore::ComponentUpdateData> - fetched_hints_update_data = CreateUpdateDataForFetchedHints(); - if (!ProcessGetHintsResponse(get_hints_response.get(), - fetched_hints_update_data.get())) { - return false; + fetched_hints_update_data = CreateUpdateDataForFetchedHints(update_time); + if (ProcessGetHintsResponse(get_hints_response.get(), + fetched_hints_update_data.get())) { + hint_store_->UpdateFetchedHintsData(std::move(fetched_hints_update_data), + std::move(callback)); + return true; } - - // TODO(mcrouse): Provide the |hint_store_| with UpdateData to stored. - return true; + return false; } bool HintCache::HasHint(const std::string& host) const { @@ -171,6 +173,13 @@ return nullptr; } +base::Time HintCache::FetchedHintsUpdateTime() const { + if (!hint_store_) { + return base::Time(); + } + return hint_store_->FetchedHintsUpdateTime(); +} + void HintCache::OnStoreInitialized(base::OnceClosure callback) { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); std::move(callback).Run();
diff --git a/components/previews/content/hint_cache.h b/components/previews/content/hint_cache.h index b21749b9..55f1ed2 100644 --- a/components/previews/content/hint_cache.h +++ b/components/previews/content/hint_cache.h
@@ -56,9 +56,10 @@ // hints. No version is needed nor applicable for fetched hints. During // processing of the GetHintsResponse, hints are moved into the update data. // After processing is complete, the update data is provided to the backing - // store to update hints. + // store to update hints. |update_time| specifies when the hints within the + // created update data will be scheduled to be updated. std::unique_ptr<HintCacheStore::ComponentUpdateData> - CreateUpdateDataForFetchedHints() const; + CreateUpdateDataForFetchedHints(base::Time update_time) const; // Updates the store's component data using the provided ComponentUpdateData // and asynchronously runs the provided callback after the update finishes. @@ -66,13 +67,18 @@ std::unique_ptr<HintCacheStore::ComponentUpdateData> component_data, base::OnceClosure callback); - // Process |get_hints_response| to be stored in the hint cache store. - // Returns true if processing get_hints_response is successful and applicable - // hints can be stored. Returns false if there are no applicable hints in - // |get_hints_response| or it cannot be processed. + // Process |get_hints_response| to be stored in the hint cache store. Returns + // true if processing |get_hints_response| is successful and applicable hints + // can be stored. Returns false if there are no applicable hints in + // |get_hints_response| or it cannot be processed. |callback| is + // asynchronously run when the hints are successfully stored or if the store + // is not available. |update_time| specifies when the hints within + // |get_hints_response| will need to be updated next. bool StoreFetchedHints( std::unique_ptr<optimization_guide::proto::GetHintsResponse> - get_hints_response); + get_hints_response, + base::Time update_time, + base::OnceClosure callback); // Returns whether the cache has a hint data for |host| locally (whether // in memory or persisted on disk). @@ -82,6 +88,11 @@ // |callback| if/when loaded. void LoadHint(const std::string& host, HintLoadedCallback callback); + // Returns the update time provided by |hint_store_|, which specifies when the + // fetched hints within the store are ready to be updated. If |hint_store_| is + // not initialized, base::Time() is returned. + base::Time FetchedHintsUpdateTime() const; + // Returns the hint data for |host| if found in memory, otherwise nullptr. const optimization_guide::proto::Hint* GetHintIfLoaded( const std::string& host);
diff --git a/components/previews/content/hint_cache_store.cc b/components/previews/content/hint_cache_store.cc index d5283d3d..6b78079e 100644 --- a/components/previews/content/hint_cache_store.cc +++ b/components/previews/content/hint_cache_store.cc
@@ -6,6 +6,8 @@ #include "base/bind.h" #include "base/metrics/histogram_macros.h" +#include "base/strings/string_number_conversions.h" +#include "base/strings/string_util.h" #include "components/leveldb_proto/public/proto_database_provider.h" #include "components/previews/content/proto/hint_cache.pb.h" @@ -42,7 +44,9 @@ kSchemaMetadataMissing = 2, kSchemaMetadataWrongVersion = 3, kComponentMetadataMissing = 4, - kMaxValue = kComponentMetadataMissing, + kFetchedMetadataMissing = 5, + kComponentAndFetchedMetadataMissing = 6, + kMaxValue = kComponentAndFetchedMetadataMissing, }; // Util class for recording the result of loading the metadata. The result is @@ -68,6 +72,12 @@ UMA_HISTOGRAM_ENUMERATION("Previews.HintCacheLevelDBStore.Status", status); } +// Returns true if |key_prefix| is a prefix of |key|. +bool DatabasePrefixFilter(const std::string& key_prefix, + const std::string& key) { + return base::StartsWith(key, key_prefix, base::CompareCase::SENSITIVE); +} + } // namespace HintCacheStore::HintCacheStore( @@ -142,11 +152,11 @@ } std::unique_ptr<HintCacheStore::ComponentUpdateData> -HintCacheStore::CreateUpdateDataForFetchedHints() const { +HintCacheStore::CreateUpdateDataForFetchedHints(base::Time update_time) const { // TODO(mcrouse): Currently returns a LevelDBComponentUpdateData, future - // refactor will create a LevelDBFetchedHintsData that will take a cache - // expiry time. The version for this object will be ignored. - return std::make_unique<LevelDBComponentUpdateData>(base::Version("0.0.1")); + // refactor will enable the construction of UpdateData with the settings + // necessary for the type of hint being updated, fetched or component. + return std::make_unique<LevelDBComponentUpdateData>(update_time); } void HintCacheStore::UpdateComponentData( @@ -209,6 +219,37 @@ weak_ptr_factory_.GetWeakPtr(), std::move(callback))); } +void HintCacheStore::UpdateFetchedHintsData( + std::unique_ptr<ComponentUpdateData> fetched_hints_data, + base::OnceClosure callback) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + DCHECK(fetched_hints_data); + DCHECK(!component_data_update_in_flight_); + + if (!IsAvailable()) { + std::move(callback).Run(); + return; + } + + fetched_update_time_ = fetched_hints_data->update_time(); + + component_data_update_in_flight_ = true; + + hint_entry_keys_.reset(); + + LevelDBComponentUpdateData* leveldb_fetched_hints_data = + static_cast<LevelDBComponentUpdateData*>(fetched_hints_data.get()); + + // This will remove the fetched metadata entry and insert all the entries + // currently in |leveldb_fetched_hints_data|. + database_->UpdateEntriesWithRemoveFilter( + std::move(leveldb_fetched_hints_data->entries_to_save_), + base::BindRepeating(&DatabasePrefixFilter, + GetMetadataTypeEntryKey(MetadataType::kFetched)), + base::BindOnce(&HintCacheStore::OnUpdateComponentData, + weak_ptr_factory_.GetWeakPtr(), std::move(callback))); +} + bool HintCacheStore::FindHintEntryKey(const std::string& host_suffix, EntryKey* out_hint_entry_key) const { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); @@ -240,6 +281,14 @@ hint_entry_key, std::move(callback))); } +base::Time HintCacheStore::FetchedHintsUpdateTime() const { + // If the store is not available, the metadata entries have not been loaded + // so there are no fetched hints. + if (!IsAvailable()) + return base::Time(); + return fetched_update_time_; +} + HintCacheStore::LevelDBComponentUpdateData::LevelDBComponentUpdateData( const base::Version& version) : ComponentUpdateData(version), @@ -254,6 +303,21 @@ std::move(metadata_component_entry)); } +HintCacheStore::LevelDBComponentUpdateData::LevelDBComponentUpdateData( + base::Time update_time) + : ComponentUpdateData(update_time), + component_hint_entry_key_prefix_(GetFetchedHintEntryKeyPrefix()), + entries_to_save_(std::make_unique<EntryVector>()) { + // Add fetched metadata entry + previews::proto::StoreEntry metadata_fetched_entry; + metadata_fetched_entry.set_update_time_secs( + update_time.ToDeltaSinceWindowsEpoch().InSeconds()); + + entries_to_save_->emplace_back( + GetMetadataTypeEntryKey(MetadataType::kFetched), + std::move(metadata_fetched_entry)); +} + HintCacheStore::LevelDBComponentUpdateData::~LevelDBComponentUpdateData() = default; @@ -299,6 +363,12 @@ component_version.GetString() + kKeySectionDelimiter; } +// static +HintCacheStore::EntryKeyPrefix HintCacheStore::GetFetchedHintEntryKeyPrefix() { + return base::NumberToString(static_cast<int>(EntryType::kFetchedHint)) + + kKeySectionDelimiter; +} + void HintCacheStore::UpdateStatus(Status new_status) { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); @@ -483,6 +553,7 @@ // If the component metadata entry exists, then use it to set the component // version. + bool component_metadata_missing = false; auto component_entry = metadata_entries->find(GetMetadataTypeEntryKey(MetadataType::kComponent)); if (component_entry != metadata_entries->end()) { @@ -491,6 +562,26 @@ } else { result_recorder.set_result(PreviewsHintCacheLevelDBStoreLoadMetadataResult:: kComponentMetadataMissing); + component_metadata_missing = true; + } + + auto fetched_entry = + metadata_entries->find(GetMetadataTypeEntryKey(MetadataType::kFetched)); + if (fetched_entry != metadata_entries->end()) { + DCHECK(fetched_entry->second.has_update_time_secs()); + fetched_update_time_ = base::Time::FromDeltaSinceWindowsEpoch( + base::TimeDelta::FromSeconds(fetched_entry->second.update_time_secs())); + } else { + if (component_metadata_missing) { + result_recorder.set_result( + PreviewsHintCacheLevelDBStoreLoadMetadataResult:: + kComponentAndFetchedMetadataMissing); + } else { + result_recorder.set_result( + PreviewsHintCacheLevelDBStoreLoadMetadataResult:: + kFetchedMetadataMissing); + } + fetched_update_time_ = base::Time(); } UpdateStatus(Status::kAvailable);
diff --git a/components/previews/content/hint_cache_store.h b/components/previews/content/hint_cache_store.h index c5310dd5..224d315 100644 --- a/components/previews/content/hint_cache_store.h +++ b/components/previews/content/hint_cache_store.h
@@ -71,9 +71,12 @@ : version_(version) { DCHECK(version_.IsValid()); } + explicit ComponentUpdateData(base::Time update_time) + : update_time_(update_time) {} virtual ~ComponentUpdateData() = default; const base::Version& version() const { return version_; } + base::Time update_time() const { return update_time_; } // Pure virtual function for moving a hint into ComponentUpdateData. After // MoveHintIntoUpdateData() is called, |hint| is no longer valid. @@ -83,6 +86,9 @@ private: // The component version of the update data. base::Version version_; + + // The time when hints in the update data need to be updated. + base::Time update_time_; }; HintCacheStore(const base::FilePath& database_dir, @@ -114,7 +120,7 @@ // Service so the store can expire old hints, remove hints specified by the // server, and store the fresh hints. std::unique_ptr<HintCacheStore::ComponentUpdateData> - CreateUpdateDataForFetchedHints() const; + CreateUpdateDataForFetchedHints(base::Time update_time) const; // Updates the component data (both version and hints) contained within the // store. When this is called, all pre-existing component data within the @@ -124,6 +130,18 @@ void UpdateComponentData(std::unique_ptr<ComponentUpdateData> component_data, base::OnceClosure callback); + // Updates the fetched hints data contained in the store, including the + // metadata entry. The callback is run asynchronously after the database + // stores the hints. + // + // TODO(mcrouse): When called, fetched hint data in the store that has expired + // specified by |expiry_time_secs| will be purged and only the new hints and + // non-expired hints are retained. + + void UpdateFetchedHintsData( + std::unique_ptr<ComponentUpdateData> fetched_hints_data, + base::OnceClosure callback); + // Finds a hint entry key associated with the specified host suffix. Returns // true if a hint entry key is found, in which case |out_hint_entry_key| is // populated with the key. @@ -137,6 +155,10 @@ // asynchronous. void LoadHint(const EntryKey& hint_entry_key, HintLoadedCallback callback); + // Returns the time that the fetched hints in the store can be updated. If + // |this| is not available, base::Time() is returned. + base::Time FetchedHintsUpdateTime() const; + private: friend class HintCacheStoreTest; friend class HintUpdateData; @@ -165,6 +187,7 @@ enum class EntryType { kMetadata = 1, kComponentHint = 2, + kFetchedHint = 3, }; // Metadata types within the store. The metadata type appears at the end of @@ -178,17 +201,23 @@ enum class MetadataType { kSchema = 1, kComponent = 2, + kFetched = 3, }; // HintCacheStore's concrete implementation of ComponentUpdateData. - // LevelDBComponentUpdateData is private within HintCacheStore. All - // classes outside of HintCacheStore can only interact with the - // ComponentUpdateData base class. LevelDBComponentUpdateData is created by - // HintCacheStore when MaybeCreateComponentUpdateData() is called and - // used to update the store's component data during UpdateComponentData(). + // LevelDBComponentUpdateData is private within HintCacheStore. All classes + // outside of HintCacheStore can only interact with the ComponentUpdateData + // base class. LevelDBComponentUpdateData is created by HintCacheStore when + // MaybeCreateComponentUpdateData() is called and used to update the store's + // component data during UpdateComponentData(). + // + // TODO(mcrouse): Bug: 932707. + // This class should be refactored so that there is a single constructor and + // the base class removed. The class will also be moved out of |this|. class LevelDBComponentUpdateData : public ComponentUpdateData { public: explicit LevelDBComponentUpdateData(const base::Version& version); + explicit LevelDBComponentUpdateData(base::Time update_time); ~LevelDBComponentUpdateData() override; // ComponentUpdateData overrides: @@ -227,6 +256,9 @@ static EntryKeyPrefix GetComponentHintEntryKeyPrefix( const base::Version& component_version); + // Returns prefix of the key of every fetched hint entry: "3_". + static EntryKeyPrefix GetFetchedHintEntryKeyPrefix(); + // Updates the status of the store to the specified value, validates the // transition, and destroys the database in the case where the status // transitions to Status::kFailed. @@ -331,6 +363,10 @@ // is true, keys and hints will not be returned by the store. bool component_data_update_in_flight_; + // The next update time for the fetched hints that are currently in the + // store. + base::Time fetched_update_time_; + // The keys of the hints available within the store. std::unique_ptr<EntryKeySet> hint_entry_keys_;
diff --git a/components/previews/content/hint_cache_store_unittest.cc b/components/previews/content/hint_cache_store_unittest.cc index 3dc531ba..0541e5d4 100644 --- a/components/previews/content/hint_cache_store_unittest.cc +++ b/components/previews/content/hint_cache_store_unittest.cc
@@ -57,7 +57,9 @@ // Initializes the entries contained within the database on startup. void SeedInitialData( MetadataSchemaState state, - base::Optional<size_t> component_hint_count = base::Optional<size_t>()) { + base::Optional<size_t> component_hint_count = base::Optional<size_t>(), + base::Optional<base::Time> fetched_hints_update = + base::Optional<base::Time>()) { db_store_.clear(); // Add a metadata schema entry if its state isn't kMissing. The version @@ -76,7 +78,9 @@ // If the database is being seeded with component hints, it is indicated // with a provided count. Add the component metadata with the default // component version and then add the indicated number of component hints. - if (component_hint_count) { + // if (component_hint_count && component_hint_count > + // static_cast<size_t>(0)) { + if (component_hint_count && component_hint_count > 0u) { db_store_[HintCacheStore::GetMetadataTypeEntryKey( HintCacheStore::MetadataType::kComponent)] .set_version(kDefaultComponentVersion); @@ -93,6 +97,12 @@ page_hint->set_page_pattern("page pattern " + std::to_string(i)); } } + if (fetched_hints_update) { + db_store_[HintCacheStore::GetMetadataTypeEntryKey( + HintCacheStore::MetadataType::kFetched)] + .set_update_time_secs( + fetched_hints_update->ToDeltaSinceWindowsEpoch().InSeconds()); + } } // Moves the specified number of component hints into the update data. @@ -173,6 +183,23 @@ HintCacheStore::MetadataType::kSchema)); } + // Verifies that the fetched metadata has the expected next update time. + void ExpectFetchedMetadata(base::Time update_time) const { + const auto& metadata_entry = + db_store_.find(HintCacheStore::GetMetadataTypeEntryKey( + HintCacheStore::MetadataType::kFetched)); + if (metadata_entry != db_store_.end()) { + // The next update time should have same time up to the second as the + // metadata entry is stored in seconds. + EXPECT_TRUE( + base::Time::FromDeltaSinceWindowsEpoch(base::TimeDelta::FromSeconds( + metadata_entry->second.update_time_secs())) - + update_time < + base::TimeDelta::FromSeconds(1)); + } else { + FAIL() << "No fetched metadata found"; + } + } // Verifies that the component metadata has the expected version and all // expected component hints are present. void ExpectComponentHintsPresent(const std::string& version, @@ -437,7 +464,7 @@ TEST_F(HintCacheStoreTest, InitializeFailedOnLoadHintEntryKeysWithInitialData) { base::HistogramTester histogram_tester; - SeedInitialData(MetadataSchemaState::kValid, 10); + SeedInitialData(MetadataSchemaState::kValid, 10, base::Time().Now()); CreateDatabase(); InitializeDatabase(true /*=success*/); @@ -535,7 +562,13 @@ histogram_tester.ExpectBucketCount( "Previews.HintCacheLevelDBStore.LoadMetadataResult", - 4 /* kComponentMetadataMissing */, 1); + 4 /* kComponentMetadataMissing*/, 0); + histogram_tester.ExpectBucketCount( + "Previews.HintCacheLevelDBStore.LoadMetadataResult", + 5 /* kFetchedMetadataMissing*/, 0); + histogram_tester.ExpectBucketCount( + "Previews.HintCacheLevelDBStore.LoadMetadataResult", + 6 /* kComponentAndFetchedMetadataMissing*/, 1); histogram_tester.ExpectBucketCount("Previews.HintCacheLevelDBStore.Status", 0 /* kUninitialized */, 1); @@ -610,6 +643,38 @@ MetadataSchemaState schema_state = MetadataSchemaState::kValid; size_t component_hint_count = 10; + SeedInitialData(schema_state, component_hint_count, base::Time().Now()); + CreateDatabase(); + InitializeStore(schema_state); + + // The store should contain the schema metadata entry, the component metadata + // entry, and all of the initial component hints. + EXPECT_EQ(GetDBStoreEntryCount(), + static_cast<size_t>(component_hint_count + 3)); + EXPECT_EQ(GetStoreHintEntryKeyCount(), component_hint_count); + + EXPECT_TRUE(IsMetadataSchemaEntryKeyPresent()); + ExpectComponentHintsPresent(kDefaultComponentVersion, component_hint_count); + + histogram_tester.ExpectBucketCount( + "Previews.HintCacheLevelDBStore.LoadMetadataResult", 0 /* kSuccess */, 1); + + histogram_tester.ExpectBucketCount("Previews.HintCacheLevelDBStore.Status", + 0 /* kUninitialized */, 1); + histogram_tester.ExpectBucketCount("Previews.HintCacheLevelDBStore.Status", + 1 /* kInitializing */, 1); + histogram_tester.ExpectBucketCount("Previews.HintCacheLevelDBStore.Status", + 2 /* kAvailable */, 1); + histogram_tester.ExpectBucketCount("Previews.HintCacheLevelDBStore.Status", + 3 /* kFailed */, 0); +} + +TEST_F(HintCacheStoreTest, + InitializeSucceededWithValidSchemaEntryAndComponentDataOnly) { + base::HistogramTester histogram_tester; + + MetadataSchemaState schema_state = MetadataSchemaState::kValid; + size_t component_hint_count = 10; SeedInitialData(schema_state, component_hint_count); CreateDatabase(); InitializeStore(schema_state); @@ -624,7 +689,46 @@ ExpectComponentHintsPresent(kDefaultComponentVersion, component_hint_count); histogram_tester.ExpectBucketCount( - "Previews.HintCacheLevelDBStore.LoadMetadataResult", 0 /* kSuccess */, 1); + "Previews.HintCacheLevelDBStore.LoadMetadataResult", + 4 /* kComponentMetadataMissing*/, 0); + histogram_tester.ExpectBucketCount( + "Previews.HintCacheLevelDBStore.LoadMetadataResult", + 5 /* kFetchedMetadataMissing*/, 1); + histogram_tester.ExpectBucketCount( + "Previews.HintCacheLevelDBStore.LoadMetadataResult", + 6 /* kComponentAndFetchedMetadataMissing*/, 0); + + histogram_tester.ExpectBucketCount("Previews.HintCacheLevelDBStore.Status", + 0 /* kUninitialized */, 1); + histogram_tester.ExpectBucketCount("Previews.HintCacheLevelDBStore.Status", + 1 /* kInitializing */, 1); + histogram_tester.ExpectBucketCount("Previews.HintCacheLevelDBStore.Status", + 2 /* kAvailable */, 1); + histogram_tester.ExpectBucketCount("Previews.HintCacheLevelDBStore.Status", + 3 /* kFailed */, 0); +} + +TEST_F(HintCacheStoreTest, + InitializeSucceededWithValidSchemaEntryAndFetchedMetaData) { + base::HistogramTester histogram_tester; + + MetadataSchemaState schema_state = MetadataSchemaState::kValid; + size_t component_hint_count = 0; + SeedInitialData(schema_state, component_hint_count, base::Time().Now()); + CreateDatabase(); + InitializeStore(schema_state); + + // The store should contain the schema metadata entry, the component metadata + // entry, and all of the initial component hints. + EXPECT_EQ(GetDBStoreEntryCount(), + static_cast<size_t>(component_hint_count + 2)); + EXPECT_EQ(GetStoreHintEntryKeyCount(), component_hint_count); + + EXPECT_TRUE(IsMetadataSchemaEntryKeyPresent()); + + histogram_tester.ExpectBucketCount( + "Previews.HintCacheLevelDBStore.LoadMetadataResult", + 4 /* kComponentMetadataMissing*/, 1); histogram_tester.ExpectBucketCount("Previews.HintCacheLevelDBStore.Status", 0 /* kUninitialized */, 1); @@ -1010,4 +1114,13 @@ } } +TEST_F(HintCacheStoreTest, FetchedHintsMetadataStored) { + MetadataSchemaState schema_state = MetadataSchemaState::kValid; + base::Time update_time = base::Time().Now(); + SeedInitialData(schema_state, 10, update_time); + CreateDatabase(); + InitializeStore(schema_state); + + ExpectFetchedMetadata(update_time); +} } // namespace previews
diff --git a/components/previews/content/hint_cache_unittest.cc b/components/previews/content/hint_cache_unittest.cc index 8415412..fa28b9c 100644 --- a/components/previews/content/hint_cache_unittest.cc +++ b/components/previews/content/hint_cache_unittest.cc
@@ -59,13 +59,16 @@ loaded_hint_ = nullptr; is_store_initialized_ = false; is_component_data_updated_ = false; - on_load_hint_callback_called = false; + on_load_hint_callback_called_ = false; + is_fetched_data_stored_ = false; RunUntilIdle(); } HintCache* hint_cache() { return hint_cache_.get(); } + bool is_fetched_data_stored() { return is_fetched_data_stored_; } + // Updates the cache with |component_data| and waits for callback indicating // that the update is complete. void UpdateComponentData( @@ -82,21 +85,27 @@ bool StoreFetchedHints( std::unique_ptr<optimization_guide::proto::GetHintsResponse> - get_hints_response) { - bool result = hint_cache_->StoreFetchedHints(std::move(get_hints_response)); + get_hints_response, + base::Time stored_time) { + is_fetched_data_stored_ = false; + bool result = hint_cache_->StoreFetchedHints( + std::move(get_hints_response), stored_time, + base::BindOnce(&HintCacheTest::OnHintStored, base::Unretained(this))); RunUntilIdle(); return result; } + void OnHintStored() { is_fetched_data_stored_ = true; } + // Loads hint for the specified host from the cache and waits for callback // indicating that loading the hint is complete. void LoadHint(const std::string& host) { - on_load_hint_callback_called = false; + on_load_hint_callback_called_ = false; loaded_hint_ = nullptr; hint_cache_->LoadHint(host, base::BindOnce(&HintCacheTest::OnLoadHint, base::Unretained(this))); - while (!on_load_hint_callback_called) { + while (!on_load_hint_callback_called_) { RunUntilIdle(); } } @@ -114,7 +123,7 @@ void OnStoreInitialized() { is_store_initialized_ = true; } void OnUpdateComponentData() { is_component_data_updated_ = true; } void OnLoadHint(const optimization_guide::proto::Hint* hint) { - on_load_hint_callback_called = true; + on_load_hint_callback_called_ = true; loaded_hint_ = hint; } @@ -126,7 +135,8 @@ bool is_store_initialized_; bool is_component_data_updated_; - bool on_load_hint_callback_called; + bool on_load_hint_callback_called_; + bool is_fetched_data_stored_; DISALLOW_COPY_AND_ASSIGN(HintCacheTest); }; @@ -486,10 +496,13 @@ EXPECT_EQ(hint_key, GetLoadedHint()->key()); } -TEST_F(HintCacheTest, ParseValidFetchedHints) { +TEST_F(HintCacheTest, StoreValidFetchedHints) { const int kMemoryCacheSize = 5; CreateAndInitializeHintCache(kMemoryCacheSize); + // Default update time for empty hint cache store is base::Time(). + EXPECT_EQ(hint_cache()->FetchedHintsUpdateTime(), base::Time()); + std::unique_ptr<optimization_guide::proto::GetHintsResponse> get_hints_response = std::make_unique<optimization_guide::proto::GetHintsResponse>(); @@ -500,7 +513,12 @@ optimization_guide::proto::PageHint* page_hint = hint->add_page_hints(); page_hint->set_page_pattern("page pattern"); - EXPECT_TRUE(StoreFetchedHints(std::move(get_hints_response))); + base::Time stored_time = base::Time().Now(); + EXPECT_TRUE(StoreFetchedHints(std::move(get_hints_response), stored_time)); + EXPECT_TRUE(is_fetched_data_stored()); + + // Next update time for hints should be updated. + EXPECT_EQ(hint_cache()->FetchedHintsUpdateTime(), stored_time); } TEST_F(HintCacheTest, ParseEmptyFetchedHints) { @@ -511,7 +529,9 @@ get_hints_response = std::make_unique<optimization_guide::proto::GetHintsResponse>(); - EXPECT_FALSE(StoreFetchedHints(std::move(get_hints_response))); + EXPECT_FALSE( + StoreFetchedHints(std::move(get_hints_response), base::Time().Now())); + EXPECT_FALSE(is_fetched_data_stored()); } } // namespace
diff --git a/components/previews/content/hints_fetcher.cc b/components/previews/content/hints_fetcher.cc index fe5f2e36..c6b54ba 100644 --- a/components/previews/content/hints_fetcher.cc +++ b/components/previews/content/hints_fetcher.cc
@@ -144,6 +144,8 @@ if (net_status == net::OK && response_code == net::HTTP_OK && get_hints_response->ParseFromString(get_hints_response_data)) { std::move(hints_fetched_callback_).Run(std::move(get_hints_response)); + } else { + std::move(hints_fetched_callback_).Run(base::nullopt); } }
diff --git a/components/previews/content/hints_fetcher.h b/components/previews/content/hints_fetcher.h index 9a8258e..1df02b5 100644 --- a/components/previews/content/hints_fetcher.h +++ b/components/previews/content/hints_fetcher.h
@@ -12,6 +12,7 @@ #include "base/callback.h" #include "base/macros.h" #include "base/memory/scoped_refptr.h" +#include "base/optional.h" #include "base/sequence_checker.h" #include "components/optimization_guide/proto/hints.pb.h" #include "components/previews/core/previews_experiments.h" @@ -35,19 +36,21 @@ // to pass back the fetched hints response from the remote Optimization Guide // Service. using HintsFetchedCallback = base::OnceCallback<void( - std::unique_ptr<optimization_guide::proto::GetHintsResponse>)>; + base::Optional< + std::unique_ptr<optimization_guide::proto::GetHintsResponse>>)>; public: HintsFetcher( scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory, GURL optimization_guide_service_url); - ~HintsFetcher(); + virtual ~HintsFetcher(); - // Requests hints from the Optimization Guide Service if a request for - // them is not already in progress. Returns whether a new request was - // issued. |hints_fetched_callback| is only run if a fetch was successful - // and GetHintsResponse can be returned. - bool FetchOptimizationGuideServiceHints( + // Requests hints from the Optimization Guide Service if a request for them is + // not already in progress. Returns whether a new request was issued. + // |hints_fetched_callback| is run, passing a GetHintsResponse object, if a + // fetch was successful or passes nullopt if the fetch fails. Virtualized for + // testing. + virtual bool FetchOptimizationGuideServiceHints( const std::vector<std::string>& hosts, HintsFetchedCallback hints_fetched_callback);
diff --git a/components/previews/content/hints_fetcher_unittest.cc b/components/previews/content/hints_fetcher_unittest.cc index b1dd3dd7..71042934 100644 --- a/components/previews/content/hints_fetcher_unittest.cc +++ b/components/previews/content/hints_fetcher_unittest.cc
@@ -9,6 +9,7 @@ #include "base/callback.h" #include "base/macros.h" #include "base/memory/scoped_refptr.h" +#include "base/optional.h" #include "base/run_loop.h" #include "base/test/scoped_feature_list.h" #include "base/test/scoped_task_environment.h" @@ -42,12 +43,14 @@ ~HintsFetcherTest() override {} void OnHintsFetched( - std::unique_ptr<optimization_guide::proto::GetHintsResponse> + base::Optional< + std::unique_ptr<optimization_guide::proto::GetHintsResponse>> get_hints_response) { - hints_fetched_ = true; + if (get_hints_response) + hints_fetched_ = true; } - bool HintsFetched() { return hints_fetched_; } + bool hints_fetched() { return hints_fetched_; } protected: bool FetchHints(const std::vector<std::string>& hosts) { @@ -99,7 +102,7 @@ EXPECT_TRUE(FetchHints(std::vector<std::string>())); VerifyHasPendingFetchRequests(); EXPECT_TRUE(SimulateResponse(response_content, net::HTTP_OK)); - EXPECT_TRUE(HintsFetched()); + EXPECT_TRUE(hints_fetched()); } // Tests to ensure that multiple hint fetches by the same object cannot be in @@ -124,7 +127,7 @@ // Send a 404 to HintsFetcher. SimulateResponse(response_content, net::HTTP_NOT_FOUND); - EXPECT_FALSE(HintsFetched()); + EXPECT_FALSE(hints_fetched()); } TEST_F(HintsFetcherTest, FetchReturnBadResponse) { @@ -132,7 +135,7 @@ EXPECT_TRUE(FetchHints(std::vector<std::string>())); VerifyHasPendingFetchRequests(); EXPECT_TRUE(SimulateResponse(response_content, net::HTTP_OK)); - EXPECT_FALSE(HintsFetched()); + EXPECT_FALSE(hints_fetched()); } } // namespace previews
diff --git a/components/previews/content/previews_decider_impl_unittest.cc b/components/previews/content/previews_decider_impl_unittest.cc index 2612a1d..0d2cd87 100644 --- a/components/previews/content/previews_decider_impl_unittest.cc +++ b/components/previews/content/previews_decider_impl_unittest.cc
@@ -22,6 +22,7 @@ #include "base/metrics/field_trial_params.h" #include "base/run_loop.h" #include "base/single_thread_task_runner.h" +#include "base/task/post_task.h" #include "base/test/metrics/histogram_tester.h" #include "base/test/scoped_command_line.h" #include "base/test/scoped_feature_list.h" @@ -151,11 +152,13 @@ TestPreviewsOptimizationGuide( optimization_guide::OptimizationGuideService* optimization_guide_service, const scoped_refptr<base::SingleThreadTaskRunner>& ui_task_runner, + const scoped_refptr<base::SequencedTaskRunner>& background_task_runner, const base::FilePath& test_path, PreviewsTopHostProvider* previews_top_host_provider, scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory) : PreviewsOptimizationGuide(optimization_guide_service, ui_task_runner, + background_task_runner, test_path, previews_top_host_provider, url_loader_factory) {} @@ -402,6 +405,8 @@ std::make_unique<TestPreviewsOptimizationGuide>( &optimization_guide_service_, scoped_task_environment_.GetMainThreadTaskRunner(), + base::CreateSequencedTaskRunnerWithTraits( + {base::MayBlock(), base::TaskPriority::BEST_EFFORT}), temp_dir_.GetPath(), &previews_top_host_provider_, url_loader_factory_), base::BindRepeating(&IsPreviewFieldTrialEnabled),
diff --git a/components/previews/content/previews_optimization_guide.cc b/components/previews/content/previews_optimization_guide.cc index 8e32ca6..98369b9 100644 --- a/components/previews/content/previews_optimization_guide.cc +++ b/components/previews/content/previews_optimization_guide.cc
@@ -9,8 +9,10 @@ #include "base/command_line.h" #include "base/files/file_path.h" #include "base/metrics/histogram_macros.h" +#include "base/rand_util.h" #include "base/task/post_task.h" #include "base/task_runner_util.h" +#include "base/time/default_clock.h" #include "components/optimization_guide/hints_component_info.h" #include "components/optimization_guide/optimization_guide_service.h" #include "components/optimization_guide/proto/hints.pb.h" @@ -34,6 +36,15 @@ // will have a newer version than it. constexpr char kManualConfigComponentVersion[] = "0.0.0"; +// Delay between retries on failed fetch and store of hints from the remote +// Optimization Guide Service. +constexpr base::TimeDelta kFetchRetryDelay = base::TimeDelta::FromMinutes(15); + +// Delay until successfully fetched hints should be updated by requesting from +// the remote Optimization Guide Service. +constexpr base::TimeDelta kUpdateFetchedHintsDelay = + base::TimeDelta::FromHours(24); + // Hints are purged during startup if the explicit purge switch exists or if // a proto override is being used--in which case the hints need to come from the // override instead. @@ -79,25 +90,60 @@ return proto_configuration; } +// Parses a list of hosts to have hints fetched for. This overrides scheduling +// of the first hints fetch and forces it to occur immediately. If no hosts are +// provided, nullopt is returned. +base::Optional<std::vector<std::string>> +ParseHintsFetchOverrideFromCommandLine() { + base::CommandLine* cmd_line = base::CommandLine::ForCurrentProcess(); + if (!cmd_line->HasSwitch(switches::kFetchHintsOverride)) + return base::nullopt; + + std::string override_hosts_value = + cmd_line->GetSwitchValueASCII(switches::kFetchHintsOverride); + + std::vector<std::string> hosts = + base::SplitString(override_hosts_value, ",", base::TRIM_WHITESPACE, + base::SPLIT_WANT_NONEMPTY); + + if (hosts.size() == 0) + return base::nullopt; + + return hosts; +} + +// Provides a random time delta in seconds between |kFetchRandomMinDelay| and +// |kFetchRandomMaxDelay|. +base::TimeDelta RandomFetchDelay() { + constexpr int kFetchRandomMinDelaySecs = 30; + constexpr int kFetchRandomMaxDelaySecs = 60; + return base::TimeDelta::FromSeconds( + base::RandInt(kFetchRandomMinDelaySecs, kFetchRandomMaxDelaySecs)); +} + } // namespace PreviewsOptimizationGuide::PreviewsOptimizationGuide( optimization_guide::OptimizationGuideService* optimization_guide_service, const scoped_refptr<base::SingleThreadTaskRunner>& ui_task_runner, + const scoped_refptr<base::SequencedTaskRunner>& background_task_runner, const base::FilePath& profile_path, PreviewsTopHostProvider* previews_top_host_provider, scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory) : optimization_guide_service_(optimization_guide_service), ui_task_runner_(ui_task_runner), - background_task_runner_(base::CreateSequencedTaskRunnerWithTraits( - {base::MayBlock(), base::TaskPriority::BEST_EFFORT})), + background_task_runner_(background_task_runner), hint_cache_(std::make_unique<HintCache>( std::make_unique<HintCacheStore>(profile_path, background_task_runner_))), previews_top_host_provider_(previews_top_host_provider), + time_clock_(base::DefaultClock::GetInstance()), url_loader_factory_(url_loader_factory), ui_weak_ptr_factory_(this) { DCHECK(optimization_guide_service_); + // TODO(mcrouse): This needs to be a pref to persist the last fetch attempt + // time and prevent crash loops. + last_fetch_attempt_ = base::Time(); hint_cache_->Initialize( ShouldPurgeHintCacheStoreOnStartup(), base::BindOnce(&PreviewsOptimizationGuide::OnHintCacheInitialized, @@ -259,30 +305,53 @@ } void PreviewsOptimizationGuide::FetchHints() { - std::vector<std::string> top_hosts = previews_top_host_provider_->GetTopHosts( - previews::params::MaxHostsForOptimizationGuideServiceHintsFetch()); + base::Optional<std::vector<std::string>> top_hosts = + ParseHintsFetchOverrideFromCommandLine(); + if (!top_hosts) { + top_hosts = previews_top_host_provider_->GetTopHosts( + previews::params::MaxHostsForOptimizationGuideServiceHintsFetch()); + } DCHECK_GE(previews::params::MaxHostsForOptimizationGuideServiceHintsFetch(), - top_hosts.size()); + top_hosts->size()); + if (!hints_fetcher_) { hints_fetcher_ = std::make_unique<HintsFetcher>( url_loader_factory_, params::GetOptimizationGuideServiceURL()); } - hints_fetcher_->FetchOptimizationGuideServiceHints( - top_hosts, base::BindOnce(&PreviewsOptimizationGuide::OnHintsFetched, - ui_weak_ptr_factory_.GetWeakPtr())); + if (top_hosts->size() > 0) { + hints_fetcher_->FetchOptimizationGuideServiceHints( + *top_hosts, base::BindOnce(&PreviewsOptimizationGuide::OnHintsFetched, + ui_weak_ptr_factory_.GetWeakPtr())); + } } void PreviewsOptimizationGuide::OnHintsFetched( - std::unique_ptr<optimization_guide::proto::GetHintsResponse> + base::Optional<std::unique_ptr<optimization_guide::proto::GetHintsResponse>> get_hints_response) { - DCHECK(get_hints_response); - // TODO(mcrouse): this will be dropped into a backgroundtask as it will likely // be intensive/slow storing hints. - // The callback will be UpdateHints(). + if (get_hints_response) { + hint_cache_->StoreFetchedHints( + std::move(*get_hints_response), + time_clock_->Now() + kUpdateFetchedHintsDelay, + base::BindOnce(&PreviewsOptimizationGuide::OnFetchedHintsStored, + ui_weak_ptr_factory_.GetWeakPtr())); + } else { + // The fetch did not succeed so we will schedule to retry the fetch in + // after delaying for |kFetchRetryDelay| + // TODO(mcrouse): When the store is refactored from closures, the timer will + // be scheduled on failure of the store instead. + hints_fetch_timer_.Start(FROM_HERE, kFetchRetryDelay, this, + &PreviewsOptimizationGuide::ScheduleHintsFetch); + } +} - hint_cache_->StoreFetchedHints(std::move(get_hints_response)); +void PreviewsOptimizationGuide::OnFetchedHintsStored() { + hints_fetch_timer_.Stop(); + hints_fetch_timer_.Start( + FROM_HERE, hint_cache_->FetchedHintsUpdateTime() - time_clock_->Now(), + this, &PreviewsOptimizationGuide::ScheduleHintsFetch); } void PreviewsOptimizationGuide::UpdateHints( @@ -322,12 +391,62 @@ // notification needs to be shown to the user. if (previews::params::IsHintsFetchingEnabled()) { - // TODO(mcrouse): On initialize, we should check if hints have been fetched - // recently. We will also schedule this to be called on a timer. - FetchHints(); + if (ParseHintsFetchOverrideFromCommandLine()) { + // Skip the fetch scheduling logic and perform a hints fetch immediately + // after initialization. + last_fetch_attempt_ = time_clock_->Now(); + FetchHints(); + } else { + ScheduleHintsFetch(); + } } } +void PreviewsOptimizationGuide::ScheduleHintsFetch() { + DCHECK(!hints_fetch_timer_.IsRunning()); + + const base::TimeDelta time_until_update_time = + hint_cache_->FetchedHintsUpdateTime() - time_clock_->Now(); + const base::TimeDelta time_until_retry = + last_fetch_attempt_ + kFetchRetryDelay - time_clock_->Now(); + base::TimeDelta fetcher_delay; + if (time_until_update_time <= base::TimeDelta() && + time_until_retry <= base::TimeDelta()) { + // Fetched hints in the store should be updated and an attempt has not been + // made in last |kFetchRetryDelay|. + last_fetch_attempt_ = time_clock_->Now(); + hints_fetch_timer_.Start(FROM_HERE, RandomFetchDelay(), this, + &PreviewsOptimizationGuide::FetchHints); + } else { + if (time_until_update_time >= base::TimeDelta()) { + // If the fetched hints in the store are still up-to-date, set a timer for + // when the hints need to be updated. + fetcher_delay = time_until_update_time; + } else { + // Otherwise, hints need to be updated but an attempt was made in last + // |kFetchRetryDelay|. Schedule the timer for after the retry + // delay. + fetcher_delay = time_until_retry; + } + hints_fetch_timer_.Start(FROM_HERE, fetcher_delay, this, + &PreviewsOptimizationGuide::ScheduleHintsFetch); + } +} + +void PreviewsOptimizationGuide::SetTimeClockForTesting( + const base::Clock* time_clock) { + time_clock_ = time_clock; +} + +void PreviewsOptimizationGuide::SetHintsFetcherForTesting( + std::unique_ptr<previews::HintsFetcher> hints_fetcher) { + hints_fetcher_ = std::move(hints_fetcher); +} + +HintsFetcher* PreviewsOptimizationGuide::GetHintsFetcherForTesting() { + return hints_fetcher_.get(); +} + void PreviewsOptimizationGuide::ListenForNextUpdateForTesting( base::OnceClosure next_update_closure) { DCHECK(next_update_closure_.is_null())
diff --git a/components/previews/content/previews_optimization_guide.h b/components/previews/content/previews_optimization_guide.h index 5a2d763..44fde91 100644 --- a/components/previews/content/previews_optimization_guide.h +++ b/components/previews/content/previews_optimization_guide.h
@@ -16,6 +16,8 @@ #include "base/memory/weak_ptr.h" #include "base/sequenced_task_runner.h" #include "base/single_thread_task_runner.h" +#include "base/time/clock.h" +#include "base/timer/timer.h" #include "components/optimization_guide/optimization_guide_service_observer.h" #include "components/previews/content/hint_cache.h" #include "components/previews/core/previews_experiments.h" @@ -34,6 +36,7 @@ class Hint; } // namespace proto } // namespace optimization_guide + namespace previews { class HintsFetcher; @@ -51,6 +54,7 @@ PreviewsOptimizationGuide( optimization_guide::OptimizationGuideService* optimization_guide_service, const scoped_refptr<base::SingleThreadTaskRunner>& ui_task_runner, + const scoped_refptr<base::SequencedTaskRunner>& background_task_runner, const base::FilePath& profile_path, PreviewsTopHostProvider* previews_top_host_provider, scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory); @@ -110,19 +114,42 @@ bool has_hints() const { return !!hints_; } + // Set |time_clock_| for testing. + void SetTimeClockForTesting(const base::Clock* time_clock); + + // Set |hints_fetcher_| for testing. + void SetHintsFetcherForTesting( + std::unique_ptr<previews::HintsFetcher> hints_fetcher); + + HintsFetcher* GetHintsFetcherForTesting(); + + // Called when the hints store is initialized to determine when hints + // should be fetched and schedules the |hints_fetch_timer_| to fire based on: + // 1. The update time for the fetched hints in the store and + // 2. The last time a fetch attempt was made, |last_fetch_attempt_|. + // TODONOW(mcrouse) : confirm is this is ok or not. + void ScheduleHintsFetch(); + + protected: + // Callback executed after remote hints have been fetched and returned from + // the remote Optimization Guide Service. At this point, the hints response + // is ready to be processed and stored for use. Virtual to be mocked in + // testing. + virtual void OnHintsFetched( + base::Optional< + std::unique_ptr<optimization_guide::proto::GetHintsResponse>> + get_hints_response); + + // Callback executed after the Hints have been successfully stored in the + // store. Virtual to be mocked in tests. + virtual void OnFetchedHintsStored(); + private: // Callback run after the hint cache is fully initialized. At this point, the // PreviewsOptimizationGuide is ready to process components from the // OptimizationGuideService and registers as an observer with it. void OnHintCacheInitialized(); - // Callback executed after remote hints have been fetched and returned from - // the remote Optimization Guide Service. At this point, the hints response - // is ready to be processed and stored for use. - void OnHintsFetched( - std::unique_ptr<optimization_guide::proto::GetHintsResponse> - get_hints_response); - // Called when the hints have been fully updated with the latest hints from // the Component Updater. This is used as a signal during tests. // |update_closure| is called immediately if not null. @@ -162,9 +189,18 @@ // Optimization Guide Service. std::unique_ptr<HintsFetcher> hints_fetcher_; + // Timer to schedule when to fetch hints from the remote Optimization Guide + // Service. + base::OneShotTimer hints_fetch_timer_; + // TopHostProvider that this guide can query. Not owned. PreviewsTopHostProvider* previews_top_host_provider_ = nullptr; + // Clock used for scheduling the |hints_fetch_timer_|. + const base::Clock* time_clock_; + + base::Time last_fetch_attempt_; + // Used for fetching Hints by the Hints Fetcher. scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory_;
diff --git a/components/previews/content/previews_optimization_guide_unittest.cc b/components/previews/content/previews_optimization_guide_unittest.cc index 1b7fbda..11d21eae 100644 --- a/components/previews/content/previews_optimization_guide_unittest.cc +++ b/components/previews/content/previews_optimization_guide_unittest.cc
@@ -21,9 +21,11 @@ #include "base/test/metrics/histogram_tester.h" #include "base/test/scoped_feature_list.h" #include "base/test/scoped_task_environment.h" +#include "base/test/simple_test_clock.h" #include "components/optimization_guide/hints_component_info.h" #include "components/optimization_guide/optimization_guide_service.h" #include "components/optimization_guide/proto/hints.pb.h" +#include "components/previews/content/hints_fetcher.h" #include "components/previews/content/previews_hints.h" #include "components/previews/content/previews_top_host_provider.h" #include "components/previews/content/previews_user_data.h" @@ -43,6 +45,19 @@ namespace { // A fake default page_id for testing. const uint64_t kDefaultPageId = 123456; + +enum class HintsFetcherEndState { + kFetchFailed = 0, + kFetchSuccessWithHints = 1, + kFetchSuccessWithNoHints = 2, +}; + +// Retry delay is 16 minutes to allow for kFetchRetryDelaySecs + +// kFetchRandomMaxDelaySecs to pass. +constexpr int kTestFetchRetryDelaySecs = 60 * 16; + +constexpr int kUpdateFetchHintsTimeSecs = 24 * 60 * 60; // 24 hours. + } // namespace class TestOptimizationGuideService @@ -78,9 +93,106 @@ MOCK_CONST_METHOD1(GetTopHosts, std::vector<std::string>(size_t max_sites)); }; +std::unique_ptr<optimization_guide::proto::GetHintsResponse> BuildHintsResponse( + std::vector<std::string> hosts) { + std::unique_ptr<optimization_guide::proto::GetHintsResponse> + get_hints_response = + std::make_unique<optimization_guide::proto::GetHintsResponse>(); + + for (const auto& host : hosts) { + optimization_guide::proto::Hint* hint = get_hints_response->add_hints(); + hint->set_key_representation(optimization_guide::proto::HOST_SUFFIX); + hint->set_key(host); + optimization_guide::proto::PageHint* page_hint = hint->add_page_hints(); + page_hint->set_page_pattern("page pattern"); + } + return get_hints_response; +} + +// A mock class implementation of HintsFetcher for unittesting +// previews_optimization_guide. +class TestHintsFetcher : public HintsFetcher { + using HintsFetchedCallback = base::OnceCallback<void( + base::Optional< + std::unique_ptr<optimization_guide::proto::GetHintsResponse>>)>; + using HintsFetcher::FetchOptimizationGuideServiceHints; + + public: + TestHintsFetcher( + scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory, + GURL optimization_guide_service_url, + HintsFetcherEndState fetch_state) + : HintsFetcher(url_loader_factory, optimization_guide_service_url), + fetch_state_(fetch_state) {} + + bool FetchOptimizationGuideServiceHints( + const std::vector<std::string>& hosts, + HintsFetchedCallback hints_fetched_callback) override { + switch (fetch_state_) { + case HintsFetcherEndState::kFetchFailed: + std::move(hints_fetched_callback).Run(base::nullopt); + return false; + case HintsFetcherEndState::kFetchSuccessWithHints: + hints_fetched_ = true; + std::move(hints_fetched_callback).Run(BuildHintsResponse({"host.com"})); + return true; + case HintsFetcherEndState::kFetchSuccessWithNoHints: + hints_fetched_ = true; + std::move(hints_fetched_callback).Run(BuildHintsResponse({})); + return true; + } + return true; + } + + bool hints_fetched() { return hints_fetched_; } + + private: + bool hints_fetched_ = false; + HintsFetcherEndState fetch_state_; +}; + +// A Test PreviewsOptimizationGuide to observe and record when callbacks +// from hints fetching and storing occur. +class TestPreviewsOptimizationGuide : public PreviewsOptimizationGuide { + public: + TestPreviewsOptimizationGuide( + optimization_guide::OptimizationGuideService* optimization_guide_service, + const scoped_refptr<base::SingleThreadTaskRunner>& ui_task_runner, + const scoped_refptr<base::SequencedTaskRunner>& background_task_runner, + const base::FilePath& profile_path, + PreviewsTopHostProvider* previews_top_host_provider, + scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory) + : PreviewsOptimizationGuide(optimization_guide_service, + ui_task_runner, + background_task_runner, + profile_path, + previews_top_host_provider, + url_loader_factory) {} + + bool fetched_hints_stored() { return fetched_hints_stored_; } + + private: + void OnHintsFetched( + base::Optional< + std::unique_ptr<optimization_guide::proto::GetHintsResponse>> + get_hints_response) override { + fetched_hints_stored_ = false; + PreviewsOptimizationGuide::OnHintsFetched(std::move(get_hints_response)); + } + + void OnFetchedHintsStored() override { + fetched_hints_stored_ = true; + PreviewsOptimizationGuide::OnFetchedHintsStored(); + } + + bool fetched_hints_stored_ = false; +}; + class PreviewsOptimizationGuideTest : public testing::Test { public: - PreviewsOptimizationGuideTest() {} + PreviewsOptimizationGuideTest() + : scoped_task_environment_( + base::test::ScopedTaskEnvironment::MainThreadType::UI_MOCK_TIME) {} ~PreviewsOptimizationGuideTest() override {} @@ -102,6 +214,14 @@ return optimization_guide_service_.get(); } + network::SharedURLLoaderFactory* url_loader_factory() { + return url_loader_factory_.get(); + } + + TestHintsFetcher* hints_fetcher() { + return static_cast<TestHintsFetcher*>(guide_->GetHintsFetcherForTesting()); + } + void ProcessHints(const optimization_guide::proto::Configuration& config, const std::string& version) { optimization_guide::HintsComponentInfo info( @@ -126,11 +246,17 @@ optimization_guide_service_ = std::make_unique<TestOptimizationGuideService>( scoped_task_environment_.GetMainThreadTaskRunner()); - guide_ = std::make_unique<PreviewsOptimizationGuide>( + guide_ = std::make_unique<TestPreviewsOptimizationGuide>( optimization_guide_service_.get(), + scoped_task_environment_.GetMainThreadTaskRunner(), scoped_task_environment_.GetMainThreadTaskRunner(), temp_dir(), previews_top_host_provider_.get(), url_loader_factory_); + guide_->SetTimeClockForTesting(scoped_task_environment_.GetMockClock()); + + base::test::ScopedFeatureList scoped_list; + scoped_list.InitAndEnableFeature(features::kOptimizationHintsFetching); + // Add observer is called after the HintCache is fully initialized, // indicating that the PreviewsOptimizationGuide is ready to process hints. while (!optimization_guide_service_->AddObserverCalled()) { @@ -143,9 +269,24 @@ RunUntilIdle(); } + std::unique_ptr<TestHintsFetcher> BuildTestHintsFetcher( + HintsFetcherEndState end_state) { + std::unique_ptr<TestHintsFetcher> hints_fetcher = + std::make_unique<TestHintsFetcher>( + url_loader_factory_, GURL("https://hintsserver.com"), end_state); + return hints_fetcher; + } + base::FilePath temp_dir() const { return temp_dir_.GetPath(); } protected: + base::test::ScopedTaskEnvironment scoped_task_environment_; + + void MoveClockForwardBy(base::TimeDelta time_delta) { + scoped_task_environment_.FastForwardBy(time_delta); + base::RunLoop().RunUntilIdle(); + } + void RunUntilIdle() { scoped_task_environment_.RunUntilIdle(); base::RunLoop().RunUntilIdle(); @@ -183,6 +324,11 @@ PreviewsType type, net::EffectiveConnectionType* out_ect_threshold); + void RunUntilFetchedHintsStored() { + while (!guide_->fetched_hints_stored()) { + } + } + private: void WriteConfigToFile(const optimization_guide::proto::Configuration& config, const base::FilePath& filePath) { @@ -193,14 +339,19 @@ serialized_config.length())); } + void TestOnHintsFetched( + std::unique_ptr<optimization_guide::proto::GetHintsResponse> + get_hints_response) {} + + std::unique_ptr<TestHintsFetcher> test_fetcher_; // Callback used to indicate that the asynchronous call to // MaybeLoadOptimizationHints() has completed its processing. void OnLoadOptimizationHints(); - base::test::ScopedTaskEnvironment scoped_task_environment_; base::ScopedTempDir temp_dir_; - std::unique_ptr<PreviewsOptimizationGuide> guide_; + // std::unique_ptr<PreviewsOptimizationGuide> guide_; + std::unique_ptr<TestPreviewsOptimizationGuide> guide_; std::unique_ptr<TestOptimizationGuideService> optimization_guide_service_; std::unique_ptr<MockPreviewsTopHostProvider> previews_top_host_provider_; @@ -213,6 +364,7 @@ GURL loaded_hints_document_gurl_; std::vector<std::string> loaded_hints_resource_patterns_; + // const base::SimpleTestClock* test_clock_; DISALLOW_COPY_AND_ASSIGN(PreviewsOptimizationGuideTest); }; @@ -1575,18 +1727,110 @@ EXPECT_TRUE(optimization_guide_service()->RemoveObserverCalled()); } -TEST_F(PreviewsOptimizationGuideTest, HintsFetcherEnabled) { +TEST_F(PreviewsOptimizationGuideTest, HintsFetcherEnabledNoHosts) { + base::HistogramTester histogram_tester; base::test::ScopedFeatureList scoped_list; scoped_list.InitAndEnableFeature(features::kOptimizationHintsFetching); base::CommandLine* command_line = base::CommandLine::ForCurrentProcess(); command_line->AppendSwitchASCII("optimization_guide_service_url", "https://hintsserver.com"); - EXPECT_CALL(*top_host_provider(), GetTopHosts(testing::_)); - CreateServiceAndGuide(); + guide()->SetHintsFetcherForTesting( + BuildTestHintsFetcher(HintsFetcherEndState::kFetchSuccessWithHints)); + + EXPECT_CALL(*top_host_provider(), GetTopHosts(testing::_)).Times(1); + // Load hints so that OnHintsUpdated is called. This will force FetchHints to // be triggered if OptimizationHintsFetching is enabled. InitializeFixedCountResourceLoadingHints(); + // Cause the timer to fire the fetch event. + MoveClockForwardBy(base::TimeDelta::FromSeconds(kTestFetchRetryDelaySecs)); + EXPECT_FALSE(hints_fetcher()->hints_fetched()); +} + +TEST_F(PreviewsOptimizationGuideTest, HintsFetcherEnabledWithHosts) { + base::HistogramTester histogram_tester; + base::test::ScopedFeatureList scoped_list; + scoped_list.InitAndEnableFeature(features::kOptimizationHintsFetching); + std::string opt_guide_url = "https://hintsserver.com"; + + guide()->SetHintsFetcherForTesting( + BuildTestHintsFetcher(HintsFetcherEndState::kFetchSuccessWithHints)); + + std::vector<std::string> hosts = {"example1.com", "example2.com"}; + EXPECT_CALL(*top_host_provider(), GetTopHosts(testing::_)) + .Times(1) + .WillRepeatedly(testing::Return(hosts)); + + // Load hints so that OnHintsUpdated is called. This will force FetchHints to + // be triggered if OptimizationHintsFetching is enabled. + InitializeFixedCountResourceLoadingHints(); + + // Force timer to expire and schedule a hints fetch. + MoveClockForwardBy(base::TimeDelta::FromSeconds(kTestFetchRetryDelaySecs)); + EXPECT_TRUE(hints_fetcher()->hints_fetched()); +} + +TEST_F(PreviewsOptimizationGuideTest, HintsFetcherTimerRetryDelay) { + base::HistogramTester histogram_tester; + base::test::ScopedFeatureList scoped_list; + scoped_list.InitAndEnableFeature(features::kOptimizationHintsFetching); + std::string opt_guide_url = "https://hintsserver.com"; + + guide()->SetHintsFetcherForTesting( + BuildTestHintsFetcher(HintsFetcherEndState::kFetchFailed)); + + std::vector<std::string> hosts = {"example1.com", "example2.com"}; + EXPECT_CALL(*top_host_provider(), GetTopHosts(testing::_)) + .Times(2) + .WillRepeatedly(testing::Return(hosts)); + + // Force hints fetch scheduling. + guide()->ScheduleHintsFetch(); + + // Force timer to expire and schedule a hints fetch - first time. + MoveClockForwardBy(base::TimeDelta::FromSeconds(kTestFetchRetryDelaySecs)); + EXPECT_FALSE(hints_fetcher()->hints_fetched()); + + // Force speculative timer to expire after fetch fails first time, update + // hints fetcher so it succeeds this time. + guide()->SetHintsFetcherForTesting( + BuildTestHintsFetcher(HintsFetcherEndState::kFetchSuccessWithHints)); + MoveClockForwardBy(base::TimeDelta::FromSeconds(kTestFetchRetryDelaySecs)); + EXPECT_TRUE(hints_fetcher()->hints_fetched()); +} + +TEST_F(PreviewsOptimizationGuideTest, HintsFetcherTimerFetchSucceeds) { + base::HistogramTester histogram_tester; + base::test::ScopedFeatureList scoped_list; + scoped_list.InitAndEnableFeature(features::kOptimizationHintsFetching); + std::string opt_guide_url = "https://hintsserver.com"; + + guide()->SetHintsFetcherForTesting( + BuildTestHintsFetcher(HintsFetcherEndState::kFetchSuccessWithHints)); + + std::vector<std::string> hosts = {"example1.com", "example2.com"}; + EXPECT_CALL(*top_host_provider(), GetTopHosts(testing::_)) + .WillRepeatedly(testing::Return(hosts)); + + // Force hints fetch scheduling. + guide()->ScheduleHintsFetch(); + + // Force timer to expire and schedule a hints fetch that succeeds. + MoveClockForwardBy(base::TimeDelta::FromSeconds(kTestFetchRetryDelaySecs)); + EXPECT_TRUE(hints_fetcher()->hints_fetched()); + + // TODO(mcrouse): Make sure timer is triggered by metadata entry, + // |hint_cache| control needed. + guide()->SetHintsFetcherForTesting( + BuildTestHintsFetcher(HintsFetcherEndState::kFetchSuccessWithHints)); + + MoveClockForwardBy(base::TimeDelta::FromSeconds(kTestFetchRetryDelaySecs)); + EXPECT_FALSE(hints_fetcher()->hints_fetched()); + MoveClockForwardBy(base::TimeDelta::FromSeconds(kUpdateFetchHintsTimeSecs)); + + RunUntilFetchedHintsStored(); + EXPECT_TRUE(hints_fetcher()->hints_fetched()); } TEST_F(PreviewsOptimizationGuideTest, HintsFetcherDisabled) {
diff --git a/components/previews/content/proto/hint_cache.proto b/components/previews/content/proto/hint_cache.proto index 9f795aa8..b04fc3c 100644 --- a/components/previews/content/proto/hint_cache.proto +++ b/components/previews/content/proto/hint_cache.proto
@@ -19,4 +19,7 @@ optional string version = 1; // The actual hint data. optional optimization_guide.proto.Hint hint = 2; + // Time when top host fetched hints are still usable but update should + // be requested. This is set on the fetched metadata entry. + optional int64 update_time_secs = 3; }
diff --git a/components/previews/core/previews_switches.cc b/components/previews/core/previews_switches.cc index 676be59..3906dd8 100644 --- a/components/previews/core/previews_switches.cc +++ b/components/previews/core/previews_switches.cc
@@ -33,6 +33,11 @@ // valid value to this switch causes Chrome startup to block on hints parsing. const char kHintsProtoOverride[] = "optimization_guide_hints_override"; +// Overrides scheduling and time delays for fetching hints and causes a hints +// fetch immediately on start up using the provided comma separate lists of +// hosts. +const char kFetchHintsOverride[] = "optimization-guide-fetch-hints-override"; + // Overrides the Optimization Guide Service URL that the HintsFetcher will // request remote hints from. const char kOptimizationGuideServiceURL[] = "optimization_guide_service_url";
diff --git a/components/previews/core/previews_switches.h b/components/previews/core/previews_switches.h index 4fa8f396..9fa115f 100644 --- a/components/previews/core/previews_switches.h +++ b/components/previews/core/previews_switches.h
@@ -14,6 +14,7 @@ extern const char kIgnoreLitePageRedirectOptimizationBlacklist[]; extern const char kClearLitePageRedirectLocalBlacklist[]; extern const char kHintsProtoOverride[]; +extern const char kFetchHintsOverride[]; extern const char kOptimizationGuideServiceURL[]; extern const char kOptimizationGuideServiceAPIKey[]; extern const char kPurgeHintCacheStore[];
diff --git a/components/sync/model/fake_model_type_sync_bridge.cc b/components/sync/model/fake_model_type_sync_bridge.cc index 58a22ef..4ead1c4 100644 --- a/components/sync/model/fake_model_type_sync_bridge.cc +++ b/components/sync/model/fake_model_type_sync_bridge.cc
@@ -9,6 +9,7 @@ #include "base/bind.h" #include "base/stl_util.h" +#include "base/strings/string_number_conversions.h" #include "components/sync/base/hash_util.h" #include "components/sync/model/conflict_resolution.h" #include "components/sync/model/model_type_store.h" @@ -131,6 +132,7 @@ const EntityData& FakeModelTypeSyncBridge::Store::GetData( const std::string& key) const { + DCHECK(data_store_.count(key) != 0) << " for key " << key; return *data_store_.find(key)->second; } @@ -227,14 +229,16 @@ std::string storage_key = change->storage_key(); EXPECT_NE(SupportsGetStorageKey(), storage_key.empty()); if (storage_key.empty()) { - storage_key = GetStorageKeyImpl(change->data()); - if (base::ContainsKey(keys_to_ignore_, storage_key)) { + if (base::ContainsKey(values_to_ignore_, + change->data().specifics.preference().value())) { change_processor()->UntrackEntityForClientTagHash( change->data().client_tag_hash); - } else { - change_processor()->UpdateStorageKey(change->data(), storage_key, - metadata_change_list.get()); + continue; } + + storage_key = GenerateStorageKey(change->data()); + change_processor()->UpdateStorageKey(change->data(), storage_key, + metadata_change_list.get()); } remote_storage_keys.insert(storage_key); db_->PutData(storage_key, change->data()); @@ -266,7 +270,7 @@ std::string storage_key = change->storage_key(); EXPECT_NE(SupportsGetStorageKey(), storage_key.empty()); if (storage_key.empty()) { - storage_key = GetStorageKeyImpl(change->data()); + storage_key = GenerateStorageKey(change->data()); change_processor()->UpdateStorageKey(change->data(), storage_key, metadata_changes.get()); } @@ -338,23 +342,22 @@ std::string FakeModelTypeSyncBridge::GetStorageKey( const EntityData& entity_data) { DCHECK(supports_get_storage_key_); - return GetStorageKeyImpl(entity_data); + return GenerateStorageKey(entity_data); } -std::string FakeModelTypeSyncBridge::GetStorageKeyImpl( +std::string FakeModelTypeSyncBridge::GenerateStorageKey( const EntityData& entity_data) { - return entity_data.specifics.preference().name(); + if (supports_get_storage_key_) { + return entity_data.specifics.preference().name(); + } else { + return base::NumberToString(++last_generated_storage_key_); + } } bool FakeModelTypeSyncBridge::SupportsGetStorageKey() const { return supports_get_storage_key_; } -void FakeModelTypeSyncBridge::SetSupportsGetStorageKey( - bool supports_get_storage_key) { - supports_get_storage_key_ = supports_get_storage_key; -} - ConflictResolution FakeModelTypeSyncBridge::ResolveConflict( const EntityData& local_data, const EntityData& remote_data) const { @@ -391,8 +394,19 @@ return new_data; } -void FakeModelTypeSyncBridge::SetKeyToIgnore(const std::string key) { - keys_to_ignore_.insert(key); +void FakeModelTypeSyncBridge::SetSupportsGetStorageKey( + bool supports_get_storage_key) { + supports_get_storage_key_ = supports_get_storage_key; +} + +std::string FakeModelTypeSyncBridge::GetLastGeneratedStorageKey() const { + // Verify that GenerateStorageKey() was called at least once. + EXPECT_NE(0, last_generated_storage_key_); + return base::NumberToString(last_generated_storage_key_); +} + +void FakeModelTypeSyncBridge::AddValueToIgnore(const std::string& value) { + values_to_ignore_.insert(value); } } // namespace syncer
diff --git a/components/sync/model/fake_model_type_sync_bridge.h b/components/sync/model/fake_model_type_sync_bridge.h index 82e37fa..dc783b80 100644 --- a/components/sync/model/fake_model_type_sync_bridge.h +++ b/components/sync/model/fake_model_type_sync_bridge.h
@@ -122,7 +122,6 @@ std::string GetClientTag(const EntityData& entity_data) override; std::string GetStorageKey(const EntityData& entity_data) override; bool SupportsGetStorageKey() const override; - void SetSupportsGetStorageKey(bool supports_get_storage_key); ConflictResolution ResolveConflict( const EntityData& local_data, const EntityData& remote_data) const override; @@ -141,8 +140,20 @@ // test code here, this function is needed to manually copy it. static std::unique_ptr<EntityData> CopyEntityData(const EntityData& old_data); - // Sets storage key which will be ignored by bridge. - void SetKeyToIgnore(const std::string key); + // Influences the way the bridge produces storage key. If set to true, the + // bridge will compute a storage key deterministically from specifics, via + // GetStorageKey(). If set to false, it will return autoincrement-like storage + // keys that cannot be inferred from specifics, and exercise + // UpdateStorageKey() for remote changes to report storage keys. + void SetSupportsGetStorageKey(bool supports_get_storage_key); + + // Returns the last generated autoincrement-like storage key, applicable only + // for the SetSupportsGetStorageKey(false) case (otherwise the storage key + // gets inferred deterministically from specifics). + std::string GetLastGeneratedStorageKey() const; + + // Add values that will be ignored by bridge. + void AddValueToIgnore(const std::string& value); const Store& db() const { return *db_; } Store* mutable_db() { return db_.get(); } @@ -155,13 +166,13 @@ // Applies |change_list| to the metadata store. void ApplyMetadataChangeList(std::unique_ptr<MetadataChangeList> change_list); - std::string GetStorageKeyImpl(const EntityData& entity_data); + std::string GenerateStorageKey(const EntityData& entity_data); // The conflict resolution to use for calls to ResolveConflict. std::unique_ptr<ConflictResolution> conflict_resolution_; - // The storage keys which bridge will ignore. - std::unordered_set<std::string> keys_to_ignore_; + // The keys that the bridge will ignore. + std::unordered_set<std::string> values_to_ignore_; // Whether an error should be produced on the next bridge call. bool error_next_ = false; @@ -170,6 +181,11 @@ // responsible for calling UpdateStorageKey when processing new entities in // MergeSyncData/ApplySyncChanges. bool supports_get_storage_key_ = true; + + // Last dynamically-generated storage key, for the case where + // |supports_get_storage_key_| == false (otherwise the storage key gets + // inferred deterministically from specifics). + int last_generated_storage_key_ = 0; }; } // namespace syncer
diff --git a/components/sync/model_impl/client_tag_based_model_type_processor.cc b/components/sync/model_impl/client_tag_based_model_type_processor.cc index 3dab9253..843ebd85 100644 --- a/components/sync/model_impl/client_tag_based_model_type_processor.cc +++ b/components/sync/model_impl/client_tag_based_model_type_processor.cc
@@ -721,7 +721,8 @@ ProcessorEntity* ClientTagBasedModelTypeProcessor::ProcessUpdate( std::unique_ptr<UpdateResponseData> update, - EntityChangeList* entity_changes) { + EntityChangeList* entity_changes, + std::string* storage_key_to_clear) { const EntityData& data = *update->entity; const std::string& client_tag_hash = data.client_tag_hash; @@ -767,7 +768,8 @@ ConflictResolution::Type resolution_type = ConflictResolution::TYPE_SIZE; if (entity && entity->IsUnsynced()) { // Handle conflict resolution. - resolution_type = ResolveConflict(*update, entity, entity_changes); + resolution_type = + ResolveConflict(*update, entity, entity_changes, storage_key_to_clear); UMA_HISTOGRAM_ENUMERATION("Sync.ResolveConflict", resolution_type, ConflictResolution::TYPE_SIZE); } else { @@ -830,7 +832,8 @@ ConflictResolution::Type ClientTagBasedModelTypeProcessor::ResolveConflict( const UpdateResponseData& update, ProcessorEntity* entity, - EntityChangeList* changes) { + EntityChangeList* changes, + std::string* storage_key_to_clear) { const EntityData& remote_data = *update.entity; ConflictResolution::Type resolution_type = ConflictResolution::TYPE_SIZE; @@ -840,6 +843,9 @@ if (entity->MatchesData(remote_data)) { // The changes are identical so there isn't a real conflict. resolution_type = ConflictResolution::CHANGES_MATCH; + } else if (entity->metadata().is_deleted()) { + // Local tombstone vs remote update (non-deletion). Should be undeleted. + resolution_type = ConflictResolution::USE_REMOTE; } else if (entity->MatchesOwnBaseData()) { // If there is no real local change, then the entity must be unsynced due to // a pending local re-encryption request. In this case, the remote data @@ -876,17 +882,28 @@ break; case ConflictResolution::USE_REMOTE: case ConflictResolution::IGNORE_LOCAL_ENCRYPTION: - // Squash the pending commit. - entity->RecordForcedUpdate(update); // Update client data to match server. if (update.entity->is_deleted()) { + DCHECK(!entity->metadata().is_deleted()); changes->push_back(EntityChange::CreateDelete(entity->storage_key())); - } else { + } else if (!entity->metadata().is_deleted()) { changes->push_back(EntityChange::CreateUpdate(entity->storage_key(), update.entity->Clone())); + } else { + // Remote undeletion. This could imply a new storage key for some + // bridges, so we may need to wait until UpdateStorageKey() is called. + if (!bridge_->SupportsGetStorageKey()) { + *storage_key_to_clear = entity->storage_key(); + entity->ClearStorageKey(); + } + changes->push_back(EntityChange::CreateAdd(entity->storage_key(), + update.entity->Clone())); } + // Squash the pending commit. + entity->RecordForcedUpdate(update); break; case ConflictResolution::USE_NEW: + DCHECK(!entity->metadata().is_deleted()); // Record that we received the update. entity->RecordIgnoredUpdate(update); // Make a new pending commit to update the server. @@ -1060,7 +1077,9 @@ for (std::unique_ptr<syncer::UpdateResponseData>& update : updates) { DCHECK(update); - ProcessorEntity* entity = ProcessUpdate(std::move(update), &entity_changes); + std::string storage_key_to_clear; + ProcessorEntity* entity = ProcessUpdate(std::move(update), &entity_changes, + &storage_key_to_clear); if (!entity) { // The update is either of the following: @@ -1079,8 +1098,19 @@ if (entity->storage_key().empty()) { // Storage key of this entity is not known yet. Don't update metadata, it // will be done from UpdateStorageKey. + + // If this is the result of a conflict resolution (where a remote + // undeletion was preferred), then need to clear a metadata entry from + // the database. + if (!storage_key_to_clear.empty()) { + metadata_changes->ClearMetadata(storage_key_to_clear); + storage_key_to_tag_hash_.erase(storage_key_to_clear); + } continue; } + + DCHECK(storage_key_to_clear.empty()); + if (entity->CanClearMetadata()) { metadata_changes->ClearMetadata(entity->storage_key()); storage_key_to_tag_hash_.erase(entity->storage_key());
diff --git a/components/sync/model_impl/client_tag_based_model_type_processor.h b/components/sync/model_impl/client_tag_based_model_type_processor.h index 0c7fbb7..e71c767 100644 --- a/components/sync/model_impl/client_tag_based_model_type_processor.h +++ b/components/sync/model_impl/client_tag_based_model_type_processor.h
@@ -131,13 +131,18 @@ // Helper function to process the update for a single entity. If a local data // change is required, it will be added to |entity_changes|. The return value // is the tracked entity, or nullptr if the update should be ignored. + // |storage_key_to_clear| must not be null and allows the implementation to + // indicate that a certain storage key is now obsolete and should be cleared, + // which is leveraged in certain conflict resolution scenarios. ProcessorEntity* ProcessUpdate(std::unique_ptr<UpdateResponseData> update, - EntityChangeList* entity_changes); + EntityChangeList* entity_changes, + std::string* storage_key_to_clear); // Resolve a conflict between |update| and the pending commit in |entity|. ConflictResolution::Type ResolveConflict(const UpdateResponseData& update, ProcessorEntity* entity, - EntityChangeList* changes); + EntityChangeList* changes, + std::string* storage_key_to_clear); // Recommit all entities for encryption except those in |already_updated|. void RecommitAllForEncryption(
diff --git a/components/sync/model_impl/client_tag_based_model_type_processor_unittest.cc b/components/sync/model_impl/client_tag_based_model_type_processor_unittest.cc index dd4e2cb..deebe2d 100644 --- a/components/sync/model_impl/client_tag_based_model_type_processor_unittest.cc +++ b/components/sync/model_impl/client_tag_based_model_type_processor_unittest.cc
@@ -1385,6 +1385,71 @@ } TEST_F(ClientTagBasedModelTypeProcessorTest, + ShouldResolveConflictToRemoteUndeletion) { + InitializeToReadyState(); + WriteItemAndAck(kKey1, kValue1); + ASSERT_EQ(0U, worker()->GetNumPendingCommits()); + + bridge()->DeleteItem(kKey1); + ASSERT_EQ(1U, worker()->GetNumPendingCommits()); + worker()->VerifyPendingCommits({{kHash1}}); + ASSERT_TRUE(worker()->GetLatestPendingCommitForHash(kHash1)); + ASSERT_TRUE( + worker()->GetLatestPendingCommitForHash(kHash1)->entity->is_deleted()); + ASSERT_EQ(2U, db()->data_change_count()); + ASSERT_EQ(3U, db()->metadata_change_count()); + ASSERT_TRUE(type_processor()->IsTrackingEntityForTest(kKey1)); + + worker()->UpdateFromServer(kHash1, GenerateSpecifics(kKey1, kValue2)); + + // Updated client data and metadata; no new commit request. + EXPECT_TRUE(type_processor()->IsTrackingEntityForTest(kKey1)); + EXPECT_EQ(3U, db()->data_change_count()); + EXPECT_EQ(kValue2, db()->GetValue(kKey1)); + EXPECT_EQ(4U, db()->metadata_change_count()); + EXPECT_EQ(2, db()->GetMetadata(kKey1).server_version()); + worker()->VerifyPendingCommits({{kHash1}}); +} + +TEST_F(ClientTagBasedModelTypeProcessorTest, + ShouldResolveConflictToRemoteUndeletionWithUpdateStorageKey) { + bridge()->SetSupportsGetStorageKey(false); + InitializeToReadyState(); + WriteItemAndAck(kKey1, kValue1); + ASSERT_EQ(0U, worker()->GetNumPendingCommits()); + + bridge()->DeleteItem(kKey1); + ASSERT_EQ(1U, worker()->GetNumPendingCommits()); + worker()->VerifyPendingCommits({{kHash1}}); + ASSERT_TRUE(worker()->GetLatestPendingCommitForHash(kHash1)); + ASSERT_TRUE( + worker()->GetLatestPendingCommitForHash(kHash1)->entity->is_deleted()); + ASSERT_EQ(2U, db()->data_change_count()); + ASSERT_EQ(3U, db()->metadata_change_count()); + ASSERT_TRUE(type_processor()->IsTrackingEntityForTest(kKey1)); + + worker()->UpdateFromServer(kHash1, GenerateSpecifics(kKey1, kValue2)); + + // A new storage key should have been generated, which should replace the + // previous when it comes to storing data and metadata. + const std::string new_storage_key = bridge()->GetLastGeneratedStorageKey(); + ASSERT_NE(kKey1, new_storage_key); + EXPECT_TRUE(db()->HasData(new_storage_key)); + EXPECT_TRUE(db()->HasMetadata(new_storage_key)); + EXPECT_TRUE(type_processor()->IsTrackingEntityForTest(new_storage_key)); + EXPECT_FALSE(db()->HasData(kKey1)); + EXPECT_FALSE(db()->HasMetadata(kKey1)); + EXPECT_FALSE(type_processor()->IsTrackingEntityForTest(kKey1)); + + // Updated client data and metadata; no new commit request. + EXPECT_EQ(3U, db()->data_change_count()); + EXPECT_EQ(kValue2, db()->GetValue(new_storage_key)); + EXPECT_EQ(5U, db()->metadata_change_count()); + EXPECT_EQ(2, db()->GetMetadata(new_storage_key).server_version()); + worker()->VerifyPendingCommits({{kHash1}}); +} + +TEST_F(ClientTagBasedModelTypeProcessorTest, ShouldResolveConflictToRemoteVersion) { InitializeToReadyState(); bridge()->WriteItem(kKey1, kValue1); @@ -1992,19 +2057,21 @@ // Create update which will be ignored by bridge. updates.push_back( worker()->GenerateUpdateData(kHash3, GenerateSpecifics(kKey3, kValue3))); - bridge()->SetKeyToIgnore(kKey3); + bridge()->AddValueToIgnore(kValue3); worker()->UpdateFromServer(std::move(updates)); EXPECT_EQ(1, bridge()->merge_call_count()); EXPECT_EQ(1U, ProcessorEntityCount()); - // Metadata should be written under kKey1. This means that UpdateStorageKey - // was called and value of storage key got propagated to MetadataChangeList. - EXPECT_TRUE(db()->HasMetadata(kKey1)); + // Metadata should be written under a new storage key. This means that + // UpdateStorageKey was called and value of storage key got propagated to + // MetadataChangeList. + const std::string storage_key1 = bridge()->GetLastGeneratedStorageKey(); + EXPECT_TRUE(db()->HasMetadata(storage_key1)); EXPECT_EQ(1U, db()->metadata_count()); EXPECT_EQ(0, bridge()->get_storage_key_call_count()); - // Local update of kKey1 should affect the same entity. This ensures that - // storage key to client tag hash mapping was updated on the previous step. - bridge()->WriteItem(kKey1, kValue2); + // Local update should affect the same entity. This ensures that storage key + // to client tag hash mapping was updated on the previous step. + bridge()->WriteItem(storage_key1, kValue2); EXPECT_EQ(1U, ProcessorEntityCount()); EXPECT_EQ(1U, db()->metadata_count()); @@ -2012,7 +2079,9 @@ // It should call UpdateStorageKey, not GetStorageKey. worker()->UpdateFromServer(kHash2, GenerateSpecifics(kKey2, kValue2)); EXPECT_EQ(1, bridge()->apply_call_count()); - EXPECT_TRUE(db()->HasMetadata(kKey2)); + const std::string storage_key2 = bridge()->GetLastGeneratedStorageKey(); + EXPECT_NE(storage_key1, storage_key2); + EXPECT_TRUE(db()->HasMetadata(storage_key2)); EXPECT_EQ(2U, db()->metadata_count()); EXPECT_EQ(0, bridge()->get_storage_key_call_count()); } @@ -2041,7 +2110,7 @@ // FakeModelTypeSyncBridge to call UpdateStorageKey for new entities and will // DCHECK if GetStorageKey gets called. bridge()->SetSupportsGetStorageKey(false); - bridge()->SetKeyToIgnore(kKey1); + bridge()->AddValueToIgnore(kValue1); ModelReadyToSync(); OnSyncStarting();
diff --git a/components/sync/model_impl/processor_entity.cc b/components/sync/model_impl/processor_entity.cc index 663b9ed0..81c19b2 100644 --- a/components/sync/model_impl/processor_entity.cc +++ b/components/sync/model_impl/processor_entity.cc
@@ -80,6 +80,11 @@ storage_key_ = storage_key; } +void ProcessorEntity::ClearStorageKey() { + DCHECK(metadata_.is_deleted()); + storage_key_.clear(); +} + void ProcessorEntity::SetCommitData(std::unique_ptr<EntityData> data) { DCHECK(data); // Update data's fields from metadata. @@ -113,10 +118,10 @@ bool ProcessorEntity::MatchesOwnBaseData() const { DCHECK(IsUnsynced()); - DCHECK(!metadata_.specifics_hash().empty()); if (metadata_.is_deleted()) { return false; } + DCHECK(!metadata_.specifics_hash().empty()); return metadata_.specifics_hash() == metadata_.base_specifics_hash(); }
diff --git a/components/sync/model_impl/processor_entity.h b/components/sync/model_impl/processor_entity.h index 023d00d..af4259f 100644 --- a/components/sync/model_impl/processor_entity.h +++ b/components/sync/model_impl/processor_entity.h
@@ -111,6 +111,10 @@ // an entity initialized with empty storage key. void SetStorageKey(const std::string& storage_key); + // Undoes SetStorageKey(), which is needed in certain conflict resolution + // scenarios. + void ClearStorageKey(); + // Takes the passed commit data updates its fields with values from metadata // and caches it in the instance. void SetCommitData(std::unique_ptr<EntityData> data);
diff --git a/components/test/data/vr_browser_ui/render_tests/VrBrowserNativeUiTest.suggestion_clicking_bottom.Pixel_XL-25.png.sha1 b/components/test/data/vr_browser_ui/render_tests/VrBrowserNativeUiTest.suggestion_clicking_bottom.Pixel_XL-25.png.sha1 index b7af035..4404c52 100644 --- a/components/test/data/vr_browser_ui/render_tests/VrBrowserNativeUiTest.suggestion_clicking_bottom.Pixel_XL-25.png.sha1 +++ b/components/test/data/vr_browser_ui/render_tests/VrBrowserNativeUiTest.suggestion_clicking_bottom.Pixel_XL-25.png.sha1
@@ -1 +1 @@ -2e43d01c6c3701a096da20305e999daebebbd327 \ No newline at end of file +b35f3cbd8f584bac45c2f8618bb827136adfd957 \ No newline at end of file
diff --git a/components/test/data/vr_browser_ui/render_tests/VrBrowserNativeUiTest.suggestion_clicking_bottom.Pixel_XL-26.png.sha1 b/components/test/data/vr_browser_ui/render_tests/VrBrowserNativeUiTest.suggestion_clicking_bottom.Pixel_XL-26.png.sha1 index a76c136..54fc0f8 100644 --- a/components/test/data/vr_browser_ui/render_tests/VrBrowserNativeUiTest.suggestion_clicking_bottom.Pixel_XL-26.png.sha1 +++ b/components/test/data/vr_browser_ui/render_tests/VrBrowserNativeUiTest.suggestion_clicking_bottom.Pixel_XL-26.png.sha1
@@ -1 +1 @@ -785ed16a1915a87c5c372c551c22e7653ee92b0b \ No newline at end of file +4494408049df80d7edf64c1d39d0a676b348b445 \ No newline at end of file
diff --git a/components/test/data/vr_browser_ui/render_tests/VrBrowserNativeUiTest.suggestion_clicking_left.Pixel_XL-25.png.sha1 b/components/test/data/vr_browser_ui/render_tests/VrBrowserNativeUiTest.suggestion_clicking_left.Pixel_XL-25.png.sha1 index 87de8f3..054ff38 100644 --- a/components/test/data/vr_browser_ui/render_tests/VrBrowserNativeUiTest.suggestion_clicking_left.Pixel_XL-25.png.sha1 +++ b/components/test/data/vr_browser_ui/render_tests/VrBrowserNativeUiTest.suggestion_clicking_left.Pixel_XL-25.png.sha1
@@ -1 +1 @@ -53a11c2b33f9b157820cfffbedb6ee85805eca92 \ No newline at end of file +671050041d59f15213880407d57847784dc6688b \ No newline at end of file
diff --git a/components/test/data/vr_browser_ui/render_tests/VrBrowserNativeUiTest.suggestion_clicking_left.Pixel_XL-26.png.sha1 b/components/test/data/vr_browser_ui/render_tests/VrBrowserNativeUiTest.suggestion_clicking_left.Pixel_XL-26.png.sha1 index 04f0565..9f1a972 100644 --- a/components/test/data/vr_browser_ui/render_tests/VrBrowserNativeUiTest.suggestion_clicking_left.Pixel_XL-26.png.sha1 +++ b/components/test/data/vr_browser_ui/render_tests/VrBrowserNativeUiTest.suggestion_clicking_left.Pixel_XL-26.png.sha1
@@ -1 +1 @@ -6ab1ece633eb6d2b97167653cc1ca2929736a55c \ No newline at end of file +3fbefba834615082936b05c145d735de48225680 \ No newline at end of file
diff --git a/components/test/data/vr_browser_ui/render_tests/VrBrowserNativeUiTest.suggestion_clicking_right.Pixel_XL-25.png.sha1 b/components/test/data/vr_browser_ui/render_tests/VrBrowserNativeUiTest.suggestion_clicking_right.Pixel_XL-25.png.sha1 index 9977e6c..665fa23 100644 --- a/components/test/data/vr_browser_ui/render_tests/VrBrowserNativeUiTest.suggestion_clicking_right.Pixel_XL-25.png.sha1 +++ b/components/test/data/vr_browser_ui/render_tests/VrBrowserNativeUiTest.suggestion_clicking_right.Pixel_XL-25.png.sha1
@@ -1 +1 @@ -cbffb42766d954725dd2af8193d75d9557ede979 \ No newline at end of file +95097302caa8353ab8d680cbeff0a62748b4b7c0 \ No newline at end of file
diff --git a/components/test/data/vr_browser_ui/render_tests/VrBrowserNativeUiTest.suggestion_clicking_right.Pixel_XL-26.png.sha1 b/components/test/data/vr_browser_ui/render_tests/VrBrowserNativeUiTest.suggestion_clicking_right.Pixel_XL-26.png.sha1 index de2eb33..b84d37e2 100644 --- a/components/test/data/vr_browser_ui/render_tests/VrBrowserNativeUiTest.suggestion_clicking_right.Pixel_XL-26.png.sha1 +++ b/components/test/data/vr_browser_ui/render_tests/VrBrowserNativeUiTest.suggestion_clicking_right.Pixel_XL-26.png.sha1
@@ -1 +1 @@ -b293e14a49f0f34aa6659c3f29d678c90a8f4dee \ No newline at end of file +135a8992a27da283499890c51343149d27f53514 \ No newline at end of file
diff --git a/components/test/data/vr_browser_ui/render_tests/VrBrowserNativeUiTest.suggestion_clicking_top.Pixel_XL-25.png.sha1 b/components/test/data/vr_browser_ui/render_tests/VrBrowserNativeUiTest.suggestion_clicking_top.Pixel_XL-25.png.sha1 index 340401c..fa654279 100644 --- a/components/test/data/vr_browser_ui/render_tests/VrBrowserNativeUiTest.suggestion_clicking_top.Pixel_XL-25.png.sha1 +++ b/components/test/data/vr_browser_ui/render_tests/VrBrowserNativeUiTest.suggestion_clicking_top.Pixel_XL-25.png.sha1
@@ -1 +1 @@ -8690cc2c9d4f13aadc0142fa7883f7b398d88fe1 \ No newline at end of file +f93bc767631ae5bc2b2c806283e52a2d66972820 \ No newline at end of file
diff --git a/components/test/data/vr_browser_ui/render_tests/VrBrowserNativeUiTest.suggestion_clicking_top.Pixel_XL-26.png.sha1 b/components/test/data/vr_browser_ui/render_tests/VrBrowserNativeUiTest.suggestion_clicking_top.Pixel_XL-26.png.sha1 index d1c6461..15959425 100644 --- a/components/test/data/vr_browser_ui/render_tests/VrBrowserNativeUiTest.suggestion_clicking_top.Pixel_XL-26.png.sha1 +++ b/components/test/data/vr_browser_ui/render_tests/VrBrowserNativeUiTest.suggestion_clicking_top.Pixel_XL-26.png.sha1
@@ -1 +1 @@ -141705b3bc31eac3fb3897ebf29224d89720e087 \ No newline at end of file +7b51ec5fec23ecb51635bf98e4e2ce6a0f053d69 \ No newline at end of file
diff --git a/components/test/data/vr_browser_ui/render_tests/VrBrowserNativeUiTest.suggestion_hovering_middle.Pixel_XL-25.png.sha1 b/components/test/data/vr_browser_ui/render_tests/VrBrowserNativeUiTest.suggestion_hovering_middle.Pixel_XL-25.png.sha1 index 9dc30f43..856c45d 100644 --- a/components/test/data/vr_browser_ui/render_tests/VrBrowserNativeUiTest.suggestion_hovering_middle.Pixel_XL-25.png.sha1 +++ b/components/test/data/vr_browser_ui/render_tests/VrBrowserNativeUiTest.suggestion_hovering_middle.Pixel_XL-25.png.sha1
@@ -1 +1 @@ -1c816c86bdebb5b034e1fa9e76a2813d71cc1fc7 \ No newline at end of file +bcb51742e0788962d58dfa8b7b0ff37100748b11 \ No newline at end of file
diff --git a/components/test/data/vr_browser_ui/render_tests/VrBrowserNativeUiTest.suggestion_hovering_middle.Pixel_XL-26.png.sha1 b/components/test/data/vr_browser_ui/render_tests/VrBrowserNativeUiTest.suggestion_hovering_middle.Pixel_XL-26.png.sha1 index 7e1344c..8e8af213b 100644 --- a/components/test/data/vr_browser_ui/render_tests/VrBrowserNativeUiTest.suggestion_hovering_middle.Pixel_XL-26.png.sha1 +++ b/components/test/data/vr_browser_ui/render_tests/VrBrowserNativeUiTest.suggestion_hovering_middle.Pixel_XL-26.png.sha1
@@ -1 +1 @@ -02880cd1a9f87d62cb20860d8a3eb0fecee54f24 \ No newline at end of file +3474d25341d4daad894fffb515c4a6726b61d040 \ No newline at end of file
diff --git a/components/test/data/vr_browser_ui/render_tests/VrBrowserNativeUiTest.suggestion_hovering_top.Pixel_XL-25.png.sha1 b/components/test/data/vr_browser_ui/render_tests/VrBrowserNativeUiTest.suggestion_hovering_top.Pixel_XL-25.png.sha1 index 83e36a5..9d38513 100644 --- a/components/test/data/vr_browser_ui/render_tests/VrBrowserNativeUiTest.suggestion_hovering_top.Pixel_XL-25.png.sha1 +++ b/components/test/data/vr_browser_ui/render_tests/VrBrowserNativeUiTest.suggestion_hovering_top.Pixel_XL-25.png.sha1
@@ -1 +1 @@ -84d05e02d5dcfbf106024f5b0f65b05211b3270d \ No newline at end of file +83f9bc92393fe3a269e61cde586b9a45b1ab4c5f \ No newline at end of file
diff --git a/components/test/data/vr_browser_ui/render_tests/VrBrowserNativeUiTest.suggestion_hovering_top.Pixel_XL-26.png.sha1 b/components/test/data/vr_browser_ui/render_tests/VrBrowserNativeUiTest.suggestion_hovering_top.Pixel_XL-26.png.sha1 index c3a23f13..9d15c818 100644 --- a/components/test/data/vr_browser_ui/render_tests/VrBrowserNativeUiTest.suggestion_hovering_top.Pixel_XL-26.png.sha1 +++ b/components/test/data/vr_browser_ui/render_tests/VrBrowserNativeUiTest.suggestion_hovering_top.Pixel_XL-26.png.sha1
@@ -1 +1 @@ -6b4327781d36a9811da53c341b77016c00f6c632 \ No newline at end of file +82f84f9ff5755656dd2a071f08391055d16d70cc \ No newline at end of file
diff --git a/components/user_manager/fake_user_manager.cc b/components/user_manager/fake_user_manager.cc index 45dd014a..54270c1 100644 --- a/components/user_manager/fake_user_manager.cc +++ b/components/user_manager/fake_user_manager.cc
@@ -286,7 +286,7 @@ } PrefService* FakeUserManager::GetLocalState() const { - return nullptr; + return local_state_; } bool FakeUserManager::IsEnterpriseManaged() const {
diff --git a/components/user_manager/fake_user_manager.h b/components/user_manager/fake_user_manager.h index 4f87775..0f124f68 100644 --- a/components/user_manager/fake_user_manager.h +++ b/components/user_manager/fake_user_manager.h
@@ -32,6 +32,8 @@ const AccountId& account_id, bool is_affiliated); + void set_local_state(PrefService* local_state) { local_state_ = local_state; } + // UserManager overrides. const user_manager::UserList& GetUsers() const override; user_manager::UserList GetUsersAllowedForMultiProfile() const override; @@ -143,6 +145,9 @@ protected: user_manager::User* primary_user_; + // Can be set by set_local_state(). + PrefService* local_state_ = nullptr; + // If set this is the active user. If empty, the first created user is the // active user. AccountId active_account_id_ = EmptyAccountId();
diff --git a/components/viz/client/client_resource_provider.cc b/components/viz/client/client_resource_provider.cc index 2c59c0b..d2c3d9c 100644 --- a/components/viz/client/client_resource_provider.cc +++ b/components/viz/client/client_resource_provider.cc
@@ -388,7 +388,7 @@ ClientResourceProvider::ScopedSkSurface::~ScopedSkSurface() { if (surface_) - surface_->prepareForExternalIO(); + surface_->flush(); } SkSurfaceProps ClientResourceProvider::ScopedSkSurface::ComputeSurfaceProps(
diff --git a/components/viz/common/features.cc b/components/viz/common/features.cc index 2fd05ae4..380e817 100644 --- a/components/viz/common/features.cc +++ b/components/viz/common/features.cc
@@ -5,6 +5,7 @@ #include "components/viz/common/features.h" #include "base/command_line.h" +#include "base/metrics/field_trial_params.h" #include "build/build_config.h" #include "components/viz/common/switches.h" @@ -14,6 +15,10 @@ namespace features { +constexpr char kProvider[] = "provider"; +constexpr char kDrawQuad[] = "draw_quad"; +constexpr char kSurfaceLayer[] = "surface_layer"; + #if defined(USE_AURA) || defined(OS_MACOSX) const base::Feature kEnableSurfaceSynchronization{ "SurfaceSynchronization", base::FEATURE_ENABLED_BY_DEFAULT}; @@ -35,13 +40,17 @@ base::FEATURE_ENABLED_BY_DEFAULT}; #endif -// Enables running the Viz-assisted hit-test logic. +// Enables running the Viz-assisted hit-test logic. We still need to keep the +// VizHitTestDrawQuad and VizHitTestSurfaceLayer features for finch launch. const base::Feature kEnableVizHitTestDrawQuad{"VizHitTestDrawQuad", base::FEATURE_ENABLED_BY_DEFAULT}; const base::Feature kEnableVizHitTestSurfaceLayer{ "VizHitTestSurfaceLayer", base::FEATURE_DISABLED_BY_DEFAULT}; +const base::Feature kEnableVizHitTest{"VizHitTest", + base::FEATURE_ENABLED_BY_DEFAULT}; + // Use the SkiaRenderer. const base::Feature kUseSkiaRenderer{"UseSkiaRenderer", base::FEATURE_DISABLED_BY_DEFAULT}; @@ -75,22 +84,32 @@ switches::kEnableVizHitTestDebug); } -bool IsVizHitTestingDrawQuadEnabled() { - if (IsVizHitTestingSurfaceLayerEnabled()) - return false; - return base::FeatureList::IsEnabled(kEnableVizHitTestDrawQuad) || +// VizHitTest is considered enabled when any of its variant is turned on, or +// when VizDisplayCompositor is turned on. +bool IsVizHitTestingEnabled() { + return base::FeatureList::IsEnabled(features::kEnableVizHitTest) || base::FeatureList::IsEnabled(kVizDisplayCompositor); } -bool IsVizHitTestingEnabled() { - return IsVizHitTestingDrawQuadEnabled() || - IsVizHitTestingSurfaceLayerEnabled(); +// VizHitTestDrawQuad is enabled when this feature is explicitly enabled on +// chrome://flags, or when VizHitTest is enabled but VizHitTestSurfaceLayer is +// turned off. +bool IsVizHitTestingDrawQuadEnabled() { + return GetFieldTrialParamValueByFeature(features::kEnableVizHitTest, + kProvider) == kDrawQuad || + (IsVizHitTestingEnabled() && !IsVizHitTestingSurfaceLayerEnabled()); } +// VizHitTestSurfaceLayer is enabled when this feature is explicitly enabled on +// chrome://flags, or when it is enabled by finch and chrome://flags does not +// conflict. bool IsVizHitTestingSurfaceLayerEnabled() { - return base::CommandLine::ForCurrentProcess()->HasSwitch( - switches::kUseVizHitTestSurfaceLayer) || - base::FeatureList::IsEnabled(kEnableVizHitTestSurfaceLayer); + return GetFieldTrialParamValueByFeature(features::kEnableVizHitTest, + kProvider) == kSurfaceLayer || + (IsVizHitTestingEnabled() && + GetFieldTrialParamValueByFeature(features::kEnableVizHitTest, + kProvider) != kDrawQuad && + base::FeatureList::IsEnabled(kEnableVizHitTestSurfaceLayer)); } bool IsUsingSkiaRenderer() {
diff --git a/components/viz/common/features.h b/components/viz/common/features.h index 1ddae70c..fd452ba 100644 --- a/components/viz/common/features.h +++ b/components/viz/common/features.h
@@ -12,6 +12,7 @@ namespace features { VIZ_COMMON_EXPORT extern const base::Feature kEnableSurfaceSynchronization; +VIZ_COMMON_EXPORT extern const base::Feature kEnableVizHitTest; VIZ_COMMON_EXPORT extern const base::Feature kEnableVizHitTestDrawQuad; VIZ_COMMON_EXPORT extern const base::Feature kEnableVizHitTestSurfaceLayer; VIZ_COMMON_EXPORT extern const base::Feature kUseSkiaRenderer;
diff --git a/components/viz/service/display/renderer_pixeltest.cc b/components/viz/service/display/renderer_pixeltest.cc index fc66a74..81464ed 100644 --- a/components/viz/service/display/renderer_pixeltest.cc +++ b/components/viz/service/display/renderer_pixeltest.cc
@@ -1723,9 +1723,6 @@ TYPED_TEST_SUITE(VideoRendererPixelTest, GLCapableRendererTypes); -// TODO(crbug.com/936822): Remove this once it passes with SkiaRenderer. -using VideoGLRendererPixelTest = VideoRendererPixelTest<GLRenderer>; - template <typename RendererType> class VideoRendererPixelHiLoTest : public VideoRendererPixelTest<RendererType>, public testing::WithParamInterface<bool> { @@ -1807,9 +1804,7 @@ ClippedYUVRect(); } -// TODO(crbug.com/936822): Enable this test for SkiaRenderer -// TYPED_TEST(VideoRendererPixelTest, OffsetYUVRect) { -TEST_F(VideoGLRendererPixelTest, OffsetYUVRect) { +TYPED_TEST(VideoRendererPixelTest, OffsetYUVRect) { gfx::Rect rect(this->device_viewport_size_); int id = 1; @@ -1923,23 +1918,19 @@ // Test that a YUV video doesn't bleed outside of its tex coords when the // tex coord rect is only a partial subrectangle of the coded contents. -// TODO(crbug.com/936822): Enable this test for SkiaRenderer -// TYPED_TEST(VideoRendererPixelTest, YUVEdgeBleed) { -TEST_F(VideoGLRendererPixelTest, YUVEdgeBleed) { +TYPED_TEST(VideoRendererPixelTest, YUVEdgeBleed) { RenderPassList pass_list; - CreateEdgeBleedPass(media::PIXEL_FORMAT_I420, gfx::ColorSpace::CreateJpeg(), - &pass_list); + this->CreateEdgeBleedPass(media::PIXEL_FORMAT_I420, + gfx::ColorSpace::CreateJpeg(), &pass_list); EXPECT_TRUE(this->RunPixelTest(&pass_list, base::FilePath(FILE_PATH_LITERAL("green.png")), cc::FuzzyPixelOffByOneComparator(true))); } -// TODO(crbug.com/936822): Enable this test for SkiaRenderer -// TYPED_TEST(VideoRendererPixelTest, YUVAEdgeBleed) { -TEST_F(VideoGLRendererPixelTest, YUVAEdgeBleed) { +TYPED_TEST(VideoRendererPixelTest, YUVAEdgeBleed) { RenderPassList pass_list; - CreateEdgeBleedPass(media::PIXEL_FORMAT_I420A, - gfx::ColorSpace::CreateREC601(), &pass_list); + this->CreateEdgeBleedPass(media::PIXEL_FORMAT_I420A, + gfx::ColorSpace::CreateREC601(), &pass_list); // Set the output color space to match the input primaries and transfer. pass_list.back()->color_space = gfx::ColorSpace(gfx::ColorSpace::PrimaryID::SMPTE170M, @@ -1974,9 +1965,7 @@ cc::FuzzyPixelOffByOneComparator(true))); } -// TODO(crbug.com/936822): Enable this test for SkiaRenderer -// TYPED_TEST(VideoRendererPixelTest, SimpleYUVARect) { -TEST_F(VideoGLRendererPixelTest, SimpleYUVARect) { +TYPED_TEST(VideoRendererPixelTest, SimpleYUVARect) { gfx::Rect rect(this->device_viewport_size_); int id = 1;
diff --git a/components/viz/service/display/skia_renderer.cc b/components/viz/service/display/skia_renderer.cc index 0e1baa3..bb80993b 100644 --- a/components/viz/service/display/skia_renderer.cc +++ b/components/viz/service/display/skia_renderer.cc
@@ -295,6 +295,44 @@ return nearest_neighbor ? kNone_SkFilterQuality : kLow_SkFilterQuality; } +// Returns kFast if sampling outside of vis_tex_coords due to AA or bilerp will +// not go outside of the content area, or if the content area is the full image +// (in which case hardware clamping handles it automatically). Different quad +// types have different rules for the content area within the image. +SkCanvas::SrcRectConstraint GetTextureConstraint( + const SkImage* image, + const gfx::RectF& vis_tex_coords, + const gfx::RectF& valid_texel_bounds) { + bool fills_left = valid_texel_bounds.x() <= 0.f; + bool fills_right = valid_texel_bounds.right() >= image->width(); + bool fills_top = valid_texel_bounds.y() <= 0.f; + bool fills_bottom = valid_texel_bounds.y() >= image->height(); + if (fills_left && fills_right && fills_top && fills_bottom) { + // The entire image is contained in the content area, so hardware clamping + // ensures only content texels are sampled + return SkCanvas::kFast_SrcRectConstraint; + } + + gfx::RectF safe_texels = valid_texel_bounds; + safe_texels.Inset(0.5f, 0.5f); + + // Check each axis independently; tile quads may only need clamping on one + // side (e.g. right or bottom) and this logic doesn't fully match a simple + // contains() check. + if ((!fills_left && vis_tex_coords.x() < safe_texels.x()) || + (!fills_right && vis_tex_coords.right() > safe_texels.right())) { + return SkCanvas::kStrict_SrcRectConstraint; + } + if ((!fills_top && vis_tex_coords.y() < safe_texels.y()) || + (!fills_bottom && vis_tex_coords.bottom() > safe_texels.bottom())) { + return SkCanvas::kStrict_SrcRectConstraint; + } + + // The texture coordinates are far enough from the content area that even with + // bilerp and AA, it won't sample outside the content area + return SkCanvas::kFast_SrcRectConstraint; +} + // Return a color filter that multiplies the incoming color by the fixed alpha sk_sp<SkColorFilter> MakeOpacityFilter(float alpha, sk_sp<SkColorFilter> in) { SkColor alpha_as_color = SkColorSetA(SK_ColorWHITE, 255 * alpha); @@ -941,6 +979,49 @@ params.aa_flags, params.draw_region.has_value()}); } +SkCanvas::SrcRectConstraint SkiaRenderer::ResolveTextureConstraints( + const SkImage* image, + const gfx::RectF& valid_texel_bounds, + DrawQuadParams* params) { + if (params->aa_flags == SkCanvas::kNone_QuadAAFlags && + params->filter_quality == kNone_SkFilterQuality) { + // Non-AA and no bilinear filtering so rendering won't filter outside the + // provided texture coordinates. + return SkCanvas::kFast_SrcRectConstraint; + } + + // Resolve texture coordinates against the valid content area of the image + SkCanvas::SrcRectConstraint constraint = + GetTextureConstraint(image, params->vis_tex_coords, valid_texel_bounds); + + // Skia clamps to the provided texture coordinates, not the content_area. If + // there is a mismatch, have to update the draw params to account for the new + // constraint + if (constraint == SkCanvas::kFast_SrcRectConstraint || + valid_texel_bounds == params->vis_tex_coords) { + return constraint; + } + + // To get |valid_texel_bounds| as the constraint, it must be sent as the tex + // coords. To draw the right shape, store |visible_rect| as the |draw_region| + // and change the visible rect so that the mapping from |visible_rect| to + // |valid_texel_bounds| causes |draw_region| to map to original + // |vis_tex_coords| + if (!params->draw_region.has_value()) { + params->draw_region.emplace(gfx::QuadF(params->visible_rect)); + } + + // Preserve the src-to-dst transformation for the padded texture coords + SkMatrix src_to_dst = SkMatrix::MakeRectToRect( + gfx::RectFToSkRect(params->vis_tex_coords), + gfx::RectFToSkRect(params->visible_rect), SkMatrix::kFill_ScaleToFit); + params->visible_rect = gfx::SkRectToRectF( + src_to_dst.mapRect(gfx::RectFToSkRect(valid_texel_bounds))); + params->vis_tex_coords = valid_texel_bounds; + + return SkCanvas::kStrict_SrcRectConstraint; +} + bool SkiaRenderer::MustFlushBatchedQuads(const DrawQuad* new_quad, const DrawQuadParams& params) { if (batched_quads_.empty()) @@ -969,14 +1050,25 @@ } void SkiaRenderer::AddQuadToBatch(const SkImage* image, + const gfx::RectF& valid_texel_bounds, DrawQuadParams* params) { + SkCanvas::SrcRectConstraint constraint = + ResolveTextureConstraints(image, valid_texel_bounds, params); + // Last check for flushing the batch, since constraint can't be known until + // the last minute. + if (!batched_quads_.empty() && batched_quad_state_.constraint != constraint) { + FlushBatchedQuads(); + } + // Configure batch state if it's the first if (batched_quads_.empty()) { batched_quad_state_.scissor_rect = params->scissor_rect; batched_quad_state_.rounded_corner_bounds = params->rounded_corner_bounds; batched_quad_state_.blend_mode = params->blend_mode; batched_quad_state_.filter_quality = params->filter_quality; + batched_quad_state_.constraint = constraint; } + DCHECK(batched_quad_state_.constraint == constraint); // Add entry, with optional clip quad and shared transform if (params->draw_region.has_value()) { @@ -1008,7 +1100,8 @@ paint.setBlendMode(batched_quad_state_.blend_mode); current_canvas_->experimental_DrawEdgeAAImageSet( &batched_quads_.front(), batched_quads_.size(), - &batched_draw_regions_.front(), &batched_cdt_matrices_.front(), &paint); + &batched_draw_regions_.front(), &batched_cdt_matrices_.front(), &paint, + batched_quad_state_.constraint); batched_quads_.clear(); batched_draw_regions_.clear(); @@ -1033,6 +1126,7 @@ } void SkiaRenderer::DrawSingleImage(const SkImage* image, + const gfx::RectF& valid_texel_bounds, const SkPaint& paint, DrawQuadParams* params) { DCHECK(batched_quads_.empty()); @@ -1041,12 +1135,16 @@ SkAutoCanvasRestore acr(current_canvas_, true /* do_save */); PrepareCanvas(params->scissor_rect, params->rounded_corner_bounds, ¶ms->content_device_transform); + + SkCanvas::SrcRectConstraint constraint = + ResolveTextureConstraints(image, valid_texel_bounds, params); + // Use -1 for matrix index since the cdt is set on the canvas. SkCanvas::ImageSetEntry entry = MakeEntry(image, -1, *params); const SkPoint* draw_region = params->draw_region.has_value() ? params->draw_region->points : nullptr; current_canvas_->experimental_DrawEdgeAAImageSet(&entry, 1, draw_region, - nullptr, &paint); + nullptr, &paint, constraint); } void SkiaRenderer::DrawDebugBorderQuad(const DebugBorderDrawQuad* quad, @@ -1157,7 +1255,15 @@ image->width(), image->height()); params->vis_tex_coords = cc::MathUtil::ScaleRectProportional( uv_rect, gfx::RectF(quad->rect), params->visible_rect); - AddQuadToBatch(image, params); + + // Use provided resource size if not empty, otherwise use the full image size + // as the content area + gfx::RectF valid_texel_bounds = + quad->resource_size_in_pixels().IsEmpty() + ? gfx::RectF(image->width(), image->height()) + : gfx::RectF(gfx::SizeF(quad->resource_size_in_pixels())); + + AddQuadToBatch(image, valid_texel_bounds, params); } void SkiaRenderer::DrawTextureQuad(const TextureDrawQuad* quad, @@ -1175,6 +1281,13 @@ params->vis_tex_coords = cc::MathUtil::ScaleRectProportional( uv_rect, gfx::RectF(quad->rect), params->visible_rect); + // Use provided resource size if not empty, otherwise use the full image size + // as the content area + gfx::RectF valid_texel_bounds = + quad->resource_size_in_pixels().IsEmpty() + ? gfx::RectF(image->width(), image->height()) + : gfx::RectF(gfx::SizeF(quad->resource_size_in_pixels())); + // There are two scenarios where a texture quad cannot be put into a batch: // 1. It needs to be blended with a constant background color. // 2. The vertex opacities are not all 1s. @@ -1187,7 +1300,7 @@ if (!blend_background && !vertex_alpha) { // This is a simple texture draw and can go into the batching system DCHECK(!MustFlushBatchedQuads(quad, *params)); - AddQuadToBatch(image, params); + AddQuadToBatch(image, valid_texel_bounds, params); return; } // This needs a color filter for background blending and/or a mask filter @@ -1264,7 +1377,7 @@ // Override the default paint opacity since it may not be params.opacity paint.setAlphaf(quad_alpha); - DrawSingleImage(image, paint, params); + DrawSingleImage(image, valid_texel_bounds, paint, params); } void SkiaRenderer::DrawTileDrawQuad(const TileDrawQuad* quad, @@ -1283,7 +1396,21 @@ params->vis_tex_coords = cc::MathUtil::ScaleRectProportional( quad->tex_coord_rect, gfx::RectF(quad->rect), params->visible_rect); - AddQuadToBatch(image, params); + // When a tile is at the right or bottom edge of the entire tiled area, its + // images won't be fully filled so use the unclipped texture coords. On + // interior tiles or left/top tiles, the image has been filled with + // overlapping content so the entire image is valid for sampling. + gfx::RectF valid_texel_bounds(gfx::SizeF(quad->texture_size)); + if (quad->IsRightEdge()) { + // Restrict the width to match tex coords + valid_texel_bounds.set_width(quad->tex_coord_rect.width()); + } + if (quad->IsBottomEdge()) { + // Restrict the height to match tex coords + valid_texel_bounds.set_height(quad->tex_coord_rect.height()); + } + + AddQuadToBatch(image, valid_texel_bounds, params); } void SkiaRenderer::DrawYUVVideoQuad(const YUVVideoDrawQuad* quad, @@ -1321,7 +1448,10 @@ if (color_filter) paint.setColorFilter(color_filter); - DrawSingleImage(image, paint, params); + // Use provided, unclipped texture coordinates as the content area, which will + // force coord clamping unless the geometry was clipped, or they span the + // entire YUV image. + DrawSingleImage(image, quad->ya_tex_coord_rect, paint, params); } void SkiaRenderer::DrawUnsupportedQuad(const DrawQuad* quad, @@ -1567,13 +1697,16 @@ params->vis_tex_coords = cc::MathUtil::ScaleRectProportional( quad->tex_coord_rect, gfx::RectF(quad->rect), params->visible_rect); + gfx::RectF valid_texel_bounds(content_image->width(), + content_image->height()); + if (params->filter_quality < kMedium_SkFilterQuality && !rpdq_params.image_filter && !rpdq_params.backdrop_filter && !rpdq_params.mask_image) { // We've checked enough to know that this is a plain textured draw that // is compatible with any batched images, so preserve that DCHECK(!MustFlushBatchedQuads(quad, *params)); - AddQuadToBatch(content_image, params); + AddQuadToBatch(content_image, valid_texel_bounds, params); return; } @@ -1592,7 +1725,7 @@ &rpdq_params.mask_to_quad_matrix))); DCHECK(paint.getMaskFilter()); } - DrawSingleImage(content_image, paint, params); + DrawSingleImage(content_image, valid_texel_bounds, paint, params); return; } @@ -1656,11 +1789,13 @@ SkPaint content_paint; content_paint.setFilterQuality(paint.getFilterQuality()); + SkCanvas::SrcRectConstraint constraint = + ResolveTextureConstraints(content_image, valid_texel_bounds, params); SkCanvas::ImageSetEntry entry = MakeEntry(content_image, -1, *params); const SkPoint* draw_region = params->draw_region.has_value() ? params->draw_region->points : nullptr; - current_canvas_->experimental_DrawEdgeAAImageSet(&entry, 1, draw_region, - nullptr, &content_paint); + current_canvas_->experimental_DrawEdgeAAImageSet( + &entry, 1, draw_region, nullptr, &content_paint, constraint); // And the saved layer will be auto-restored when |acr| is destructed }
diff --git a/components/viz/service/display/skia_renderer.h b/components/viz/service/display/skia_renderer.h index 95106659..1c6afdc6 100644 --- a/components/viz/service/display/skia_renderer.h +++ b/components/viz/service/display/skia_renderer.h
@@ -116,16 +116,25 @@ SkCanvas::ImageSetEntry MakeEntry(const SkImage* image, int matrix_index, const DrawQuadParams& params); + // Returns overall constraint to pass to Skia, and modifies |params| to + // emulate content area clamping different from the provided texture coords. + SkCanvas::SrcRectConstraint ResolveTextureConstraints( + const SkImage* image, + const gfx::RectF& valid_texel_bounds, + DrawQuadParams* params); bool MustFlushBatchedQuads(const DrawQuad* new_quad, const DrawQuadParams& params); - void AddQuadToBatch(const SkImage* image, DrawQuadParams* params); + void AddQuadToBatch(const SkImage* image, + const gfx::RectF& valid_texel_bounds, + DrawQuadParams* params); void FlushBatchedQuads(); // Utility to draw a single quad as a filled color void DrawColoredQuad(SkColor color, DrawQuadParams* params); // Utility to make a single ImageSetEntry and draw it with the complex paint. void DrawSingleImage(const SkImage* image, + const gfx::RectF& valid_texel_bounds, const SkPaint& paint, DrawQuadParams* params); @@ -218,6 +227,7 @@ base::Optional<gfx::RRectF> rounded_corner_bounds; SkBlendMode blend_mode; SkFilterQuality filter_quality; + SkCanvas::SrcRectConstraint constraint; BatchedQuadState(); };
diff --git a/components/viz/service/surfaces/surface_allocation_group.cc b/components/viz/service/surfaces/surface_allocation_group.cc index 6e60ce22..a61c55b 100644 --- a/components/viz/service/surfaces/surface_allocation_group.cc +++ b/components/viz/service/surfaces/surface_allocation_group.cc
@@ -150,8 +150,8 @@ surface->TakeActiveLatencyInfo(out); auto it = FindLatestSurfaceUpTo(surface->surface_id()); DCHECK_EQ(*it, surface); - for (--it; it >= surfaces_.begin() && !(*it)->is_latency_info_taken(); --it) - (*it)->TakeActiveAndPendingLatencyInfo(out); + while (it > surfaces_.begin() && !(*it)->is_latency_info_taken()) + (*--it)->TakeActiveAndPendingLatencyInfo(out); } void SurfaceAllocationGroup::OnFirstSurfaceActivation(Surface* surface) {
diff --git a/components/webcrypto/algorithm_dispatch.cc b/components/webcrypto/algorithm_dispatch.cc index 20e7df8..87058546 100644 --- a/components/webcrypto/algorithm_dispatch.cc +++ b/components/webcrypto/algorithm_dispatch.cc
@@ -317,12 +317,6 @@ import_algorithm, extractable, usages, derived_key); } -std::unique_ptr<blink::WebCryptoDigestor> CreateDigestor( - blink::WebCryptoAlgorithmId algorithm) { - crypto::EnsureOpenSSLInit(); - return CreateDigestorImplementation(algorithm); -} - bool SerializeKeyForClone(const blink::WebCryptoKey& key, blink::WebVector<uint8_t>* key_data) { const AlgorithmImplementation* impl = nullptr;
diff --git a/components/webcrypto/algorithm_dispatch.h b/components/webcrypto/algorithm_dispatch.h index 713e03f..fe3d9e5d 100644 --- a/components/webcrypto/algorithm_dispatch.h +++ b/components/webcrypto/algorithm_dispatch.h
@@ -114,9 +114,6 @@ blink::WebCryptoKeyUsageMask usages, blink::WebCryptoKey* derived_key); -std::unique_ptr<blink::WebCryptoDigestor> CreateDigestor( - blink::WebCryptoAlgorithmId algorithm); - bool SerializeKeyForClone(const blink::WebCryptoKey& key, blink::WebVector<uint8_t>* key_data);
diff --git a/components/webcrypto/algorithm_implementations.h b/components/webcrypto/algorithm_implementations.h index 94f0bb9..186256c 100644 --- a/components/webcrypto/algorithm_implementations.h +++ b/components/webcrypto/algorithm_implementations.h
@@ -14,9 +14,6 @@ class AlgorithmImplementation; -std::unique_ptr<blink::WebCryptoDigestor> CreateDigestorImplementation( - blink::WebCryptoAlgorithmId algorithm); - std::unique_ptr<AlgorithmImplementation> CreateShaImplementation(); std::unique_ptr<AlgorithmImplementation> CreateAesCbcImplementation(); std::unique_ptr<AlgorithmImplementation> CreateAesCtrImplementation();
diff --git a/components/webcrypto/algorithms/sha.cc b/components/webcrypto/algorithms/sha.cc index 60c64f9..1b44bf5b60 100644 --- a/components/webcrypto/algorithms/sha.cc +++ b/components/webcrypto/algorithms/sha.cc
@@ -18,100 +18,40 @@ namespace { -// Implementation of blink::WebCryptoDigester, an internal Blink detail not -// part of WebCrypto, that allows chunks of data to be streamed in before -// computing a SHA-* digest (as opposed to ShaImplementation, which computes -// digests over complete messages) -class DigestorImpl : public blink::WebCryptoDigestor { - public: - explicit DigestorImpl(blink::WebCryptoAlgorithmId algorithm_id) - : initialized_(false), - algorithm_id_(algorithm_id) {} - - bool Consume(const unsigned char* data, unsigned int size) override { - return ConsumeWithStatus(data, size).IsSuccess(); - } - - Status ConsumeWithStatus(const unsigned char* data, unsigned int size) { - crypto::OpenSSLErrStackTracer err_tracer(FROM_HERE); - Status error = Init(); - if (!error.IsSuccess()) - return error; - - if (!EVP_DigestUpdate(digest_context_.get(), data, size)) - return Status::OperationError(); - - return Status::Success(); - } - - bool Finish(unsigned char*& result_data, - unsigned int& result_data_size) override { - Status error = FinishInternal(result_, &result_data_size); - if (!error.IsSuccess()) - return false; - result_data = result_; - return true; - } - - Status FinishWithVectorAndStatus(std::vector<uint8_t>* result) { - const size_t hash_expected_size = EVP_MD_CTX_size(digest_context_.get()); - result->resize(hash_expected_size); - unsigned int hash_buffer_size; // ignored - return FinishInternal(result->data(), &hash_buffer_size); - } - - private: - Status Init() { - if (initialized_) - return Status::Success(); - - const EVP_MD* digest_algorithm = GetDigest(algorithm_id_); - if (!digest_algorithm) - return Status::ErrorUnsupported(); - - if (!EVP_DigestInit_ex(digest_context_.get(), digest_algorithm, nullptr)) - return Status::OperationError(); - - initialized_ = true; - return Status::Success(); - } - - Status FinishInternal(unsigned char* result, unsigned int* result_size) { - crypto::OpenSSLErrStackTracer err_tracer(FROM_HERE); - Status error = Init(); - if (!error.IsSuccess()) - return error; - - const size_t hash_expected_size = EVP_MD_CTX_size(digest_context_.get()); - if (hash_expected_size == 0) - return Status::ErrorUnexpected(); - DCHECK_LE(hash_expected_size, static_cast<unsigned>(EVP_MAX_MD_SIZE)); - - if (!EVP_DigestFinal_ex(digest_context_.get(), result, result_size) || - *result_size != hash_expected_size) - return Status::OperationError(); - - return Status::Success(); - } - - bool initialized_; - bssl::ScopedEVP_MD_CTX digest_context_; - blink::WebCryptoAlgorithmId algorithm_id_; - unsigned char result_[EVP_MAX_MD_SIZE]; -}; - class ShaImplementation : public AlgorithmImplementation { public: Status Digest(const blink::WebCryptoAlgorithm& algorithm, const CryptoData& data, std::vector<uint8_t>* buffer) const override { - DigestorImpl digestor(algorithm.Id()); - Status error = digestor.ConsumeWithStatus(data.bytes(), data.byte_length()); // http://crbug.com/366427: the spec does not define any other failures for // digest, so none of the subsequent errors are spec compliant. - if (!error.IsSuccess()) - return error; - return digestor.FinishWithVectorAndStatus(buffer); + bssl::ScopedEVP_MD_CTX digest_context; + crypto::OpenSSLErrStackTracer err_tracer(FROM_HERE); + + const EVP_MD* digest_algorithm = GetDigest(algorithm.Id()); + if (!digest_algorithm) + return Status::ErrorUnsupported(); + + if (!EVP_DigestInit_ex(digest_context.get(), digest_algorithm, nullptr)) + return Status::OperationError(); + + if (!EVP_DigestUpdate(digest_context.get(), data.bytes(), + data.byte_length())) + return Status::OperationError(); + + const size_t hash_expected_size = EVP_MD_CTX_size(digest_context.get()); + if (hash_expected_size == 0) + return Status::ErrorUnexpected(); + DCHECK_LE(hash_expected_size, static_cast<unsigned>(EVP_MAX_MD_SIZE)); + + buffer->resize(hash_expected_size); + unsigned result_size; // ignored + if (!EVP_DigestFinal_ex(digest_context.get(), buffer->data(), + &result_size) || + result_size != hash_expected_size) + return Status::OperationError(); + + return Status::Success(); } }; @@ -121,9 +61,4 @@ return std::make_unique<ShaImplementation>(); } -std::unique_ptr<blink::WebCryptoDigestor> CreateDigestorImplementation( - blink::WebCryptoAlgorithmId algorithm) { - return std::unique_ptr<blink::WebCryptoDigestor>(new DigestorImpl(algorithm)); -} - } // namespace webcrypto
diff --git a/components/webcrypto/algorithms/sha_unittest.cc b/components/webcrypto/algorithms/sha_unittest.cc index 8363ca36..80ec9ae 100644 --- a/components/webcrypto/algorithms/sha_unittest.cc +++ b/components/webcrypto/algorithms/sha_unittest.cc
@@ -43,44 +43,6 @@ } } -TEST_F(WebCryptoShaTest, DigestSampleSetsInChunks) { - base::ListValue tests; - ASSERT_TRUE(ReadJsonTestFileToList("sha.json", &tests)); - - for (size_t test_index = 0; test_index < tests.GetSize(); ++test_index) { - SCOPED_TRACE(test_index); - base::DictionaryValue* test; - ASSERT_TRUE(tests.GetDictionary(test_index, &test)); - - blink::WebCryptoAlgorithm test_algorithm = - GetDigestAlgorithm(test, "algorithm"); - std::vector<uint8_t> test_input = GetBytesFromHexString(test, "input"); - std::vector<uint8_t> test_output = GetBytesFromHexString(test, "output"); - - // Test the chunk version of the digest functions. Test with 129 byte chunks - // because the SHA-512 chunk size is 128 bytes. - unsigned char* output; - unsigned int output_length; - static const size_t kChunkSizeBytes = 129; - size_t length = test_input.size(); - std::unique_ptr<blink::WebCryptoDigestor> digestor( - CreateDigestor(test_algorithm.Id())); - auto begin = test_input.begin(); - size_t chunk_index = 0; - while (begin != test_input.end()) { - size_t chunk_length = std::min(kChunkSizeBytes, length - chunk_index); - std::vector<uint8_t> chunk(begin, begin + chunk_length); - ASSERT_TRUE(chunk.size() > 0); - EXPECT_TRUE(digestor->Consume(chunk.data(), - static_cast<unsigned int>(chunk.size()))); - chunk_index = chunk_index + chunk_length; - begin = begin + chunk_length; - } - EXPECT_TRUE(digestor->Finish(output, output_length)); - EXPECT_BYTES_EQ(test_output, CryptoData(output, output_length)); - } -} - } // namespace } // namespace webcrypto
diff --git a/components/webcrypto/webcrypto_impl.cc b/components/webcrypto/webcrypto_impl.cc index ffd52546..67ad546 100644 --- a/components/webcrypto/webcrypto_impl.cc +++ b/components/webcrypto/webcrypto_impl.cc
@@ -822,11 +822,6 @@ } } -std::unique_ptr<blink::WebCryptoDigestor> WebCryptoImpl::CreateDigestor( - blink::WebCryptoAlgorithmId algorithm_id) { - return webcrypto::CreateDigestor(algorithm_id); -} - bool WebCryptoImpl::DeserializeKeyForClone( const blink::WebCryptoKeyAlgorithm& algorithm, blink::WebCryptoKeyType type,
diff --git a/components/webcrypto/webcrypto_impl.h b/components/webcrypto/webcrypto_impl.h index 36156aa..1dd6021 100644 --- a/components/webcrypto/webcrypto_impl.h +++ b/components/webcrypto/webcrypto_impl.h
@@ -108,13 +108,6 @@ blink::WebCryptoResult result, scoped_refptr<base::SingleThreadTaskRunner> task_runner) override; - // This method returns a digestor object that can be used to synchronously - // compute a digest one chunk at a time. Thus, the consume does not need to - // hold onto a large buffer with all the data to digest. Chunks can be given - // one at a time and the digest will be computed piecemeal. - std::unique_ptr<blink::WebCryptoDigestor> CreateDigestor( - blink::WebCryptoAlgorithmId algorithm_id) override; - bool DeserializeKeyForClone(const blink::WebCryptoKeyAlgorithm& algorithm, blink::WebCryptoKeyType type, bool extractable,
diff --git a/content/browser/accessibility/accessibility_tree_formatter_blink.cc b/content/browser/accessibility/accessibility_tree_formatter_blink.cc index ecbc5a0..272fab4 100644 --- a/content/browser/accessibility/accessibility_tree_formatter_blink.cc +++ b/content/browser/accessibility/accessibility_tree_formatter_blink.cc
@@ -85,6 +85,10 @@ return ui::ToString(static_cast<ax::mojom::Restriction>(value)); case ax::mojom::IntAttribute::kSortDirection: return ui::ToString(static_cast<ax::mojom::SortDirection>(value)); + case ax::mojom::IntAttribute::kTextOverlineStyle: + case ax::mojom::IntAttribute::kTextStrikethroughStyle: + case ax::mojom::IntAttribute::kTextUnderlineStyle: + return ui::ToString(static_cast<ax::mojom::TextDecorationStyle>(value)); case ax::mojom::IntAttribute::kTextDirection: return ui::ToString(static_cast<ax::mojom::TextDirection>(value)); case ax::mojom::IntAttribute::kTextPosition:
diff --git a/content/browser/accessibility/dump_accessibility_tree_browsertest.cc b/content/browser/accessibility/dump_accessibility_tree_browsertest.cc index abb70db8..b3b913e6 100644 --- a/content/browser/accessibility/dump_accessibility_tree_browsertest.cc +++ b/content/browser/accessibility/dump_accessibility_tree_browsertest.cc
@@ -1871,6 +1871,11 @@ RunHtmlTest(FILE_PATH_LITERAL("table-multiple-row-and-column-headers.html")); } +IN_PROC_BROWSER_TEST_P(DumpAccessibilityTreeTest, + AccessibilityTextDecorationStyles) { + RunHtmlTest(FILE_PATH_LITERAL("text-decoration-styles.html")); +} + IN_PROC_BROWSER_TEST_P(DumpAccessibilityTreeTest, AccessibilityTextarea) { RunHtmlTest(FILE_PATH_LITERAL("textarea.html")); }
diff --git a/content/browser/blob_storage/blob_transport_host_unittest.cc b/content/browser/blob_storage/blob_transport_host_unittest.cc deleted file mode 100644 index 9dcaaf9..0000000 --- a/content/browser/blob_storage/blob_transport_host_unittest.cc +++ /dev/null
@@ -1,523 +0,0 @@ -// Copyright 2015 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 <stddef.h> -#include <stdint.h> -#include <string.h> - -#include "base/bind.h" -#include "base/logging.h" -#include "base/memory/shared_memory.h" -#include "base/run_loop.h" -#include "content/public/test/test_browser_thread_bundle.h" -#include "storage/browser/blob/blob_data_builder.h" -#include "storage/browser/blob/blob_data_handle.h" -#include "storage/browser/blob/blob_storage_context.h" -#include "storage/browser/blob/blob_transport_host.h" -#include "storage/common/blob_storage/blob_storage_constants.h" -#include "testing/gtest/include/gtest/gtest.h" - -namespace content { -namespace { -const std::string kBlobUUID = "blobUUIDYAY"; -const std::string kContentType = "content_type"; -const std::string kContentDisposition = "content_disposition"; -const std::string kCompletedBlobUUID = "completedBlob"; -const std::string kCompletedBlobData = "completedBlobData"; - -const size_t kTestBlobStorageIPCThresholdBytes = 5; -const size_t kTestBlobStorageMaxSharedMemoryBytes = 20; - -const size_t kTestBlobStorageMaxBlobMemorySize = 400; -const uint64_t kTestBlobStorageMaxDiskSpace = 4000; -const uint64_t kTestBlobStorageMinFileSizeBytes = 10; -const uint64_t kTestBlobStorageMaxFileSizeBytes = 100; - -void PopulateBytes(char* bytes, size_t length) { - for (size_t i = 0; i < length; i++) { - bytes[i] = static_cast<char>(i); - } -} - -void AddMemoryItem(size_t length, std::vector<network::DataElement>* out) { - network::DataElement bytes; - bytes.SetToBytesDescription(length); - out->push_back(std::move(bytes)); -} - -void AddShortcutMemoryItem(size_t length, - std::vector<network::DataElement>* out) { - network::DataElement bytes; - bytes.SetToAllocatedBytes(length); - PopulateBytes(bytes.mutable_bytes(), length); - out->push_back(std::move(bytes)); -} - -void AddShortcutMemoryItem(size_t length, storage::BlobDataBuilder* out) { - network::DataElement bytes; - bytes.SetToAllocatedBytes(length); - PopulateBytes(bytes.mutable_bytes(), length); - out->AppendData(bytes.bytes(), length); -} - -void AddBlobItem(std::vector<network::DataElement>* out) { - network::DataElement blob; - blob.SetToBlob(kCompletedBlobUUID); - out->push_back(std::move(blob)); -} -} // namespace - -class BlobTransportHostTest : public testing::Test { - public: - BlobTransportHostTest() - : status_code_(storage::BlobStatus::ERR_INVALID_CONSTRUCTION_ARGUMENTS), - request_called_(false) {} - ~BlobTransportHostTest() override {} - - void SetUp() override { - status_code_ = storage::BlobStatus::ERR_INVALID_CONSTRUCTION_ARGUMENTS; - request_called_ = false; - requests_.clear(); - memory_handles_.clear(); - storage::BlobStorageLimits limits; - limits.max_ipc_memory_size = kTestBlobStorageIPCThresholdBytes; - limits.max_shared_memory_size = kTestBlobStorageMaxSharedMemoryBytes; - limits.max_blob_in_memory_space = kTestBlobStorageMaxBlobMemorySize; - limits.desired_max_disk_space = kTestBlobStorageMaxDiskSpace; - limits.effective_max_disk_space = kTestBlobStorageMaxDiskSpace; - limits.min_page_file_size = kTestBlobStorageMinFileSizeBytes; - limits.max_file_size = kTestBlobStorageMaxFileSizeBytes; - context_.mutable_memory_controller()->set_limits_for_testing(limits); - storage::BlobDataBuilder builder(kCompletedBlobUUID); - builder.AppendData(kCompletedBlobData); - completed_blob_handle_ = context_.AddFinishedBlob(builder); - EXPECT_EQ(storage::BlobStatus::DONE, - completed_blob_handle_->GetBlobStatus()); - } - - void StatusCallback(storage::BlobStatus status) { - status_called_ = true; - status_code_ = status; - } - - void RequestMemoryCallback( - std::vector<storage::BlobItemBytesRequest> requests, - std::vector<base::SharedMemoryHandle> shared_memory_handles, - std::vector<base::File> files) { - requests_ = std::move(requests); - memory_handles_ = std::move(shared_memory_handles); - request_called_ = true; - } - - storage::BlobStatus BuildBlobAsync( - const std::string& uuid, - const std::vector<network::DataElement>& descriptions, - std::unique_ptr<storage::BlobDataHandle>* storage) { - EXPECT_NE(storage, nullptr); - request_called_ = false; - status_called_ = false; - *storage = host_.StartBuildingBlob( - uuid, kContentType, kContentDisposition, descriptions, &context_, - nullptr, - base::Bind(&BlobTransportHostTest::RequestMemoryCallback, - base::Unretained(this)), - base::Bind(&BlobTransportHostTest::StatusCallback, - base::Unretained(this))); - if (status_called_) - return status_code_; - else - return context_.GetBlobStatus(uuid); - } - - storage::BlobStatus GetBlobStatus(const std::string& uuid) const { - return context_.GetBlobStatus(uuid); - } - - bool IsBeingBuiltInContext(const std::string& uuid) const { - return BlobStatusIsPending(context_.GetBlobStatus(uuid)); - } - - TestBrowserThreadBundle browser_thread_bundle_; - storage::BlobStorageContext context_; - storage::BlobTransportHost host_; - bool status_called_; - storage::BlobStatus status_code_; - - bool request_called_; - std::vector<storage::BlobItemBytesRequest> requests_; - std::vector<base::SharedMemoryHandle> memory_handles_; - std::unique_ptr<storage::BlobDataHandle> completed_blob_handle_; -}; - -// The 'shortcut' method is when the data is included in the initial IPCs and -// the browser uses that instead of requesting the memory. -TEST_F(BlobTransportHostTest, TestShortcut) { - std::vector<network::DataElement> descriptions; - - AddShortcutMemoryItem(10, &descriptions); - AddBlobItem(&descriptions); - AddShortcutMemoryItem(300, &descriptions); - - storage::BlobDataBuilder expected(kBlobUUID); - expected.set_content_type(kContentType); - expected.set_content_disposition(kContentDisposition); - AddShortcutMemoryItem(10, &expected); - expected.AppendData(kCompletedBlobData); - AddShortcutMemoryItem(300, &expected); - - std::unique_ptr<storage::BlobDataHandle> handle; - EXPECT_EQ(storage::BlobStatus::DONE, - BuildBlobAsync(kBlobUUID, descriptions, &handle)); - - EXPECT_FALSE(request_called_); - EXPECT_EQ(0u, host_.blob_building_count()); - EXPECT_FALSE(handle->IsBeingBuilt()); - ASSERT_FALSE(handle->IsBroken()); - std::unique_ptr<storage::BlobDataSnapshot> data = handle->CreateSnapshot(); - EXPECT_EQ(expected, *data); - data.reset(); - handle.reset(); - base::RunLoop().RunUntilIdle(); -}; - -TEST_F(BlobTransportHostTest, TestShortcutNoRoom) { - std::vector<network::DataElement> descriptions; - - AddShortcutMemoryItem(10, &descriptions); - AddBlobItem(&descriptions); - AddShortcutMemoryItem(5000, &descriptions); - - std::unique_ptr<storage::BlobDataHandle> handle; - EXPECT_EQ(storage::BlobStatus::ERR_OUT_OF_MEMORY, - BuildBlobAsync(kBlobUUID, descriptions, &handle)); - - EXPECT_FALSE(request_called_); - EXPECT_EQ(0u, host_.blob_building_count()); -}; - -TEST_F(BlobTransportHostTest, TestSingleSharedMemRequest) { - std::vector<network::DataElement> descriptions; - const size_t kSize = kTestBlobStorageIPCThresholdBytes + 1; - AddMemoryItem(kSize, &descriptions); - - std::unique_ptr<storage::BlobDataHandle> handle; - EXPECT_EQ(storage::BlobStatus::PENDING_TRANSPORT, - BuildBlobAsync(kBlobUUID, descriptions, &handle)); - EXPECT_TRUE(handle); - EXPECT_EQ(storage::BlobStatus::PENDING_TRANSPORT, handle->GetBlobStatus()); - - EXPECT_TRUE(request_called_); - EXPECT_EQ(1u, host_.blob_building_count()); - ASSERT_EQ(1u, requests_.size()); - request_called_ = false; - - EXPECT_EQ(storage::BlobItemBytesRequest::CreateSharedMemoryRequest( - 0, 0, 0, kSize, 0, 0), - requests_.at(0)); -}; - -TEST_F(BlobTransportHostTest, TestMultipleSharedMemRequests) { - std::vector<network::DataElement> descriptions; - const size_t kSize = kTestBlobStorageMaxSharedMemoryBytes + 1; - const char kFirstBlockByte = 7; - const char kSecondBlockByte = 19; - AddMemoryItem(kSize, &descriptions); - - storage::BlobDataBuilder expected(kBlobUUID); - expected.set_content_type(kContentType); - expected.set_content_disposition(kContentDisposition); - char data[kSize]; - memset(data, kFirstBlockByte, kTestBlobStorageMaxSharedMemoryBytes); - expected.AppendData(data, kTestBlobStorageMaxSharedMemoryBytes); - expected.AppendData(&kSecondBlockByte, 1); - - std::unique_ptr<storage::BlobDataHandle> handle; - EXPECT_EQ(storage::BlobStatus::PENDING_TRANSPORT, - BuildBlobAsync(kBlobUUID, descriptions, &handle)); - - EXPECT_TRUE(request_called_); - EXPECT_EQ(1u, host_.blob_building_count()); - ASSERT_EQ(1u, requests_.size()); - request_called_ = false; - - // We need to grab a duplicate handle so we can have two blocks open at the - // same time. - base::SharedMemoryHandle shared_mem_handle = - base::SharedMemory::DuplicateHandle(memory_handles_.at(0)); - EXPECT_TRUE(base::SharedMemory::IsHandleValid(shared_mem_handle)); - base::SharedMemory shared_memory(shared_mem_handle, false); - EXPECT_TRUE(shared_memory.Map(kTestBlobStorageMaxSharedMemoryBytes)); - - EXPECT_EQ(storage::BlobItemBytesRequest::CreateSharedMemoryRequest( - 0, 0, 0, kTestBlobStorageMaxSharedMemoryBytes, 0, 0), - requests_.at(0)); - - memset(shared_memory.memory(), kFirstBlockByte, - kTestBlobStorageMaxSharedMemoryBytes); - - storage::BlobItemBytesResponse response(0); - std::vector<storage::BlobItemBytesResponse> responses = {response}; - host_.OnMemoryResponses(kBlobUUID, responses, &context_); - EXPECT_EQ(storage::BlobStatus::PENDING_TRANSPORT, GetBlobStatus(kBlobUUID)); - ASSERT_TRUE(handle); - EXPECT_EQ(storage::BlobStatus::PENDING_TRANSPORT, handle->GetBlobStatus()); - - EXPECT_TRUE(request_called_); - EXPECT_EQ(1u, host_.blob_building_count()); - ASSERT_EQ(1u, requests_.size()); - request_called_ = false; - - EXPECT_EQ(storage::BlobItemBytesRequest::CreateSharedMemoryRequest( - 1, 0, kTestBlobStorageMaxSharedMemoryBytes, 1, 0, 0), - requests_.at(0)); - - memset(shared_memory.memory(), kSecondBlockByte, 1); - - response.request_number = 1; - responses[0] = response; - host_.OnMemoryResponses(kBlobUUID, responses, &context_); - EXPECT_TRUE(handle); - EXPECT_EQ(storage::BlobStatus::DONE, handle->GetBlobStatus()); - EXPECT_FALSE(request_called_); - EXPECT_EQ(0u, host_.blob_building_count()); - std::unique_ptr<storage::BlobDataHandle> blob_handle = - context_.GetBlobDataFromUUID(kBlobUUID); - EXPECT_FALSE(blob_handle->IsBeingBuilt()); - EXPECT_FALSE(blob_handle->IsBroken()); - std::unique_ptr<storage::BlobDataSnapshot> blob_data = - blob_handle->CreateSnapshot(); - EXPECT_EQ(expected, *blob_data); -}; - -TEST_F(BlobTransportHostTest, TestBasicIPCAndStopBuilding) { - std::vector<network::DataElement> descriptions; - - AddMemoryItem(2, &descriptions); - AddBlobItem(&descriptions); - AddMemoryItem(2, &descriptions); - - storage::BlobDataBuilder expected(kBlobUUID); - expected.set_content_type(kContentType); - expected.set_content_disposition(kContentDisposition); - AddShortcutMemoryItem(2, &expected); - expected.AppendData(kCompletedBlobData); - AddShortcutMemoryItem(2, &expected); - - std::unique_ptr<storage::BlobDataHandle> handle1; - EXPECT_EQ(storage::BlobStatus::PENDING_TRANSPORT, - BuildBlobAsync(kBlobUUID, descriptions, &handle1)); - EXPECT_TRUE(handle1); - host_.CancelBuildingBlob(kBlobUUID, storage::BlobStatus::ERR_OUT_OF_MEMORY, - &context_); - - // Check that we're broken, and then remove the blob. - EXPECT_FALSE(handle1->IsBeingBuilt()); - EXPECT_TRUE(handle1->IsBroken()); - handle1.reset(); - base::RunLoop().RunUntilIdle(); - handle1 = context_.GetBlobDataFromUUID(kBlobUUID); - EXPECT_FALSE(handle1.get()); - - // This should succeed because we've removed all references to the blob. - std::unique_ptr<storage::BlobDataHandle> handle2; - EXPECT_EQ(storage::BlobStatus::PENDING_TRANSPORT, - BuildBlobAsync(kBlobUUID, descriptions, &handle2)); - - EXPECT_TRUE(request_called_); - EXPECT_EQ(1u, host_.blob_building_count()); - request_called_ = false; - - storage::BlobItemBytesResponse response1(0); - PopulateBytes(response1.allocate_mutable_data(2), 2); - storage::BlobItemBytesResponse response2(1); - PopulateBytes(response2.allocate_mutable_data(2), 2); - std::vector<storage::BlobItemBytesResponse> responses = {response1, - response2}; - - host_.OnMemoryResponses(kBlobUUID, responses, &context_); - EXPECT_EQ(storage::BlobStatus::DONE, handle2->GetBlobStatus()); - EXPECT_FALSE(request_called_); - EXPECT_EQ(0u, host_.blob_building_count()); - EXPECT_FALSE(handle2->IsBeingBuilt()); - EXPECT_FALSE(handle2->IsBroken()); - std::unique_ptr<storage::BlobDataSnapshot> blob_data = - handle2->CreateSnapshot(); - EXPECT_EQ(expected, *blob_data); -}; - -TEST_F(BlobTransportHostTest, TestBreakingAllBuilding) { - const std::string& kBlob1 = "blob1"; - const std::string& kBlob2 = "blob2"; - const std::string& kBlob3 = "blob3"; - - std::vector<network::DataElement> descriptions; - AddMemoryItem(2, &descriptions); - - // Register blobs. - std::unique_ptr<storage::BlobDataHandle> handle1; - std::unique_ptr<storage::BlobDataHandle> handle2; - std::unique_ptr<storage::BlobDataHandle> handle3; - EXPECT_EQ(storage::BlobStatus::PENDING_TRANSPORT, - BuildBlobAsync(kBlob1, descriptions, &handle1)); - EXPECT_EQ(storage::BlobStatus::PENDING_TRANSPORT, - BuildBlobAsync(kBlob2, descriptions, &handle2)); - EXPECT_EQ(storage::BlobStatus::PENDING_TRANSPORT, - BuildBlobAsync(kBlob3, descriptions, &handle3)); - - EXPECT_TRUE(request_called_); - EXPECT_TRUE(handle1->IsBeingBuilt() && handle2->IsBeingBuilt() && - handle3->IsBeingBuilt()); - EXPECT_FALSE(handle1->IsBroken() || handle2->IsBroken() || - handle3->IsBroken()); - - EXPECT_TRUE(IsBeingBuiltInContext(kBlob1) && IsBeingBuiltInContext(kBlob2) && - IsBeingBuiltInContext(kBlob3)); - - // This shouldn't call the transport complete callbacks, so our handles should - // still be false. - host_.CancelAll(&context_); - - EXPECT_FALSE(handle1->IsBeingBuilt() || handle2->IsBeingBuilt() || - handle3->IsBeingBuilt()); - EXPECT_TRUE(handle1->IsBroken() && handle2->IsBroken() && - handle3->IsBroken()); - - base::RunLoop().RunUntilIdle(); -}; - -TEST_F(BlobTransportHostTest, TestBadIPCs) { - std::vector<network::DataElement> descriptions; - - // Test reusing same blob uuid. - AddMemoryItem(10, &descriptions); - AddBlobItem(&descriptions); - AddMemoryItem(300, &descriptions); - std::unique_ptr<storage::BlobDataHandle> handle1; - EXPECT_EQ(storage::BlobStatus::PENDING_TRANSPORT, - BuildBlobAsync(kBlobUUID, descriptions, &handle1)); - EXPECT_TRUE(host_.IsBeingBuilt(kBlobUUID)); - EXPECT_TRUE(request_called_); - host_.CancelBuildingBlob( - kBlobUUID, storage::BlobStatus::ERR_REFERENCED_BLOB_BROKEN, &context_); - handle1.reset(); - EXPECT_FALSE(context_.GetBlobDataFromUUID(kBlobUUID).get()); - - // Test empty responses. - EXPECT_EQ(storage::BlobStatus::PENDING_TRANSPORT, - BuildBlobAsync(kBlobUUID, descriptions, &handle1)); - std::vector<storage::BlobItemBytesResponse> responses; - host_.OnMemoryResponses(kBlobUUID, responses, &context_); - EXPECT_EQ(storage::BlobStatus::ERR_INVALID_CONSTRUCTION_ARGUMENTS, - handle1->GetBlobStatus()); - handle1.reset(); - - // Test response problems below here. - descriptions.clear(); - AddMemoryItem(2, &descriptions); - AddBlobItem(&descriptions); - AddMemoryItem(2, &descriptions); - EXPECT_EQ(storage::BlobStatus::PENDING_TRANSPORT, - BuildBlobAsync(kBlobUUID, descriptions, &handle1)); - - // Invalid request number. - storage::BlobItemBytesResponse response1(3); - PopulateBytes(response1.allocate_mutable_data(2), 2); - responses = {response1}; - host_.OnMemoryResponses(kBlobUUID, responses, &context_); - EXPECT_EQ(storage::BlobStatus::ERR_INVALID_CONSTRUCTION_ARGUMENTS, - handle1->GetBlobStatus()); - EXPECT_TRUE(context_.GetBlobDataFromUUID(kBlobUUID)->IsBroken()); - handle1.reset(); - base::RunLoop().RunUntilIdle(); - - // Duplicate request number responses. - EXPECT_EQ(storage::BlobStatus::PENDING_TRANSPORT, - BuildBlobAsync(kBlobUUID, descriptions, &handle1)); - response1.request_number = 0; - storage::BlobItemBytesResponse response2(0); - PopulateBytes(response2.allocate_mutable_data(2), 2); - responses = {response1, response2}; - host_.OnMemoryResponses(kBlobUUID, responses, &context_); - EXPECT_EQ(storage::BlobStatus::ERR_INVALID_CONSTRUCTION_ARGUMENTS, - handle1->GetBlobStatus()); - EXPECT_TRUE(context_.GetBlobDataFromUUID(kBlobUUID)->IsBroken()); - handle1.reset(); - base::RunLoop().RunUntilIdle(); -}; - -TEST_F(BlobTransportHostTest, WaitOnReferencedBlob) { - const std::string& kBlob1 = "blob1"; - const std::string& kBlob2 = "blob2"; - const std::string& kBlob3 = "blob3"; - - std::vector<network::DataElement> descriptions; - AddMemoryItem(2, &descriptions); - - // Register blobs. - std::unique_ptr<storage::BlobDataHandle> handle1; - - std::unique_ptr<storage::BlobDataHandle> handle2; - std::unique_ptr<storage::BlobDataHandle> handle3; - EXPECT_EQ(storage::BlobStatus::PENDING_TRANSPORT, - BuildBlobAsync(kBlob1, descriptions, &handle1)); - EXPECT_EQ(storage::BlobStatus::PENDING_TRANSPORT, - BuildBlobAsync(kBlob2, descriptions, &handle2)); - EXPECT_TRUE(request_called_); - request_called_ = false; - - // Finish the third one, with a reference to the first and second blob. - network::DataElement element; - element.SetToBlob(kBlob1); - descriptions.push_back(std::move(element)); - element.SetToBlob(kBlob2); - descriptions.push_back(std::move(element)); - - EXPECT_EQ(storage::BlobStatus::PENDING_TRANSPORT, - BuildBlobAsync(kBlob3, descriptions, &handle3)); - EXPECT_TRUE(request_called_); - request_called_ = false; - - // Finish the third, but we should still be 'building' it. - storage::BlobItemBytesResponse response1(0); - PopulateBytes(response1.allocate_mutable_data(2), 2); - std::vector<storage::BlobItemBytesResponse> responses = {response1}; - host_.OnMemoryResponses(kBlob3, responses, &context_); - EXPECT_EQ(storage::BlobStatus::PENDING_REFERENCED_BLOBS, - handle3->GetBlobStatus()); - EXPECT_FALSE(request_called_); - EXPECT_FALSE(host_.IsBeingBuilt(kBlob3)); - EXPECT_TRUE(IsBeingBuiltInContext(kBlob3)); - - // Finish the first. - descriptions.clear(); - AddShortcutMemoryItem(2, &descriptions); - host_.OnMemoryResponses(kBlob1, responses, &context_); - EXPECT_EQ(storage::BlobStatus::DONE, handle1->GetBlobStatus()); - EXPECT_FALSE(request_called_); - EXPECT_FALSE(host_.IsBeingBuilt(kBlob1)); - EXPECT_FALSE(IsBeingBuiltInContext(kBlob1)); - EXPECT_TRUE(context_.GetBlobDataFromUUID(kBlob1)); - - // Run the message loop so we propogate the construction complete callbacks. - base::RunLoop().RunUntilIdle(); - // Verify we're not done. - EXPECT_TRUE(IsBeingBuiltInContext(kBlob3)); - - // Finish the second. - host_.OnMemoryResponses(kBlob2, responses, &context_); - EXPECT_EQ(storage::BlobStatus::DONE, handle2->GetBlobStatus()); - EXPECT_FALSE(request_called_); - EXPECT_FALSE(host_.IsBeingBuilt(kBlob2)); - EXPECT_FALSE(IsBeingBuiltInContext(kBlob2)); - EXPECT_TRUE(context_.GetBlobDataFromUUID(kBlob2)); - - // Run the message loop so we propogate the construction complete callbacks. - base::RunLoop().RunUntilIdle(); - // Finally, we should be finished with third blob. - EXPECT_FALSE(host_.IsBeingBuilt(kBlob3)); - EXPECT_FALSE(IsBeingBuiltInContext(kBlob3)); - EXPECT_TRUE(context_.GetBlobDataFromUUID(kBlob3)); -}; - -} // namespace content
diff --git a/content/browser/devtools/devtools_session_encoding.cc b/content/browser/devtools/devtools_session_encoding.cc index 6021b4d..aa5f39f 100644 --- a/content/browser/devtools/devtools_session_encoding.cc +++ b/content/browser/devtools/devtools_session_encoding.cc
@@ -73,7 +73,7 @@ std::unique_ptr<StreamingParserHandler> encoder = NewCBOREncoder(&cbor, &status); ParseJSON( - &platform, + platform, span<uint8_t>(reinterpret_cast<const uint8_t*>(json.data()), json.size()), encoder.get()); if (!status.ok()) {
diff --git a/content/browser/frame_host/render_frame_host_impl.cc b/content/browser/frame_host/render_frame_host_impl.cc index 89c2fbd6..4be51c9 100644 --- a/content/browser/frame_host/render_frame_host_impl.cc +++ b/content/browser/frame_host/render_frame_host_impl.cc
@@ -899,7 +899,9 @@ site_instance_->RemoveObserver(this); - if (delegate_ && render_frame_created_) + const bool was_created = render_frame_created_; + render_frame_created_ = false; + if (delegate_ && was_created) delegate_->RenderFrameDeleted(this); // Ensure that the render process host has been notified that all audio @@ -963,7 +965,7 @@ // *always* first be unassociated from its corresponding RFHM. Thus, it // follows that |GetMainFrame()| will never return the speculative main frame // being deleted, since it must have already been unset. - if (render_frame_created_ && render_view_host_->GetMainFrame() != this) + if (was_created && render_view_host_->GetMainFrame() != this) CHECK(!is_active()); GetProcess()->RemoveRoute(routing_id_); @@ -5265,6 +5267,10 @@ visual_state_callbacks_.emplace(key, std::move(callback)); } +bool RenderFrameHostImpl::IsRenderFrameCreated() { + return render_frame_created_; +} + bool RenderFrameHostImpl::IsRenderFrameLive() { bool is_live = GetProcess()->IsInitializedAndNotDead() && render_frame_created_;
diff --git a/content/browser/frame_host/render_frame_host_impl.h b/content/browser/frame_host/render_frame_host_impl.h index 15d0da85..2d937fc 100644 --- a/content/browser/frame_host/render_frame_host_impl.h +++ b/content/browser/frame_host/render_frame_host_impl.h
@@ -242,6 +242,7 @@ service_manager::InterfaceProvider* GetRemoteInterfaces() override; blink::AssociatedInterfaceProvider* GetRemoteAssociatedInterfaces() override; content::PageVisibilityState GetVisibilityState() override; + bool IsRenderFrameCreated() override; bool IsRenderFrameLive() override; bool IsCurrent() override; size_t GetProxyCount() override;
diff --git a/content/browser/site_per_process_hit_test_browsertest.cc b/content/browser/site_per_process_hit_test_browsertest.cc index d65f88e..2af65f3 100644 --- a/content/browser/site_per_process_hit_test_browsertest.cc +++ b/content/browser/site_per_process_hit_test_browsertest.cc
@@ -715,14 +715,21 @@ void SetUpCommandLine(base::CommandLine* command_line) override { SitePerProcessBrowserTest::SetUpCommandLine(command_line); ui::PlatformEventSource::SetIgnoreNativePlatformEvents(true); + const char kParam[] = "provider"; + std::map<std::string, std::string> parameters; if (std::get<0>(GetParam()) == 1) { - feature_list_.InitAndEnableFeature(features::kEnableVizHitTestDrawQuad); + parameters[kParam] = "draw_quad"; + feature_list_.InitAndEnableFeatureWithParameters( + features::kEnableVizHitTest, parameters); } else if (std::get<0>(GetParam()) == 2) { - feature_list_.InitWithFeatures({features::kEnableVizHitTestSurfaceLayer}, - {features::kEnableVizHitTestDrawQuad}); + parameters[kParam] = "surface_layer"; + feature_list_.InitAndEnableFeatureWithParameters( + features::kEnableVizHitTest, parameters); } else { - feature_list_.InitWithFeatures({}, {features::kEnableVizHitTestDrawQuad, - features::kVizDisplayCompositor}); + feature_list_.InitWithFeatures( + {}, + {features::kVizDisplayCompositor, features::kEnableVizHitTestDrawQuad, + features::kEnableVizHitTestSurfaceLayer}); } }
diff --git a/content/public/browser/render_frame_host.h b/content/public/browser/render_frame_host.h index 76b89de..ab2e327 100644 --- a/content/public/browser/render_frame_host.h +++ b/content/public/browser/render_frame_host.h
@@ -249,6 +249,11 @@ // of a frame are defined in Blink. virtual PageVisibilityState GetVisibilityState() = 0; + // Returns true if WebContentsObserver::RenderFrameCreate notification has + // been dispatched for this frame, and so a RenderFrameDeleted notification + // will later be dispatched for this frame. + virtual bool IsRenderFrameCreated() = 0; + // Returns whether the RenderFrame in the renderer process has been created // and still has a connection. This is valid for all frames. virtual bool IsRenderFrameLive() = 0;
diff --git a/content/renderer/accessibility/blink_ax_tree_source.cc b/content/renderer/accessibility/blink_ax_tree_source.cc index f26597e..2951268 100644 --- a/content/renderer/accessibility/blink_ax_tree_source.cc +++ b/content/renderer/accessibility/blink_ax_tree_source.cc
@@ -690,9 +690,30 @@ static_cast<int32_t>(src.GetTextPosition())); } - if (src.TextStyle()) { - dst->AddIntAttribute(ax::mojom::IntAttribute::kTextStyle, - src.TextStyle()); + int32_t text_style = 0; + ax::mojom::TextDecorationStyle text_overline_style; + ax::mojom::TextDecorationStyle text_strikethrough_style; + ax::mojom::TextDecorationStyle text_underline_style; + src.GetTextStyleAndTextDecorationStyle(&text_style, &text_overline_style, + &text_strikethrough_style, + &text_underline_style); + if (text_style) { + dst->AddIntAttribute(ax::mojom::IntAttribute::kTextStyle, text_style); + } + + if (text_overline_style != ax::mojom::TextDecorationStyle::kNone) { + dst->AddIntAttribute(ax::mojom::IntAttribute::kTextOverlineStyle, + static_cast<int32_t>(text_overline_style)); + } + + if (text_strikethrough_style != ax::mojom::TextDecorationStyle::kNone) { + dst->AddIntAttribute(ax::mojom::IntAttribute::kTextStrikethroughStyle, + static_cast<int32_t>(text_strikethrough_style)); + } + + if (text_underline_style != ax::mojom::TextDecorationStyle::kNone) { + dst->AddIntAttribute(ax::mojom::IntAttribute::kTextUnderlineStyle, + static_cast<int32_t>(text_underline_style)); } if (dst->role == ax::mojom::Role::kInlineTextBox) {
diff --git a/content/renderer/mus/renderer_window_tree_client.cc b/content/renderer/mus/renderer_window_tree_client.cc index 3efa3d342..384141a 100644 --- a/content/renderer/mus/renderer_window_tree_client.cc +++ b/content/renderer/mus/renderer_window_tree_client.cc
@@ -265,6 +265,7 @@ void RendererWindowTreeClient::OnWindowBoundsChanged( ws::Id window_id, const gfx::Rect& new_bounds, + ui::WindowShowState state, const base::Optional<viz::LocalSurfaceIdAllocation>& local_surface_id_allocation) {}
diff --git a/content/renderer/mus/renderer_window_tree_client.h b/content/renderer/mus/renderer_window_tree_client.h index 0938ed3..723e7fe 100644 --- a/content/renderer/mus/renderer_window_tree_client.h +++ b/content/renderer/mus/renderer_window_tree_client.h
@@ -138,6 +138,7 @@ void OnWindowBoundsChanged( ws::Id window_id, const gfx::Rect& new_bounds, + ui::WindowShowState state, const base::Optional<viz::LocalSurfaceIdAllocation>& local_surface_id_allocation) override; void OnWindowTransformChanged(ws::Id window_id,
diff --git a/content/renderer/render_widget.cc b/content/renderer/render_widget.cc index 987016a..f41433f 100644 --- a/content/renderer/render_widget.cc +++ b/content/renderer/render_widget.cc
@@ -1339,7 +1339,7 @@ void RenderWidget::EndUpdateLayers() { if (GetWebWidget()) - GetWebWidget()->BeginUpdateLayers(); + GetWebWidget()->EndUpdateLayers(); } void RenderWidget::WillBeginCompositorFrame() {
diff --git a/content/test/data/accessibility/html/text-decoration-styles-expected-blink.txt b/content/test/data/accessibility/html/text-decoration-styles-expected-blink.txt new file mode 100644 index 0000000..e1aee47 --- /dev/null +++ b/content/test/data/accessibility/html/text-decoration-styles-expected-blink.txt
@@ -0,0 +1,55 @@ +rootWebArea +++paragraph textOverlineStyle=solid +++++staticText name='overline style: none' textOverlineStyle=solid +++++++inlineTextBox name='overline style: none' +++paragraph textOverlineStyle=dotted +++++staticText name='overline style: dotted' textOverlineStyle=dotted +++++++inlineTextBox name='overline style: dotted' +++paragraph textOverlineStyle=dashed +++++staticText name='overline style: dashed' textOverlineStyle=dashed +++++++inlineTextBox name='overline style: dashed' +++paragraph textOverlineStyle=solid +++++staticText name='overline style: solid' textOverlineStyle=solid +++++++inlineTextBox name='overline style: solid' +++paragraph textOverlineStyle=double +++++staticText name='overline style: double' textOverlineStyle=double +++++++inlineTextBox name='overline style: double' +++paragraph textOverlineStyle=wavy +++++staticText name='overline style: wavy' textOverlineStyle=wavy +++++++inlineTextBox name='overline style: wavy' +++paragraph textUnderlineStyle=solid +++++staticText name='underline style: none' textUnderlineStyle=solid +++++++inlineTextBox name='underline style: none' +++paragraph textUnderlineStyle=dotted +++++staticText name='underline style: dotted' textUnderlineStyle=dotted +++++++inlineTextBox name='underline style: dotted' +++paragraph textUnderlineStyle=dashed +++++staticText name='underline style: dashed' textUnderlineStyle=dashed +++++++inlineTextBox name='underline style: dashed' +++paragraph textUnderlineStyle=solid +++++staticText name='underline style: solid' textUnderlineStyle=solid +++++++inlineTextBox name='underline style: solid' +++paragraph textUnderlineStyle=double +++++staticText name='underline style: double' textUnderlineStyle=double +++++++inlineTextBox name='underline style: double' +++paragraph textUnderlineStyle=wavy +++++staticText name='underline style: wavy' textUnderlineStyle=wavy +++++++inlineTextBox name='underline style: wavy' +++paragraph textStrikethroughStyle=solid +++++staticText name='line-through style: none' textStrikethroughStyle=solid +++++++inlineTextBox name='line-through style: none' +++paragraph textStrikethroughStyle=dotted +++++staticText name='line-through style: dotted' textStrikethroughStyle=dotted +++++++inlineTextBox name='line-through style: dotted' +++paragraph textStrikethroughStyle=dashed +++++staticText name='line-through style: dashed' textStrikethroughStyle=dashed +++++++inlineTextBox name='line-through style: dashed' +++paragraph textStrikethroughStyle=solid +++++staticText name='line-through style: solid' textStrikethroughStyle=solid +++++++inlineTextBox name='line-through style: solid' +++paragraph textStrikethroughStyle=double +++++staticText name='line-through style: double' textStrikethroughStyle=double +++++++inlineTextBox name='line-through style: double' +++paragraph textStrikethroughStyle=wavy +++++staticText name='line-through style: wavy' textStrikethroughStyle=wavy +++++++inlineTextBox name='line-through style: wavy'
diff --git a/content/test/data/accessibility/html/text-decoration-styles.html b/content/test/data/accessibility/html/text-decoration-styles.html new file mode 100644 index 0000000..2c9b3b3a --- /dev/null +++ b/content/test/data/accessibility/html/text-decoration-styles.html
@@ -0,0 +1,65 @@ +<!-- +@BLINK-ALLOW:textOverlineStyle* +@BLINK-ALLOW:textUnderlineStyle* +@BLINK-ALLOW:textStrikethroughStyle* +--> +<html> + <body> + <p style="text-decoration: overline; text-decoration-style: none;"> + overline style: none + </p> + <p style="text-decoration: overline; text-decoration-style: dotted;"> + overline style: dotted + </p> + <p style="text-decoration: overline; text-decoration-style: dashed;"> + overline style: dashed + </p> + <p style="text-decoration: overline; text-decoration-style: solid;"> + overline style: solid + </p> + <p style="text-decoration: overline; text-decoration-style: double;"> + overline style: double + </p> + <p style="text-decoration: overline; text-decoration-style: wavy;"> + overline style: wavy + </p> + + <p style="text-decoration: underline; text-decoration-style: none;"> + underline style: none + </p> + <p style="text-decoration: underline; text-decoration-style: dotted;"> + underline style: dotted + </p> + <p style="text-decoration: underline; text-decoration-style: dashed;"> + underline style: dashed + </p> + <p style="text-decoration: underline; text-decoration-style: solid;"> + underline style: solid + </p> + <p style="text-decoration: underline; text-decoration-style: double;"> + underline style: double + </p> + <p style="text-decoration: underline; text-decoration-style: wavy;"> + underline style: wavy + </p> + + <p style="text-decoration: line-through; text-decoration-style: none;"> + line-through style: none + </p> + <p style="text-decoration: line-through; text-decoration-style: dotted;"> + line-through style: dotted + </p> + <p style="text-decoration: line-through; text-decoration-style: dashed;"> + line-through style: dashed + </p> + <p style="text-decoration: line-through; text-decoration-style: solid;"> + line-through style: solid + </p> + <p style="text-decoration: line-through; text-decoration-style: double;"> + line-through style: double + </p> + <p style="text-decoration: line-through; text-decoration-style: wavy;"> + line-through style: wavy + </p> + </body> +</html>
diff --git a/content/test/web_contents_observer_sanity_checker.cc b/content/test/web_contents_observer_sanity_checker.cc index e1c6276..97cab3b8 100644 --- a/content/test/web_contents_observer_sanity_checker.cc +++ b/content/test/web_contents_observer_sanity_checker.cc
@@ -55,12 +55,14 @@ << Format(render_frame_host); } + CHECK(render_frame_host->IsRenderFrameCreated()) + << "RenderFrameCreated was called for a RenderFrameHost that has not been" + "marked created."; CHECK(render_frame_host->GetProcess()->IsInitializedAndNotDead()) << "RenderFrameCreated was called for a RenderFrameHost whose render " "process is not currently live, so there's no way for the RenderFrame " "to have been created."; - CHECK( - static_cast<RenderFrameHostImpl*>(render_frame_host)->IsRenderFrameLive()) + CHECK(render_frame_host->IsRenderFrameLive()) << "RenderFrameCreated called on for a RenderFrameHost that thinks it is " "not alive."; @@ -81,6 +83,13 @@ void WebContentsObserverSanityChecker::RenderFrameDeleted( RenderFrameHost* render_frame_host) { CHECK(!web_contents_destroyed_); + CHECK(!render_frame_host->IsRenderFrameCreated()) + << "RenderFrameDeleted was called for a RenderFrameHost that is" + "(still) marked as created."; + CHECK(!render_frame_host->IsRenderFrameLive()) + << "RenderFrameDeleted was called for a RenderFrameHost that is" + "still live."; + GlobalRoutingID routing_pair = GetRoutingPair(render_frame_host); bool was_live = !!live_routes_.erase(routing_pair); bool was_dead_already = !deleted_routes_.insert(routing_pair).second;
diff --git a/device/gamepad/dualshock4_controller_linux.cc b/device/gamepad/dualshock4_controller_linux.cc index 2787d38..d13368f 100644 --- a/device/gamepad/dualshock4_controller_linux.cc +++ b/device/gamepad/dualshock4_controller_linux.cc
@@ -8,7 +8,8 @@ namespace device { -Dualshock4ControllerLinux::Dualshock4ControllerLinux(int fd) : fd_(fd) {} +Dualshock4ControllerLinux::Dualshock4ControllerLinux(const base::ScopedFD& fd) + : fd_(fd.get()) {} Dualshock4ControllerLinux::~Dualshock4ControllerLinux() = default;
diff --git a/device/gamepad/dualshock4_controller_linux.h b/device/gamepad/dualshock4_controller_linux.h index dfc0bde..82b159c51 100644 --- a/device/gamepad/dualshock4_controller_linux.h +++ b/device/gamepad/dualshock4_controller_linux.h
@@ -7,16 +7,19 @@ #include "device/gamepad/dualshock4_controller_base.h" +#include "base/files/scoped_file.h" + namespace device { class Dualshock4ControllerLinux : public Dualshock4ControllerBase { public: - Dualshock4ControllerLinux(int fd); + Dualshock4ControllerLinux(const base::ScopedFD& fd); ~Dualshock4ControllerLinux() override; size_t WriteOutputReport(void* report, size_t report_length) override; private: + // Not owned. int fd_; };
diff --git a/device/gamepad/gamepad_device_linux.cc b/device/gamepad/gamepad_device_linux.cc index c18f8b5..9c60275 100644 --- a/device/gamepad/gamepad_device_linux.cc +++ b/device/gamepad/gamepad_device_linux.cc
@@ -54,9 +54,9 @@ return data[bit / LONG_BITS] & (1UL << (bit % LONG_BITS)); } -GamepadBusType GetEvdevBusType(int fd) { +GamepadBusType GetEvdevBusType(const base::ScopedFD& fd) { struct input_id input_info; - if (HANDLE_EINTR(ioctl(fd, EVIOCGID, &input_info)) >= 0) { + if (HANDLE_EINTR(ioctl(fd.get(), EVIOCGID, &input_info)) >= 0) { if (input_info.bustype == BUS_USB) return GAMEPAD_BUS_USB; if (input_info.bustype == BUS_BLUETOOTH) @@ -65,12 +65,12 @@ return GAMEPAD_BUS_UNKNOWN; } -bool HasRumbleCapability(int fd) { +bool HasRumbleCapability(const base::ScopedFD& fd) { unsigned long evbit[BITS_TO_LONGS(EV_MAX)]; unsigned long ffbit[BITS_TO_LONGS(FF_MAX)]; - if (HANDLE_EINTR(ioctl(fd, EVIOCGBIT(0, EV_MAX), evbit)) < 0 || - HANDLE_EINTR(ioctl(fd, EVIOCGBIT(EV_FF, FF_MAX), ffbit)) < 0) { + if (HANDLE_EINTR(ioctl(fd.get(), EVIOCGBIT(0, EV_MAX), evbit)) < 0 || + HANDLE_EINTR(ioctl(fd.get(), EVIOCGBIT(EV_FF, FF_MAX), ffbit)) < 0) { return false; } @@ -85,15 +85,16 @@ // aren't reported by joydev. If a special key is found, the corresponding entry // of the |has_special_key| vector is set to true. Returns the number of // special keys found. -size_t CheckSpecialKeys(int fd, std::vector<bool>* has_special_key) { +size_t CheckSpecialKeys(const base::ScopedFD& fd, + std::vector<bool>* has_special_key) { DCHECK(has_special_key); unsigned long evbit[BITS_TO_LONGS(EV_MAX)]; unsigned long keybit[BITS_TO_LONGS(KEY_MAX)]; size_t found_special_keys = 0; has_special_key->clear(); - if (HANDLE_EINTR(ioctl(fd, EVIOCGBIT(0, EV_MAX), evbit)) < 0 || - HANDLE_EINTR(ioctl(fd, EVIOCGBIT(EV_KEY, KEY_MAX), keybit)) < 0) { + if (HANDLE_EINTR(ioctl(fd.get(), EVIOCGBIT(0, EV_MAX), evbit)) < 0 || + HANDLE_EINTR(ioctl(fd.get(), EVIOCGBIT(EV_KEY, KEY_MAX), keybit)) < 0) { return 0; } @@ -112,12 +113,12 @@ return found_special_keys; } -bool GetHidrawDevinfo(int fd, +bool GetHidrawDevinfo(const base::ScopedFD& fd, GamepadBusType* bus_type, uint16_t* vendor_id, uint16_t* product_id) { struct hidraw_devinfo info; - if (HANDLE_EINTR(ioctl(fd, HIDIOCGRAWINFO, &info)) < 0) + if (HANDLE_EINTR(ioctl(fd.get(), HIDIOCGRAWINFO, &info)) < 0) return false; if (bus_type) { if (info.bustype == BUS_USB) @@ -134,7 +135,7 @@ return true; } -int StoreRumbleEffect(int fd, +int StoreRumbleEffect(const base::ScopedFD& fd, int effect_id, uint16_t duration, uint16_t start_delay, @@ -149,23 +150,23 @@ effect.u.rumble.strong_magnitude = strong_magnitude; effect.u.rumble.weak_magnitude = weak_magnitude; - if (HANDLE_EINTR(ioctl(fd, EVIOCSFF, (const void*)&effect)) < 0) + if (HANDLE_EINTR(ioctl(fd.get(), EVIOCSFF, (const void*)&effect)) < 0) return kInvalidEffectId; return effect.id; } -void DestroyEffect(int fd, int effect_id) { - HANDLE_EINTR(ioctl(fd, EVIOCRMFF, effect_id)); +void DestroyEffect(const base::ScopedFD& fd, int effect_id) { + HANDLE_EINTR(ioctl(fd.get(), EVIOCRMFF, effect_id)); } -bool StartOrStopEffect(int fd, int effect_id, bool do_start) { +bool StartOrStopEffect(const base::ScopedFD& fd, int effect_id, bool do_start) { struct input_event start_stop; memset(&start_stop, 0, sizeof(start_stop)); start_stop.type = EV_FF; start_stop.code = effect_id; start_stop.value = do_start ? 1 : 0; - ssize_t nbytes = - HANDLE_EINTR(write(fd, (const void*)&start_stop, sizeof(start_stop))); + ssize_t nbytes = HANDLE_EINTR( + write(fd.get(), (const void*)&start_stop, sizeof(start_stop))); return nbytes == sizeof(start_stop); } @@ -198,18 +199,19 @@ } bool GamepadDeviceLinux::IsEmpty() const { - return joydev_fd_ < 0 && evdev_fd_ < 0 && hidraw_fd_ < 0; + return !joydev_fd_.is_valid() && !evdev_fd_.is_valid() && + !hidraw_fd_.is_valid(); } bool GamepadDeviceLinux::SupportsVibration() const { if (dualshock4_ || hid_haptics_) return true; - return supports_force_feedback_ && evdev_fd_ >= 0; + return supports_force_feedback_ && evdev_fd_.is_valid(); } void GamepadDeviceLinux::ReadPadState(Gamepad* pad) { - DCHECK_GE(joydev_fd_, 0); + DCHECK(joydev_fd_.is_valid()); // Read button and axis events from the joydev device. bool pad_updated = ReadJoydevState(pad); @@ -234,13 +236,14 @@ DCHECK(polling_runner_->RunsTasksInCurrentSequence()); DCHECK(pad); - if (joydev_fd_ < 0) + if (!joydev_fd_.is_valid()) return false; // Read button and axis events from the joydev device. bool pad_updated = false; js_event event; - while (HANDLE_EINTR(read(joydev_fd_, &event, sizeof(struct js_event))) > 0) { + while (HANDLE_EINTR(read(joydev_fd_.get(), &event, sizeof(struct js_event))) > + 0) { size_t item = event.number; if (event.type & JS_EVENT_AXIS) { if (item >= Gamepad::kAxesLengthCap) @@ -274,7 +277,7 @@ void GamepadDeviceLinux::InitializeEvdevSpecialKeys() { DCHECK(polling_runner_->RunsTasksInCurrentSequence()); - if (evdev_fd_ < 0) + if (!evdev_fd_.is_valid()) return; // Do some one-time initialization to decide indices for the evdev special @@ -316,15 +319,15 @@ DCHECK(polling_runner_->RunsTasksInCurrentSequence()); DCHECK(pad); - if (evdev_fd_ < 0) + if (!evdev_fd_.is_valid()) return false; // Read special button events through evdev. bool pad_updated = false; input_event ev; ssize_t bytes_read; - while ((bytes_read = - HANDLE_EINTR(read(evdev_fd_, &ev, sizeof(input_event)))) > 0) { + while ((bytes_read = HANDLE_EINTR( + read(evdev_fd_.get(), &ev, sizeof(input_event)))) > 0) { if (size_t{bytes_read} < sizeof(input_event)) break; if (ev.type != EV_KEY) @@ -362,8 +365,9 @@ DCHECK(pad_info.syspath_prefix == syspath_prefix_); CloseJoydevNode(); - joydev_fd_ = open(pad_info.path.c_str(), O_RDONLY | O_NONBLOCK); - if (joydev_fd_ < 0) + joydev_fd_ = + base::ScopedFD(open(pad_info.path.c_str(), O_RDONLY | O_NONBLOCK)); + if (!joydev_fd_.is_valid()) return false; udev_device* parent_device = @@ -422,10 +426,7 @@ void GamepadDeviceLinux::CloseJoydevNode() { DCHECK(polling_runner_->RunsTasksInCurrentSequence()); - if (joydev_fd_ >= 0) { - close(joydev_fd_); - joydev_fd_ = -1; - } + joydev_fd_.reset(); joydev_index_ = -1; vendor_id_ = 0; product_id_ = 0; @@ -444,8 +445,8 @@ DCHECK(pad_info.syspath_prefix == syspath_prefix_); CloseEvdevNode(); - evdev_fd_ = open(pad_info.path.c_str(), O_RDWR | O_NONBLOCK); - if (evdev_fd_ < 0) + evdev_fd_ = base::ScopedFD(open(pad_info.path.c_str(), O_RDWR | O_NONBLOCK)); + if (!evdev_fd_.is_valid()) return false; supports_force_feedback_ = HasRumbleCapability(evdev_fd_); @@ -456,14 +457,13 @@ void GamepadDeviceLinux::CloseEvdevNode() { DCHECK(polling_runner_->RunsTasksInCurrentSequence()); - if (evdev_fd_ >= 0) { + if (evdev_fd_.is_valid()) { if (effect_id_ != kInvalidEffectId) { DestroyEffect(evdev_fd_, effect_id_); effect_id_ = kInvalidEffectId; } - close(evdev_fd_); - evdev_fd_ = -1; } + evdev_fd_.reset(); supports_force_feedback_ = false; // Clear any entries in |button_indices_used_| that were taken by evdev. @@ -520,7 +520,7 @@ void GamepadDeviceLinux::InitializeHidraw(base::ScopedFD fd) { DCHECK(polling_runner_->RunsTasksInCurrentSequence()); DCHECK(fd.is_valid()); - hidraw_fd_ = fd.release(); + hidraw_fd_ = std::move(fd); uint16_t vendor_id; uint16_t product_id; @@ -550,10 +550,7 @@ if (hid_haptics_) hid_haptics_->Shutdown(); hid_haptics_.reset(); - if (hidraw_fd_ >= 0) { - close(hidraw_fd_); - hidraw_fd_ = -1; - } + hidraw_fd_.reset(); } #if defined(OS_CHROMEOS)
diff --git a/device/gamepad/gamepad_device_linux.h b/device/gamepad/gamepad_device_linux.h index c47c927..3254158 100644 --- a/device/gamepad/gamepad_device_linux.h +++ b/device/gamepad/gamepad_device_linux.h
@@ -28,8 +28,8 @@ // evdev interface. A gamepad must be enumerated through joydev to be usable, // but the evdev interface is only required for haptic effects. // -// Dualshock4 haptics are not supported through evdev and are instead sent -// through the raw HID (hidraw) interface. +// For some devices, haptics are not supported through evdev and are instead +// sent through the raw HID (hidraw) interface. class GamepadDeviceLinux : public AbstractHapticGamepad { public: using OpenDeviceNodeCallback = base::OnceCallback<void(GamepadDeviceLinux*)>; @@ -124,9 +124,8 @@ // the syspath prefix up to the subsystem. std::string syspath_prefix_; - // The file descriptor for the device's joydev node, or -1 if no joydev node - // is associated with this device. - int joydev_fd_ = -1; + // The file descriptor for the device's joydev node. + base::ScopedFD joydev_fd_; // The index of the device's joydev node, or -1 if unknown. // The joydev index is the integer at the end of the joydev node path and is @@ -150,9 +149,8 @@ // A string identifying the manufacturer and model of the device. std::string name_; - // The file descriptor for the device's evdev node, or -1 if no evdev node is - // associated with this device. - int evdev_fd_ = -1; + // The file descriptor for the device's evdev node. + base::ScopedFD evdev_fd_; // The ID of the haptic effect stored on the device, or -1 if none is stored. int effect_id_ = -1; @@ -168,9 +166,8 @@ // button is not mapped. Empty if no special buttons are mapped. std::vector<int> special_button_map_; - // The file descriptor for the device's hidraw node, or -1 if no hidraw node - // is associated with this device. - int hidraw_fd_ = -1; + // The file descriptor for the device's hidraw node. + base::ScopedFD hidraw_fd_; // The type of the bus through which the device is connected, or // GAMEPAD_BUS_UNKNOWN if the bus type could not be determined.
diff --git a/device/gamepad/hid_haptic_gamepad_linux.cc b/device/gamepad/hid_haptic_gamepad_linux.cc index d0680c9..cbc53b3 100644 --- a/device/gamepad/hid_haptic_gamepad_linux.cc +++ b/device/gamepad/hid_haptic_gamepad_linux.cc
@@ -8,15 +8,17 @@ namespace device { -HidHapticGamepadLinux::HidHapticGamepadLinux(int fd, +HidHapticGamepadLinux::HidHapticGamepadLinux(const base::ScopedFD& fd, const HapticReportData& data) - : HidHapticGamepadBase(data), fd_(fd) {} + : HidHapticGamepadBase(data), fd_(fd.get()) {} HidHapticGamepadLinux::~HidHapticGamepadLinux() = default; // static -std::unique_ptr<HidHapticGamepadLinux> -HidHapticGamepadLinux::Create(uint16_t vendor_id, uint16_t product_id, int fd) { +std::unique_ptr<HidHapticGamepadLinux> HidHapticGamepadLinux::Create( + uint16_t vendor_id, + uint16_t product_id, + const base::ScopedFD& fd) { const auto* haptic_data = GetHapticReportData(vendor_id, product_id); if (!haptic_data) return nullptr;
diff --git a/device/gamepad/hid_haptic_gamepad_linux.h b/device/gamepad/hid_haptic_gamepad_linux.h index fa3c9a04..6656a740 100644 --- a/device/gamepad/hid_haptic_gamepad_linux.h +++ b/device/gamepad/hid_haptic_gamepad_linux.h
@@ -9,20 +9,22 @@ #include <memory> +#include "base/files/scoped_file.h" + namespace device { class HidHapticGamepadLinux : public HidHapticGamepadBase { public: - HidHapticGamepadLinux(int fd, const HapticReportData& data); + HidHapticGamepadLinux(const base::ScopedFD& fd, const HapticReportData& data); ~HidHapticGamepadLinux() override; - static std::unique_ptr<HidHapticGamepadLinux> Create(uint16_t vendor_id, - uint16_t product_id, - int fd); + static std::unique_ptr<HidHapticGamepadLinux> + Create(uint16_t vendor_id, uint16_t product_id, const base::ScopedFD& fd); size_t WriteOutputReport(void* report, size_t report_length) override; private: + // Not owned. int fd_; };
diff --git a/extensions/browser/api/feedback_private/feedback_private_api.cc b/extensions/browser/api/feedback_private/feedback_private_api.cc index eb70419..c80b292 100644 --- a/extensions/browser/api/feedback_private/feedback_private_api.cc +++ b/extensions/browser/api/feedback_private/feedback_private_api.cc
@@ -22,6 +22,7 @@ #include "base/system/sys_info.h" #include "base/values.h" #include "build/build_config.h" +#include "components/feedback/feedback_report.h" #include "components/feedback/system_logs/system_logs_fetcher.h" #include "components/feedback/tracing_manager.h" #include "extensions/browser/api/extensions_api_client.h" @@ -60,6 +61,11 @@ constexpr char kBluetoothLogsAttachmentName[] = "bluetooth_logs.bz2"; +bool IsGoogleEmail(const std::string& email) { + return base::EndsWith(email, "@google.com", + base::CompareCase::INSENSITIVE_ASCII); +} + // Getting the filename of a blob prepends a "C:\fakepath" to the filename. // This is undesirable, strip it if it exists. std::string StripFakepath(const std::string& path) { @@ -79,10 +85,8 @@ if (board.find("eve") == std::string::npos) return feedback_private::LANDING_PAGE_TYPE_NORMAL; - if (!base::EndsWith(email, "@google.com", - base::CompareCase::INSENSITIVE_ASCII)) { + if (!IsGoogleEmail(email)) return feedback_private::LANDING_PAGE_TYPE_NORMAL; - } return feedback_private::LANDING_PAGE_TYPE_TECHSTOP; #else @@ -229,7 +233,21 @@ SystemInformationList sys_info_list; if (sys_info) { sys_info_list.reserve(sys_info->size()); + const bool google_email = + IsGoogleEmail(ExtensionsAPIClient::Get() + ->GetFeedbackPrivateDelegate() + ->GetSignedInUserEmail(browser_context())); for (auto& itr : *sys_info) { + // We only send the list of all the crash report IDs if the user has a + // @google.com email. We strip this here so that the system information + // view properly reflects what we will be uploading to the server. It is + // also stripped later on in the feedback processing for other code paths + // that don't go through this. + if (itr.first == feedback::FeedbackReport::kAllCrashReportIdsKey && + !google_email) { + continue; + } + SystemInformation sys_info_entry; sys_info_entry.key = std::move(itr.first); sys_info_entry.value = std::move(itr.second);
diff --git a/extensions/browser/api/web_request/web_request_api.cc b/extensions/browser/api/web_request/web_request_api.cc index 1d5ae26..eb50f9f 100644 --- a/extensions/browser/api/web_request/web_request_api.cc +++ b/extensions/browser/api/web_request/web_request_api.cc
@@ -757,8 +757,9 @@ auto authentication_request = mojo::MakeRequest(auth_handler); network::mojom::TrustedHeaderClientRequest header_client_request; - if (ExtensionWebRequestEventRouter::GetInstance()->HasAnyExtraHeadersListener( - frame->GetProcess()->GetBrowserContext())) { + if (ExtensionWebRequestEventRouter::GetInstance() + ->HasAnyExtraHeadersListenerOnUI( + frame->GetProcess()->GetBrowserContext())) { header_client_request = mojo::MakeRequest(header_client); } @@ -1669,9 +1670,19 @@ listeners_[browser_context][event_name].push_back(std::move(listener)); - if (extra_info_spec & ExtraInfoSpec::EXTRA_HEADERS) + if (extra_info_spec & ExtraInfoSpec::EXTRA_HEADERS) { + bool had_previously = extra_headers_listener_count_[browser_context] > 0; extra_headers_listener_count_[browser_context]++; + if (!had_previously) { + base::PostTaskWithTraits( + FROM_HERE, {BrowserThread::UI}, + base::BindOnce( + &ExtensionWebRequestEventRouter::UpdateExtraHeadersListenerOnUI, + base::Unretained(this), browser_context, true)); + } + } + return true; } @@ -1729,6 +1740,15 @@ extra_headers_listener_count_[listener->id.browser_context]--; DCHECK_GE(extra_headers_listener_count_[listener->id.browser_context], 0); + + if (extra_headers_listener_count_[listener->id.browser_context] == 0) { + base::PostTaskWithTraits( + FROM_HERE, {BrowserThread::UI}, + base::BindOnce(&ExtensionWebRequestEventRouter:: + UpdateExtraHeadersListenerOnUI, + base::Unretained(this), + listener->id.browser_context, false)); + } } listeners.erase(it); @@ -1800,6 +1820,7 @@ bool ExtensionWebRequestEventRouter::HasAnyExtraHeadersListener( void* browser_context) { + DCHECK_CURRENTLY_ON(content::BrowserThread::IO); if (HasAnyExtraHeadersListenerImpl(browser_context)) return true; @@ -1810,11 +1831,42 @@ return false; } +bool ExtensionWebRequestEventRouter::HasAnyExtraHeadersListenerOnUI( + content::BrowserContext* browser_context) { + DCHECK_CURRENTLY_ON(content::BrowserThread::UI); + if (browser_contexts_with_extra_headers_.find(browser_context) != + browser_contexts_with_extra_headers_.end()) + return true; + + if (browser_context->IsOffTheRecord()) { + auto* original_browser_context = + ExtensionsBrowserClient::Get()->GetOriginalContext(browser_context); + if (browser_contexts_with_extra_headers_.find(original_browser_context) != + browser_contexts_with_extra_headers_.end()) + return true; + } + + return false; +} + bool ExtensionWebRequestEventRouter::HasAnyExtraHeadersListenerImpl( void* browser_context) { return extra_headers_listener_count_[browser_context] > 0; } +void ExtensionWebRequestEventRouter::UpdateExtraHeadersListenerOnUI( + void* browser_context, + bool has_extra_headers_listeners) { + DCHECK_CURRENTLY_ON(content::BrowserThread::UI); + auto* browser_context_ptr = + static_cast<content::BrowserContext*>(browser_context); + if (has_extra_headers_listeners) { + browser_contexts_with_extra_headers_.insert(browser_context_ptr); + } else { + browser_contexts_with_extra_headers_.erase(browser_context_ptr); + } +} + bool ExtensionWebRequestEventRouter::IsPageLoad( const WebRequestInfo& request) const { return request.type == content::RESOURCE_TYPE_MAIN_FRAME;
diff --git a/extensions/browser/api/web_request/web_request_api.h b/extensions/browser/api/web_request/web_request_api.h index bd9ea87..ac06e82 100644 --- a/extensions/browser/api/web_request/web_request_api.h +++ b/extensions/browser/api/web_request/web_request_api.h
@@ -502,6 +502,9 @@ // ExtraInfoSpec::EXTRA_HEADERS set. bool HasAnyExtraHeadersListener(void* browser_context); + // Like above, but for usage on the UI thread. + bool HasAnyExtraHeadersListenerOnUI(content::BrowserContext* browser_context); + private: friend class WebRequestAPI; friend class base::NoDestructor<ExtensionWebRequestEventRouter>; @@ -731,6 +734,10 @@ // Helper for |HasAnyExtraHeadersListener()|. bool HasAnyExtraHeadersListenerImpl(void* browser_context); + // Called on the UI thread to update |browser_contexts_with_extra_headers_|. + void UpdateExtraHeadersListenerOnUI(void* browser_context, + bool has_extra_headers_listeners); + // Get the number of listeners - for testing only. size_t GetListenerCountForTesting(void* browser_context, const std::string& event_name); @@ -745,6 +752,10 @@ // Count of listeners per browser context which request extra headers. ExtraHeadersListenerCountMap extra_headers_listener_count_; + // Accessed on the UI thread to check if a given BrowserContext has any + // extra headers listeners. + std::set<content::BrowserContext*> browser_contexts_with_extra_headers_; + // A map of network requests that are waiting for at least one event handler // to respond. BlockedRequestMap blocked_requests_;
diff --git a/extensions/browser/url_loader_factory_manager.cc b/extensions/browser/url_loader_factory_manager.cc index adafc8c..1a42885 100644 --- a/extensions/browser/url_loader_factory_manager.cc +++ b/extensions/browser/url_loader_factory_manager.cc
@@ -102,6 +102,8 @@ "3787567233ED6BACC4FC05387BE30E03434CE4CC", "37AC33A3A46D271CCE57DD6CB3FACE6B01F5A347", "38B4D1CC339580F506BC86D4027A49721AFB4BB9", + "395D84F94DA287C0E4DBAF9ACE478B9710C0029F", + "3A2E664CA697C622EA4CFA40373B3E641C01713B", "3BC834B48C2C13765147FBAD710F792F026378D8", "3CD98763C80D86E00CB1C4CAA56CEA8F3B0BA4F1", "3EB17C39F8B6B28FAF34E2406E57A76013A2E066", @@ -121,6 +123,7 @@ "4E4167EDA0CFF22F261C0655E979B9474BF67C04", "5053323D1F7B6EEC97A77A350DB6D0D8E51CD0AC", "505F2C1E723731B2C8C9182FEAA73D00525B2048", + "505F3697087BFD3F290F42D029CE67F1C793B2DA", "50DDD8734521B61564FCE273F8E60547F88BBCBE", "52865B2087D0ABCD195A83DFD4BD041A3B4EBC34", "52C94AC7680C3A03CCB6EA31445DD42BD0D5CA8E", @@ -175,6 +178,7 @@ "973E35633030AD27DABEC99609424A61386C7309", "97E04C5632954E778306CAC40B3F95C470B463B6", "98EF7B1601119AEE1FCC28EE5CE247DED5676539", + "999BD8D1929F9ABB817E9368480D93BAB2A0983D", "99E06C364BBB2D1F82A9D20BC1645BF21E478259", "9C6A186F8D3C5FD0CC8DCF49682FA726BD8A7705", "A04F08A772F1C83B7A14ED29788ACA4F000BBE05",
diff --git a/gpu/command_buffer/client/shared_image_interface.h b/gpu/command_buffer/client/shared_image_interface.h index 4f41238d..8b80c01 100644 --- a/gpu/command_buffer/client/shared_image_interface.h +++ b/gpu/command_buffer/client/shared_image_interface.h
@@ -34,8 +34,8 @@ // API(s) the image will be used with. // Returns a mailbox that can be imported into said APIs using their // corresponding shared image functions (e.g. - // GLES2Interface::CreateAndTexStorage2DSharedImageCHROMIUM) or (deprecated) - // mailbox functions (e.g. RasterInterface::CreateAndConsumeTexture or + // GLES2Interface::CreateAndTexStorage2DSharedImageCHROMIUM or + // RasterInterface::CopySubTexture) or (deprecated) mailbox functions (e.g. // GLES2Interface::CreateAndConsumeTextureCHROMIUM). // The |SharedImageInterface| keeps ownership of the image until // |DestroySharedImage| is called or the interface itself is destroyed (e.g. @@ -59,12 +59,15 @@ // |usage| is a combination of |SharedImageUsage| bits that describes which // API(s) the image will be used with. Format and size are derived from the // GpuMemoryBuffer. |gpu_memory_buffer_manager| is the manager that created - // |gpu_memory_buffer|. If valid, |color_space| will be applied to the shared + // |gpu_memory_buffer|. If the |gpu_memory_buffer| was created on the client + // side (for NATIVE_PIXMAP or ANDROID_HARDWARE_BUFFER types only), without a + // GpuMemoryBufferManager, |gpu_memory_buffer_manager| can be nullptr. + // If valid, |color_space| will be applied to the shared // image (possibly overwriting the one set on the GpuMemoryBuffer). // Returns a mailbox that can be imported into said APIs using their // corresponding shared image functions (e.g. - // GLES2Interface::CreateAndTexStorage2DSharedImageCHROMIUM) or (deprecated) - // mailbox functions (e.g. RasterInterface::CreateAndConsumeTexture or + // GLES2Interface::CreateAndTexStorage2DSharedImageCHROMIUM or + // RasterInterface::CopySubTexture) or (deprecated) mailbox functions (e.g. // GLES2Interface::CreateAndConsumeTextureCHROMIUM). // The |SharedImageInterface| keeps ownership of the image until // |DestroySharedImage| is called or the interface itself is destroyed (e.g.
diff --git a/gpu/command_buffer/service/raster_decoder.cc b/gpu/command_buffer/service/raster_decoder.cc index eb476fd..896da60 100644 --- a/gpu/command_buffer/service/raster_decoder.cc +++ b/gpu/command_buffer/service/raster_decoder.cc
@@ -2178,7 +2178,7 @@ // hangs. gl::ScopedProgressReporter report_progress( shared_context_state_->progress_reporter()); - sk_surface_->prepareForExternalIO(); + sk_surface_->flush(); } if (!shared_image_) { @@ -2190,7 +2190,7 @@ } // Unlock all font handles. This needs to be deferred until - // SkSurface::prepareForExternalIO since that flushes batched Gr operations + // SkSurface::flush since that flushes batched Gr operations // in skia that access the glyph data. // TODO(khushalsagar): We just unlocked a bunch of handles, do we need to // give a call to skia to attempt to purge any unlocked handles? @@ -2201,7 +2201,7 @@ locked_handles_.clear(); // We just flushed a tile's worth of GPU work from the SkSurface in - // prepareForExternalIO above. Use kDeferLaterCommands to ensure we yield to + // flush above. Use kDeferLaterCommands to ensure we yield to // the Scheduler before processing more commands. current_decoder_error_ = error::kDeferLaterCommands; }
diff --git a/gpu/ipc/client/shared_image_interface_proxy.cc b/gpu/ipc/client/shared_image_interface_proxy.cc index 3adb4b3..db7e9432 100644 --- a/gpu/ipc/client/shared_image_interface_proxy.cc +++ b/gpu/ipc/client/shared_image_interface_proxy.cc
@@ -123,7 +123,9 @@ GpuMemoryBufferManager* gpu_memory_buffer_manager, const gfx::ColorSpace& color_space, uint32_t usage) { - DCHECK(gpu_memory_buffer_manager); + DCHECK(gpu_memory_buffer->GetType() == gfx::NATIVE_PIXMAP || + gpu_memory_buffer->GetType() == gfx::ANDROID_HARDWARE_BUFFER || + gpu_memory_buffer_manager); GpuChannelMsg_CreateGMBSharedImage_Params params; params.mailbox = Mailbox::GenerateForSharedImage(); params.handle = gpu_memory_buffer->CloneHandle();
diff --git a/gpu/ipc/in_process_command_buffer.cc b/gpu/ipc/in_process_command_buffer.cc index 375d7184..623895c 100644 --- a/gpu/ipc/in_process_command_buffer.cc +++ b/gpu/ipc/in_process_command_buffer.cc
@@ -173,7 +173,9 @@ GpuMemoryBufferManager* gpu_memory_buffer_manager, const gfx::ColorSpace& color_space, uint32_t usage) override { - DCHECK(gpu_memory_buffer_manager); + DCHECK(gpu_memory_buffer->GetType() == gfx::NATIVE_PIXMAP || + gpu_memory_buffer->GetType() == gfx::ANDROID_HARDWARE_BUFFER || + gpu_memory_buffer_manager); // TODO(piman): DCHECK GMB format support. DCHECK(gpu::IsImageSizeValidForGpuMemoryBufferFormat(
diff --git a/ios/chrome/browser/about_flags.mm b/ios/chrome/browser/about_flags.mm index 92d57e5e..92faf6e 100644 --- a/ios/chrome/browser/about_flags.mm +++ b/ios/chrome/browser/about_flags.mm
@@ -598,6 +598,13 @@ flags_ui::kOsIos, FEATURE_VALUE_TYPE( autofill::features::kAutofillImportNonFocusableCreditCardForms)}, + {"enable-autofill-do-not-upload-save-unsupported-cards", + flag_descriptions::kEnableAutofillDoNotUploadSaveUnsupportedCardsName, + flag_descriptions:: + kEnableAutofillDoNotUploadSaveUnsupportedCardsDescription, + flags_ui::kOsIos, + FEATURE_VALUE_TYPE( + autofill::features::kAutofillDoNotUploadSaveUnsupportedCards)}, }; // Add all switches from experimental flags to |command_line|.
diff --git a/ios/chrome/browser/ios_chrome_flag_descriptions.cc b/ios/chrome/browser/ios_chrome_flag_descriptions.cc index e6c7d64..bfec0c9 100644 --- a/ios/chrome/browser/ios_chrome_flag_descriptions.cc +++ b/ios/chrome/browser/ios_chrome_flag_descriptions.cc
@@ -68,6 +68,12 @@ "If enabled, changes the server save card prompt's explanation to mention " "the saving of the billing address."; +const char kEnableAutofillDoNotUploadSaveUnsupportedCardsName[] = + "Prevents upload save on cards from unsupported networks"; +const char kEnableAutofillDoNotUploadSaveUnsupportedCardsDescription[] = + "If enabled, cards from unsupported networks will not be offered upload " + "save, and will instead be offered local save."; + const char kEnableAutofillSaveCreditCardUsesStrikeSystemName[] = "Enable limit on offering to save the same credit card repeatedly"; const char kEnableAutofillSaveCreditCardUsesStrikeSystemDescription[] =
diff --git a/ios/chrome/browser/ios_chrome_flag_descriptions.h b/ios/chrome/browser/ios_chrome_flag_descriptions.h index e2a6e475..628c376 100644 --- a/ios/chrome/browser/ios_chrome_flag_descriptions.h +++ b/ios/chrome/browser/ios_chrome_flag_descriptions.h
@@ -55,6 +55,11 @@ extern const char kEnableAutofillCreditCardUploadUpdatePromptExplanationDescription[]; +// Title and description for the flag to control if cards from unsupported +// networks should be prevented form being uploaded. +extern const char kEnableAutofillDoNotUploadSaveUnsupportedCardsName[]; +extern const char kEnableAutofillDoNotUploadSaveUnsupportedCardsDescription[]; + // Title and description for the flag to control if credit card save should // utilize the Autofill StrikeDatabase when determining whether save // should be offered.
diff --git a/ios/testing/earl_grey/earl_grey_app.h b/ios/testing/earl_grey/earl_grey_app.h index 4f9facc..b74efd5 100644 --- a/ios/testing/earl_grey/earl_grey_app.h +++ b/ios/testing/earl_grey/earl_grey_app.h
@@ -18,8 +18,10 @@ #elif defined(CHROME_EARL_GREY_2) #import <AppFramework/Action/GREYActionsShorthand.h> +#import <AppFramework/Core/GREYElementInteraction.h> #import <AppFramework/EarlGreyApp.h> #import <AppFramework/Matcher/GREYMatchersShorthand.h> +#import <CommonLib/Error/GREYErrorConstants.h> #else #error Must define either CHROME_EARL_GREY_1 or CHROME_EARL_GREY_2.
diff --git a/ios/web/BUILD.gn b/ios/web/BUILD.gn index 77ece4f..82da640 100644 --- a/ios/web/BUILD.gn +++ b/ios/web/BUILD.gn
@@ -104,6 +104,7 @@ } source_set("earl_grey_test_support") { + defines = [ "CHROME_EARL_GREY_1" ] configs += [ "//build/config/compiler:enable_arc" ] testonly = true @@ -129,6 +130,32 @@ ] } +source_set("eg_app_support+eg2") { + defines = [ "CHROME_EARL_GREY_2" ] + configs += [ "//build/config/compiler:enable_arc" ] + testonly = true + + deps = [ + ":web", + "//base", + "//base/test:test_support", + "//ios/testing/earl_grey:eg_app_support+eg2", + "//ios/third_party/earl_grey2:app_framework+link", + "//ios/web/interstitials", + "//ios/web/public/test", + "//net", + ] + + sources = [ + "public/test/earl_grey/js_test_util.h", + "public/test/earl_grey/js_test_util.mm", + "public/test/earl_grey/web_view_actions.h", + "public/test/earl_grey/web_view_actions.mm", + "public/test/earl_grey/web_view_matchers.h", + "public/test/earl_grey/web_view_matchers.mm", + ] +} + source_set("run_all_unittests") { testonly = true sources = [
diff --git a/ios/web/public/test/earl_grey/js_test_util.mm b/ios/web/public/test/earl_grey/js_test_util.mm index b754f324..50ac21f 100644 --- a/ios/web/public/test/earl_grey/js_test_util.mm +++ b/ios/web/public/test/earl_grey/js_test_util.mm
@@ -4,11 +4,11 @@ #import "ios/web/public/test/earl_grey/js_test_util.h" -#import <EarlGrey/EarlGrey.h> #import <WebKit/WebKit.h> #import "base/test/ios/wait_util.h" #include "base/timer/elapsed_timer.h" +#import "ios/testing/earl_grey/earl_grey_app.h" #import "ios/web/interstitials/web_interstitial_impl.h" #import "ios/web/public/web_state/js/crw_js_injection_receiver.h"
diff --git a/ios/web/public/test/earl_grey/web_view_actions.h b/ios/web/public/test/earl_grey/web_view_actions.h index edeac77..4057c8c 100644 --- a/ios/web/public/test/earl_grey/web_view_actions.h +++ b/ios/web/public/test/earl_grey/web_view_actions.h
@@ -7,12 +7,11 @@ #include <string> -#import <EarlGrey/EarlGrey.h> - -#include "ios/web/public/test/element_selector.h" -#import "ios/web/public/web_state/web_state.h" +@class ElementSelector; +@protocol GREYAction; namespace web { +class WebState; // Action wrapper that performs |action| on the webview of |state|. // The action will fail (in addition to its own failure modes) if the element
diff --git a/ios/web/public/test/earl_grey/web_view_actions.mm b/ios/web/public/test/earl_grey/web_view_actions.mm index 3099e4d..138f905 100644 --- a/ios/web/public/test/earl_grey/web_view_actions.mm +++ b/ios/web/public/test/earl_grey/web_view_actions.mm
@@ -14,8 +14,11 @@ #include "base/strings/sys_string_conversions.h" #import "base/test/ios/wait_util.h" #include "base/values.h" +#import "ios/testing/earl_grey/earl_grey_app.h" #import "ios/web/public/test/earl_grey/web_view_matchers.h" +#include "ios/web/public/test/element_selector.h" #import "ios/web/public/test/web_view_interaction_test_util.h" +#import "ios/web/public/web_state/web_state.h" #import "ios/web/web_state/web_state_impl.h" #if !defined(__has_feature) || !__has_feature(objc_arc) @@ -198,11 +201,14 @@ } // Run the action and wait for the UI to settle. - [[EarlGrey selectElementWithMatcher:WebViewInWebState(state)] + NSError* actionError = nil; + [[[GREYElementInteraction alloc] + initWithElementMatcher:WebViewInWebState(state)] performAction:action - error:error]; + error:&actionError]; - if (*error) { + if (actionError) { + *error = actionError; return NO; } [[GREYUIThreadExecutor sharedInstance] drainUntilIdle];
diff --git a/ios/web/public/test/earl_grey/web_view_matchers.mm b/ios/web/public/test/earl_grey/web_view_matchers.mm index d1c250e..4b721b9 100644 --- a/ios/web/public/test/earl_grey/web_view_matchers.mm +++ b/ios/web/public/test/earl_grey/web_view_matchers.mm
@@ -4,7 +4,6 @@ #import "ios/web/public/test/earl_grey/web_view_matchers.h" -#import <EarlGrey/EarlGrey.h> #import <UIKit/UIKit.h> #import <WebKit/WebKit.h> @@ -12,9 +11,10 @@ #include "base/strings/sys_string_conversions.h" #include "base/strings/utf_string_conversions.h" #include "base/values.h" +#import "ios/testing/earl_grey/earl_grey_app.h" #import "ios/web/interstitials/web_interstitial_impl.h" -#import "ios/web/public/test/earl_grey/js_test_util.h" #import "ios/web/public/test/web_view_interaction_test_util.h" +#import "ios/web/public/web_state/web_state.h" #import "net/base/mac/url_conversions.h" #if !defined(__has_feature) || !__has_feature(objc_arc) @@ -43,12 +43,12 @@ namespace web { id<GREYMatcher> WebViewInWebState(WebState* web_state) { - MatchesBlock matches = ^BOOL(UIView* view) { + GREYMatchesBlock matches = ^BOOL(UIView* view) { return [view isKindOfClass:[WKWebView class]] && [view isDescendantOfView:web_state->GetView()]; }; - DescribeToBlock describe = ^(id<GREYDescription> description) { + GREYDescribeToBlock describe = ^(id<GREYDescription> description) { [description appendText:@"web view in web state"]; }; @@ -57,13 +57,13 @@ } id<GREYMatcher> WebViewScrollView(WebState* web_state) { - MatchesBlock matches = ^BOOL(UIView* view) { + GREYMatchesBlock matches = ^BOOL(UIView* view) { return [view isKindOfClass:[UIScrollView class]] && [view.superview isKindOfClass:[WKWebView class]] && [view isDescendantOfView:web_state->GetView()]; }; - DescribeToBlock describe = ^(id<GREYDescription> description) { + GREYDescribeToBlock describe = ^(id<GREYDescription> description) { [description appendText:@"web view scroll view"]; }; @@ -72,14 +72,14 @@ } id<GREYMatcher> Interstitial(WebState* web_state) { - MatchesBlock matches = ^BOOL(WKWebView* view) { + GREYMatchesBlock matches = ^BOOL(WKWebView* view) { web::WebInterstitialImpl* interstitial = static_cast<web::WebInterstitialImpl*>(web_state->GetWebInterstitial()); return interstitial && [view isDescendantOfView:interstitial->GetContentView()]; }; - DescribeToBlock describe = ^(id<GREYDescription> description) { + GREYDescribeToBlock describe = ^(id<GREYDescription> description) { [description appendText:@"interstitial displayed"]; };
diff --git a/ios/web/web_state/ui/crw_web_controller.mm b/ios/web/web_state/ui/crw_web_controller.mm index 4f3a9e87..9c136c4 100644 --- a/ios/web/web_state/ui/crw_web_controller.mm +++ b/ios/web/web_state/ui/crw_web_controller.mm
@@ -5247,6 +5247,13 @@ return; } + if (error.code == web::kWebKitErrorUrlBlockedByContentFilter && + web::GetWebClient()->IsSlimNavigationManagerEnabled()) { + // If URL is blocked due to Restriction, do not take any further action as + // WKWebView will show a built-in error. + return; + } + if (error.code == web::kWebKitErrorFrameLoadInterruptedByPolicyChange) { // This method should not be called if the navigation was cancelled by // embedder.
diff --git a/mojo/core/channel_mac.cc b/mojo/core/channel_mac.cc index 0ea384d..8820afe 100644 --- a/mojo/core/channel_mac.cc +++ b/mojo/core/channel_mac.cc
@@ -181,7 +181,10 @@ DCHECK(receive_port_ == MACH_PORT_NULL); CHECK(base::mac::CreateMachPort(&receive_port_, nullptr, MACH_PORT_QLIMIT_LARGE)); - RequestSendDeadNameNotification(); + if (!RequestSendDeadNameNotification()) { + OnError(Error::kConnectionFailed); + return; + } SendHandshake(); } else if (receive_port_ != MACH_PORT_NULL) { DCHECK(send_port_ == MACH_PORT_NULL); @@ -219,15 +222,20 @@ // Requests that the kernel notify the |receive_port_| when the receive right // connected to |send_port_| becomes a dead name. This should be called as // soon as the Channel establishes both the send and receive ports. - void RequestSendDeadNameNotification() { + bool RequestSendDeadNameNotification() { base::mac::ScopedMachSendRight previous; kern_return_t kr = mach_port_request_notification( mach_task_self(), send_port_.get(), MACH_NOTIFY_DEAD_NAME, 0, receive_port_.get(), MACH_MSG_TYPE_MAKE_SEND_ONCE, base::mac::ScopedMachSendRight::Receiver(previous).get()); if (kr != KERN_SUCCESS) { - MACH_LOG(ERROR, kr) << "mach_port_request_notification"; + // If port is already a dead name (i.e. the receiver is already gone), + // then the channel should be shut down by the caller. + MACH_LOG_IF(ERROR, kr != KERN_INVALID_ARGUMENT, kr) + << "mach_port_request_notification"; + return false; } + return true; } // SendHandshake() sends to the |receive_port_| a right to |send_port_|, @@ -285,7 +293,10 @@ send_port_ = base::mac::ScopedMachSendRight(message->msgh_remote_port); - RequestSendDeadNameNotification(); + if (!RequestSendDeadNameNotification()) { + OnError(Error::kConnectionFailed); + return false; + } base::AutoLock lock(write_lock_); handshake_done_ = true;
diff --git a/mojo/public/js/bindings_lite.js b/mojo/public/js/bindings_lite.js index c7fd658..e16d8db 100644 --- a/mojo/public/js/bindings_lite.js +++ b/mojo/public/js/bindings_lite.js
@@ -84,7 +84,7 @@ */ mojo.internal.setInt64 = function(dataView, byteOffset, value) { if (mojo.internal.kHostLittleEndian) { - dataView.setInt32( + dataView.setUint32( byteOffset, Number(BigInt(value) & BigInt(0xffffffff)), mojo.internal.kHostLittleEndian); dataView.setInt32( @@ -94,7 +94,7 @@ dataView.setInt32( byteOffset, Number(BigInt(value) >> BigInt(32)), mojo.internal.kHostLittleEndian); - dataView.setInt32( + dataView.setUint32( byteOffset + 4, Number(BigInt(value) & BigInt(0xffffffff)), mojo.internal.kHostLittleEndian); } @@ -131,10 +131,10 @@ mojo.internal.getInt64 = function(dataView, byteOffset) { let low, high; if (mojo.internal.kHostLittleEndian) { - low = dataView.getInt32(byteOffset, mojo.internal.kHostLittleEndian); + low = dataView.getUint32(byteOffset, mojo.internal.kHostLittleEndian); high = dataView.getInt32(byteOffset + 4, mojo.internal.kHostLittleEndian); } else { - low = dataView.getInt32(byteOffset + 4, mojo.internal.kHostLittleEndian); + low = dataView.getUint32(byteOffset + 4, mojo.internal.kHostLittleEndian); high = dataView.getInt32(byteOffset, mojo.internal.kHostLittleEndian); } const value = (BigInt(high) << BigInt(32)) | BigInt(low);
diff --git a/net/base/network_change_notifier_mac.cc b/net/base/network_change_notifier_mac.cc index 95f60e99..7f11018 100644 --- a/net/base/network_change_notifier_mac.cc +++ b/net/base/network_change_notifier_mac.cc
@@ -10,9 +10,9 @@ #include "base/bind.h" #include "base/macros.h" #include "base/message_loop/message_loop.h" -#include "base/threading/thread.h" -#include "base/threading/thread_restrictions.h" -#include "build/build_config.h" +#include "base/sequenced_task_runner.h" +#include "base/task/post_task.h" +#include "base/task/task_traits.h" #include "net/dns/dns_config_service.h" namespace net { @@ -23,44 +23,36 @@ return reachable && !connection_required; } -// Thread on which we can run DnsConfigService, which requires a TYPE_IO -// message loop. -class NetworkChangeNotifierMac::DnsConfigServiceThread : public base::Thread { - public: - DnsConfigServiceThread() : base::Thread("DnsConfigService") {} - - ~DnsConfigServiceThread() override { Stop(); } - - void Init() override { - service_ = DnsConfigService::CreateSystemService(); - service_->WatchConfig(base::Bind(&NetworkChangeNotifier::SetDnsConfig)); - } - - void CleanUp() override { service_.reset(); } - - private: - std::unique_ptr<DnsConfigService> service_; - - DISALLOW_COPY_AND_ASSIGN(DnsConfigServiceThread); -}; - NetworkChangeNotifierMac::NetworkChangeNotifierMac() : NetworkChangeNotifier(NetworkChangeCalculatorParamsMac()), connection_type_(CONNECTION_UNKNOWN), connection_type_initialized_(false), initial_connection_type_cv_(&connection_type_lock_), - forwarder_(this) { - // Must be initialized after the rest of this object, as it may call back into - // SetInitialConnectionType(). - config_watcher_ = std::make_unique<NetworkConfigWatcherMac>(&forwarder_); + forwarder_(this) #if !defined(OS_IOS) + , + dns_config_service_runner_( + base::CreateSequencedTaskRunnerWithTraits({base::MayBlock()})), + dns_config_service_( + DnsConfigService::CreateSystemService().release(), + // Ensure DnsConfigService lives on |dns_config_service_runner_| + // to prevent races where NetworkChangeNotifierPosix outlives + // ScopedTaskEnvironment. https://crbug.com/938126 + base::OnTaskRunnerDeleter(dns_config_service_runner_)) { // DnsConfigService on iOS doesn't watch the config so its result can become // inaccurate at any time. Disable it to prevent promulgation of inaccurate // DnsConfigs. - dns_config_service_thread_ = std::make_unique<DnsConfigServiceThread>(); - dns_config_service_thread_->StartWithOptions( - base::Thread::Options(base::MessageLoop::TYPE_IO, 0)); -#endif + dns_config_service_runner_->PostTask( + FROM_HERE, base::BindOnce(&DnsConfigService::WatchConfig, + base::Unretained(dns_config_service_.get()), + base::BindRepeating( + &NetworkChangeNotifier::SetDnsConfig))); +#else +{ +#endif // defined(OS_IOS) + // Must be initialized after the rest of this object, as it may call back into + // SetInitialConnectionType(). + config_watcher_ = std::make_unique<NetworkConfigWatcherMac>(&forwarder_); } NetworkChangeNotifierMac::~NetworkChangeNotifierMac() {
diff --git a/net/base/network_change_notifier_mac.h b/net/base/network_change_notifier_mac.h index 34b1266..4037534 100644 --- a/net/base/network_change_notifier_mac.h +++ b/net/base/network_change_notifier_mac.h
@@ -12,13 +12,22 @@ #include "base/compiler_specific.h" #include "base/mac/scoped_cftyperef.h" #include "base/macros.h" +#include "base/memory/scoped_refptr.h" #include "base/synchronization/condition_variable.h" #include "base/synchronization/lock.h" +#include "build/build_config.h" #include "net/base/network_change_notifier.h" #include "net/base/network_config_watcher_mac.h" +namespace base { +class SequencedTaskRunner; +struct OnTaskRunnerDeleter; +} // namespace base + namespace net { +class DnsConfigService; + class NetworkChangeNotifierMac: public NetworkChangeNotifier { public: NetworkChangeNotifierMac(); @@ -46,8 +55,6 @@ }; private: - class DnsConfigServiceThread; - // Called on the main thread on startup, afterwards on the notifier thread. static ConnectionType CalculateConnectionType(SCNetworkConnectionFlags flags); @@ -76,7 +83,13 @@ Forwarder forwarder_; std::unique_ptr<const NetworkConfigWatcherMac> config_watcher_; - std::unique_ptr<DnsConfigServiceThread> dns_config_service_thread_; +#if !defined(OS_IOS) + // |dns_config_service_| will live on this runner. + scoped_refptr<base::SequencedTaskRunner> dns_config_service_runner_; + // DnsConfigService that lives on |dns_config_service_runner_|. + std::unique_ptr<DnsConfigService, base::OnTaskRunnerDeleter> + dns_config_service_; +#endif DISALLOW_COPY_AND_ASSIGN(NetworkChangeNotifierMac); };
diff --git a/services/resource_coordinator/public/mojom/BUILD.gn b/services/resource_coordinator/public/mojom/BUILD.gn index 932c3d6..79769fd1 100644 --- a/services/resource_coordinator/public/mojom/BUILD.gn +++ b/services/resource_coordinator/public/mojom/BUILD.gn
@@ -15,7 +15,6 @@ "memory_instrumentation/memory_instrumentation.mojom", "page_signal.mojom", "service_constants.mojom", - "signals.mojom", "webui_graph_dump.mojom", ]
diff --git a/services/resource_coordinator/public/mojom/coordination_unit.mojom b/services/resource_coordinator/public/mojom/coordination_unit.mojom index a594ac41..99e2196 100644 --- a/services/resource_coordinator/public/mojom/coordination_unit.mojom +++ b/services/resource_coordinator/public/mojom/coordination_unit.mojom
@@ -7,7 +7,6 @@ import "mojo/public/mojom/base/process_id.mojom"; import "mojo/public/mojom/base/time.mojom"; import "services/resource_coordinator/public/mojom/lifecycle.mojom"; -import "services/resource_coordinator/public/mojom/signals.mojom"; // Any new type here needs to be mirrored between coordination_unit_types.h and // coordination_unit.mojom, and have mappings between the two defined in
diff --git a/services/resource_coordinator/public/mojom/signals.mojom b/services/resource_coordinator/public/mojom/signals.mojom deleted file mode 100644 index 94c83f78..0000000 --- a/services/resource_coordinator/public/mojom/signals.mojom +++ /dev/null
@@ -1,21 +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. - -module resource_coordinator.mojom; - -enum Event { - kTestEvent, - kFaviconUpdated, - // This event signal is received when main frame navigation is committed. - kNavigationCommitted, - // Only care about non-persistent notifications, notifications launched from - // ServiceWorker are persistent and compatible with LifeCycle. - kNonPersistentNotificationCreated, - kTitleUpdated, - // This signal is sent to a SystemCU when all ProcessCU CPU usage estimates - // have been updated and are coherent. - kProcessCPUUsageReady, - // This signal is set to the renderer ProcessCU. - kRendererIsBloated, -};
diff --git a/services/ws/client_root.cc b/services/ws/client_root.cc index a47fa2e..dba4cde 100644 --- a/services/ws/client_root.cc +++ b/services/ws/client_root.cc
@@ -18,6 +18,7 @@ #include "services/ws/top_level_proxy_window.h" #include "services/ws/window_service.h" #include "services/ws/window_tree.h" +#include "ui/aura/client/aura_constants.h" #include "ui/aura/env.h" #include "ui/aura/mus/client_surface_embedder.h" #include "ui/aura/mus/property_converter.h" @@ -344,6 +345,7 @@ TRACE_EVENT_FLAG_FLOW_OUT); window_tree_->window_tree_client_->OnWindowBoundsChanged( window_tree_->TransportIdForWindow(window_), last_bounds_, + window_->GetProperty(aura::client::kShowStateKey), ProxyWindow::GetMayBeNull(window_)->local_surface_id_allocation()); }
diff --git a/services/ws/public/mojom/window_tree.mojom b/services/ws/public/mojom/window_tree.mojom index 70ba315..aca3358 100644 --- a/services/ws/public/mojom/window_tree.mojom +++ b/services/ws/public/mojom/window_tree.mojom
@@ -519,11 +519,14 @@ bool parent_drawn, viz.mojom.LocalSurfaceIdAllocation local_surface_id_allocation); - // Invoked when a window's bounds have changed. |local_surface_id_allocation| - // is only supplied for roots. + // Invoked when a window's bounds have changed. |state| is supplied for roots, + // and SHOW_STATE_DEFAULT for non-roots. It allows the client to + // simultaneously update both bounds and show state, e.g. after a maximize + // operation. |local_surface_id_allocation| is only supplied for roots. OnWindowBoundsChanged( uint64 window, gfx.mojom.Rect new_bounds, + ui.mojom.WindowShowState state, viz.mojom.LocalSurfaceIdAllocation? local_surface_id_allocation); OnWindowTransformChanged(uint64 window,
diff --git a/services/ws/public/mojom/window_tree_constants.mojom b/services/ws/public/mojom/window_tree_constants.mojom index cc69965..edf9d3d 100644 --- a/services/ws/public/mojom/window_tree_constants.mojom +++ b/services/ws/public/mojom/window_tree_constants.mojom
@@ -4,6 +4,7 @@ module ws.mojom; +import "ui/base/mojo/ui_base_types.mojom"; import "ui/display/mojo/display.mojom"; import "ui/gfx/geometry/mojo/geometry.mojom"; @@ -30,6 +31,8 @@ gfx.mojom.Rect bounds; + ui.mojom.WindowShowState state = ui.mojom.WindowShowState.kDefault; + // Arbitrary key/value pairs. The interpretation of these is left to the // client. See SetWindowProperty() for more information. map<string, array<uint8>> properties;
diff --git a/services/ws/test_change_tracker.cc b/services/ws/test_change_tracker.cc index 74b7b9b..3f8e056 100644 --- a/services/ws/test_change_tracker.cc +++ b/services/ws/test_change_tracker.cc
@@ -356,12 +356,14 @@ void TestChangeTracker::OnWindowBoundsChanged( Id window_id, const gfx::Rect& new_bounds, + ui::WindowShowState new_state, const base::Optional<viz::LocalSurfaceIdAllocation>& local_surface_id_allocation) { Change change; change.type = CHANGE_TYPE_NODE_BOUNDS_CHANGED; change.window_id = window_id; change.bounds = new_bounds; + change.state = new_state; change.local_surface_id_allocation = local_surface_id_allocation; AddChange(change); }
diff --git a/services/ws/test_change_tracker.h b/services/ws/test_change_tracker.h index 6a512b8..cf6be0e 100644 --- a/services/ws/test_change_tracker.h +++ b/services/ws/test_change_tracker.h
@@ -90,6 +90,7 @@ Id window_id2 = 0; Id window_id3 = 0; gfx::Rect bounds; + ui::WindowShowState state; viz::FrameSinkId frame_sink_id; base::Optional<viz::LocalSurfaceIdAllocation> local_surface_id_allocation; int32_t event_action = 0; @@ -190,6 +191,7 @@ void OnWindowBoundsChanged( Id window_id, const gfx::Rect& new_bounds, + ui::WindowShowState state, const base::Optional<viz::LocalSurfaceIdAllocation>& local_surface_id_allocation); void OnWindowTransformChanged(Id window_id);
diff --git a/services/ws/test_window_tree_client.cc b/services/ws/test_window_tree_client.cc index 06caae4f..838300b 100644 --- a/services/ws/test_window_tree_client.cc +++ b/services/ws/test_window_tree_client.cc
@@ -124,6 +124,7 @@ void TestWindowTreeClient::OnWindowBoundsChanged( Id window_id, const gfx::Rect& new_bounds, + ui::WindowShowState state, const base::Optional<viz::LocalSurfaceIdAllocation>& local_surface_id_allocation) { // The bounds of the root may change during startup on Android at random @@ -131,7 +132,7 @@ // it is ignored. if (window_id == root_window_id_ && !track_root_bounds_changes_) return; - tracker_.OnWindowBoundsChanged(window_id, new_bounds, + tracker_.OnWindowBoundsChanged(window_id, new_bounds, state, local_surface_id_allocation); }
diff --git a/services/ws/test_window_tree_client.h b/services/ws/test_window_tree_client.h index 9065d74..3707a11 100644 --- a/services/ws/test_window_tree_client.h +++ b/services/ws/test_window_tree_client.h
@@ -111,6 +111,7 @@ void OnWindowBoundsChanged( Id window_id, const gfx::Rect& new_bounds, + ui::WindowShowState state, const base::Optional<viz::LocalSurfaceIdAllocation>& local_surface_id_allocation) override; void OnWindowTransformChanged(Id window_id,
diff --git a/services/ws/window_tree.cc b/services/ws/window_tree.cc index d67fe66..0f8cbfd 100644 --- a/services/ws/window_tree.cc +++ b/services/ws/window_tree.cc
@@ -705,6 +705,9 @@ : kInvalidTransportId; window_data->bounds = is_top_level ? window->GetBoundsInScreen() : window->bounds(); + window_data->state = is_top_level + ? window->GetProperty(aura::client::kShowStateKey) + : ui::SHOW_STATE_DEFAULT; window_data->properties = window_service_->property_converter()->GetTransportProperties(window); window_data->visible = (!IsClientRootWindow(window) || is_top_level) @@ -1298,9 +1301,10 @@ // The window's bounds changed, but not to the value the client requested. // Tell the client the new value, and return false, which triggers the client // to use the value supplied to OnWindowBoundsChanged(). - window_tree_client_->OnWindowBoundsChanged(TransportIdForWindow(window), - window->bounds(), - local_surface_id_allocation); + window_tree_client_->OnWindowBoundsChanged( + TransportIdForWindow(window), window->bounds(), + window->GetProperty(aura::client::kShowStateKey), + local_surface_id_allocation); return false; }
diff --git a/services/ws/window_tree_client_unittest.cc b/services/ws/window_tree_client_unittest.cc index f26bb68..3b4f250 100644 --- a/services/ws/window_tree_client_unittest.cc +++ b/services/ws/window_tree_client_unittest.cc
@@ -325,6 +325,7 @@ void OnWindowBoundsChanged( Id window_id, const gfx::Rect& new_bounds, + ui::WindowShowState state, const base::Optional<viz::LocalSurfaceIdAllocation>& local_surface_id_allocation) override { // The bounds of the root may change during startup on Android at random @@ -332,7 +333,7 @@ // it is ignored. if (window_id == root_window_id_ && !track_root_bounds_changes_) return; - tracker()->OnWindowBoundsChanged(window_id, new_bounds, + tracker()->OnWindowBoundsChanged(window_id, new_bounds, state, local_surface_id_allocation); } void OnWindowTransformChanged(Id window_id,
diff --git a/skia/ext/SkOpts_hsw_stub.cc b/skia/ext/SkOpts_hsw_stub.cc deleted file mode 100644 index 70573c85..0000000 --- a/skia/ext/SkOpts_hsw_stub.cc +++ /dev/null
@@ -1,13 +0,0 @@ -// 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. - -// This is SkOpts_hsw.cpp, stubbed out to do nothing. This is used by the -// SyzyAsan builds because the Syzygy instrumentation pipeline doesn't support -// the AVX2 and F16C instructions. - -namespace SkOpts { - void Init_hsw(); - void Init_hsw() {} -} -
diff --git a/storage/browser/blob/README.md b/storage/browser/blob/README.md index 9ed3fa8..3543ed45 100644 --- a/storage/browser/blob/README.md +++ b/storage/browser/blob/README.md
@@ -311,25 +311,22 @@ responsible for creating & managing all the state of constructing blobs, as well as all blob handle generation and general blob status access. -When a `BlobDataBuilder` is given to the context, whether from the -`BlobTransportHost` or from elsewhere, the context will do the following: +When a `BlobDataBuilder` is given to the context, it will do the following: 1. Find all dependent blobs in the new blob (any blob reference in the blob item list), and create a 'slice' of their items for the new blob. 2. Create the final blob item list representation, which creates a new blob item list which inserts these 'slice' items into the blob reference spots. This is 'flattening' the blob. -3. Ask the `BlobMemoryManager` for file or memory quota for the transportation -if necessary - * When the quota request is granted, notify the `BlobTransportHost` that to - begin transporting the data. -4. Ask the `BlobMemoryManager` for memory quota for any copies necessary for +3. Ask the `BlobMemoryController` for file or memory quota for the +transportation if necessary. +4. Ask the `BlobMemoryController` for memory quota for any copies necessary for blob slicing. 5. Adds completion callbacks to any blobs our blob depends on. When all of the following conditions are met: -1. The `BlobTransportHost` tells us it has transported all the data (or we +1. The `BlobRegistry` tells us it has transported all the data (or we don't need to transport data), 2. The `BlobMemoryManager` approves our memory quota for slice copies (or we don't need slice copies), and
diff --git a/storage/browser/blob/blob_storage_context.h b/storage/browser/blob/blob_storage_context.h index a323049..25b32c6 100644 --- a/storage/browser/blob/blob_storage_context.h +++ b/storage/browser/blob/blob_storage_context.h
@@ -32,7 +32,6 @@ namespace content { class BlobDispatcherHost; class BlobDispatcherHostTest; -class BlobTransportHostTest; class ChromeBlobStorageContext; class ShareableBlobDataItem; } @@ -164,10 +163,8 @@ protected: friend class content::BlobDispatcherHost; friend class content::BlobDispatcherHostTest; - friend class content::BlobTransportHostTest; friend class content::ChromeBlobStorageContext; friend class BlobBuilderFromStream; - friend class BlobTransportHost; friend class BlobDataHandle; friend class BlobDataHandle::BlobDataHandleShared; friend class BlobRegistryImplTest;
diff --git a/third_party/blink/public/mojom/web_feature/web_feature.mojom b/third_party/blink/public/mojom/web_feature/web_feature.mojom index 957ee9ce..fefc291f 100644 --- a/third_party/blink/public/mojom/web_feature/web_feature.mojom +++ b/third_party/blink/public/mojom/web_feature/web_feature.mojom
@@ -2263,6 +2263,8 @@ kInputTypeReset = 2855, kSelectElementSingle = 2856, kSelectElementMultiple = 2857, + kV8Animation_Effect_AttributeGetter = 2858, + kV8Animation_Effect_AttributeSetter = 2859, // Add new features immediately above this line. Don't change assigned // numbers of any item, and don't reuse removed slots.
diff --git a/third_party/blink/public/platform/web_crypto.h b/third_party/blink/public/platform/web_crypto.h index f2f2dd4..570ebc77 100644 --- a/third_party/blink/public/platform/web_crypto.h +++ b/third_party/blink/public/platform/web_crypto.h
@@ -109,31 +109,6 @@ cancel_; }; -class WebCryptoDigestor { - public: - virtual ~WebCryptoDigestor() = default; - - // Consume() will return |true| on the successful addition of data to the - // partially generated digest. It will return |false| when that fails. After - // a return of |false|, Consume() should not be called again (nor should - // Finish() be called). - virtual bool Consume(const unsigned char* data, unsigned data_size) { - return false; - } - - // Finish() will return |true| if the digest has been successfully computed - // and put into the result buffer, otherwise it will return |false|. In - // either case, neither Finish() nor Consume() should be called again after - // a call to Finish(). |result_data| is valid until the WebCrytpoDigestor - // object is destroyed. - virtual bool Finish(unsigned char*& result_data, unsigned& result_data_size) { - return false; - } - - protected: - WebCryptoDigestor() = default; -}; - class WebCrypto { public: // WebCrypto is the interface for starting one-shot cryptographic @@ -308,15 +283,6 @@ result.CompleteWithError(kWebCryptoErrorTypeNotSupported, ""); } - // This is the exception to the "Completing the request" guarantees - // outlined above. This is useful for Blink internal crypto and is not part - // of the WebCrypto standard. CreateDigestor must provide the result via - // the WebCryptoDigestor object synchronously. This will never return null. - virtual std::unique_ptr<WebCryptoDigestor> CreateDigestor( - WebCryptoAlgorithmId algorithm_id) { - return nullptr; - } - // ----------------------- // Structured clone // -----------------------
diff --git a/third_party/blink/public/web/web_ax_object.h b/third_party/blink/public/web/web_ax_object.h index f56b763e..4d227af5 100644 --- a/third_party/blink/public/web/web_ax_object.h +++ b/third_party/blink/public/web/web_ax_object.h
@@ -186,8 +186,11 @@ BLINK_EXPORT WebString StringValue() const; BLINK_EXPORT ax::mojom::TextDirection GetTextDirection() const; BLINK_EXPORT ax::mojom::TextPosition GetTextPosition() const; - // Bitmask from ax::mojom::TextStyle. - BLINK_EXPORT int32_t TextStyle() const; + BLINK_EXPORT void GetTextStyleAndTextDecorationStyle( + int32_t* text_style, + ax::mojom::TextDecorationStyle* text_overline_style, + ax::mojom::TextDecorationStyle* text_strikethrough_style, + ax::mojom::TextDecorationStyle* text_underline_style) const; BLINK_EXPORT WebURL Url() const; // Retrieves the accessible name of the object, an enum indicating where the
diff --git a/third_party/blink/renderer/core/animation/animation.idl b/third_party/blink/renderer/core/animation/animation.idl index 1c5fb3f..7b78ea2 100644 --- a/third_party/blink/renderer/core/animation/animation.idl +++ b/third_party/blink/renderer/core/animation/animation.idl
@@ -28,7 +28,7 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -// https://drafts.csswg.org/web-animations/#animation +// https://drafts.csswg.org/web-animations/#the-animation-interface enum AnimationPlayState { "idle", "pending", "running", "paused", "finished" }; @@ -39,8 +39,8 @@ RaisesException=Constructor, ActiveScriptWrappable ] interface Animation : EventTarget { + [Measure] attribute AnimationEffect? effect; // TODO(suzyh): Make timeline mutable. - [RuntimeEnabled=WebAnimationsAPI] attribute AnimationEffect? effect; [RuntimeEnabled=WebAnimationsAPI] readonly attribute AnimationTimeline? timeline; [Measure] attribute double? startTime; [Measure, RaisesException=Setter] attribute double? currentTime;
diff --git a/third_party/blink/renderer/core/animation/animation_effect.cc b/third_party/blink/renderer/core/animation/animation_effect.cc index 5750b86..27597d21 100644 --- a/third_party/blink/renderer/core/animation/animation_effect.cc +++ b/third_party/blink/renderer/core/animation/animation_effect.cc
@@ -208,7 +208,9 @@ current_phase, timing_); current_iteration = CalculateCurrentIteration( - iteration_duration, iteration_time, offset_active_time, timing_); + current_phase, active_time, iteration_duration, + timing_.iteration_count, timing_.iteration_start); + const base::Optional<double> transformed_time = CalculateTransformedTime( current_iteration, iteration_duration, iteration_time, timing_); @@ -259,8 +261,11 @@ kLocalIterationDuration, local_active_duration, offset_active_time, start_offset, current_phase, timing_); - current_iteration = CalculateCurrentIteration( - kLocalIterationDuration, iteration_time, offset_active_time, timing_); + current_iteration = CalculateCurrentIteration(current_phase, active_time, + /*iteration_duration=*/0, + timing_.iteration_count, + timing_.iteration_start); + progress = CalculateTransformedTime( current_iteration, kLocalIterationDuration, iteration_time, timing_); }
diff --git a/third_party/blink/renderer/core/animation/animation_effect.idl b/third_party/blink/renderer/core/animation/animation_effect.idl index 1aca06b..bf779a5 100644 --- a/third_party/blink/renderer/core/animation/animation_effect.idl +++ b/third_party/blink/renderer/core/animation/animation_effect.idl
@@ -30,9 +30,8 @@ // https://drafts.csswg.org/web-animations/#the-animationeffect-interface -[ - RuntimeEnabled=WebAnimationsAPI -] interface AnimationEffect { +[Exposed=Window] +interface AnimationEffect { EffectTiming getTiming(); ComputedEffectTiming getComputedTiming(); [RaisesException] void updateTiming(optional OptionalEffectTiming timing);
diff --git a/third_party/blink/renderer/core/animation/keyframe_effect.cc b/third_party/blink/renderer/core/animation/keyframe_effect.cc index f89d2a6..be54de7 100644 --- a/third_party/blink/renderer/core/animation/keyframe_effect.cc +++ b/third_party/blink/renderer/core/animation/keyframe_effect.cc
@@ -63,7 +63,6 @@ const ScriptValue& keyframes, const UnrestrictedDoubleOrKeyframeEffectOptions& options, ExceptionState& exception_state) { - DCHECK(RuntimeEnabledFeatures::WebAnimationsAPIEnabled()); if (element) { UseCounter::Count( element->GetDocument(), @@ -92,7 +91,6 @@ Element* element, const ScriptValue& keyframes, ExceptionState& exception_state) { - DCHECK(RuntimeEnabledFeatures::WebAnimationsAPIEnabled()); if (element) { UseCounter::Count( element->GetDocument(),
diff --git a/third_party/blink/renderer/core/animation/keyframe_effect.idl b/third_party/blink/renderer/core/animation/keyframe_effect.idl index 79a9096..c1e196c 100644 --- a/third_party/blink/renderer/core/animation/keyframe_effect.idl +++ b/third_party/blink/renderer/core/animation/keyframe_effect.idl
@@ -28,19 +28,19 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -// https://drafts.csswg.org/web-animations/#the-keyframeeffect-interfaces +// https://drafts.csswg.org/web-animations/#the-keyframeeffect-interface enum CompositeOperation { "replace", "add", "accumulate" }; [ + Exposed=Window, Constructor(Element? target, object? keyframes, optional (unrestricted double or KeyframeEffectOptions) options), Constructor(KeyframeEffect source), ConstructorCallWith=ScriptState, - RaisesException=Constructor, - RuntimeEnabled=WebAnimationsAPI + RaisesException=Constructor ] interface KeyframeEffect : AnimationEffect { attribute Element? target; - attribute CompositeOperation composite; - [CallWith=ScriptState] sequence<object> getKeyframes(); - [CallWith=ScriptState, RaisesException] void setKeyframes(object? keyframes); + [RuntimeEnabled=WebAnimationsAPI] attribute CompositeOperation composite; + [CallWith=ScriptState, RuntimeEnabled=WebAnimationsAPI] sequence<object> getKeyframes(); + [CallWith=ScriptState, RaisesException, RuntimeEnabled=WebAnimationsAPI] void setKeyframes(object? keyframes); };
diff --git a/third_party/blink/renderer/core/animation/timing_calculations.h b/third_party/blink/renderer/core/animation/timing_calculations.h index 0002986..37f28a20 100644 --- a/third_party/blink/renderer/core/animation/timing_calculations.h +++ b/third_party/blink/renderer/core/animation/timing_calculations.h
@@ -50,6 +50,12 @@ return x.is_zero() || y == 0 ? 0 : (x * y).InSecondsF(); } +static inline bool IsWithinEpsilon(double a, double b) { + // Permit 2-bits of quantization error. Threshold based on experimentation + // with accuracy of fmod. + return std::abs(a - b) <= 2.0 * std::numeric_limits<double>::epsilon(); +} + // https://drafts.csswg.org/web-animations-1/#animation-effect-phases-and-states static inline AnimationEffect::Phase CalculatePhase( double active_duration, @@ -170,27 +176,104 @@ return iteration_time; } -static inline double CalculateCurrentIteration(double iteration_duration, - double iteration_time, - double offset_active_time, - const Timing& specified) { - DCHECK_GT(iteration_duration, 0); - DCHECK(IsNull(iteration_time) || iteration_time >= 0); - - if (IsNull(offset_active_time)) +// Calculates the overall progress, which describes the number of iterations +// that have completed (including partial iterations). +// https://drafts.csswg.org/web-animations/#calculating-the-overall-progress +static inline double CalculateOverallProgress(AnimationEffect::Phase phase, + double active_time, + double iteration_duration, + double iteration_count, + double iteration_start) { + // 1. If the active time is unresolved, return unresolved. + if (IsNull(active_time)) return NullValue(); - DCHECK_GE(iteration_time, 0); - DCHECK_LE(iteration_time, iteration_duration); - DCHECK_GE(offset_active_time, 0); + // 2. Calculate an initial value for overall progress. + double overall_progress = 0; + if (!iteration_duration) { + if (phase != AnimationEffect::kPhaseBefore) + overall_progress = iteration_count; + } else { + overall_progress = active_time / iteration_duration; + } - if (!offset_active_time) - return 0; + return overall_progress + iteration_start; +} - if (iteration_time == iteration_duration) - return specified.iteration_start + specified.iteration_count - 1; +// Calculates the simple iteration progress, which is a fraction of the progress +// through the current iteration that ignores transformations to the time +// introduced by the playback direction or timing functions applied to the +// effect. +// https://drafts.csswg.org/web-animations/#calculating-the-simple-iteration +// -progress +static inline double CalculateSimpleIterationProgress( + AnimationEffect::Phase phase, + double overall_progress, + double iteration_start, + double active_time, + double iteration_duration, + double iteration_count) { + // 1. If the overall progress is unresolved, return unresolved. + if (IsNull(overall_progress)) + return NullValue(); - return floor(offset_active_time / iteration_duration); + // 2. If overall progress is infinity, let the simple iteration progress be + // iteration start % 1.0, otherwise, let the simple iteration progress be + // overall progress % 1.0. + double simple_iteration_progress = std::isinf(overall_progress) + ? fmod(iteration_start, 1.0) + : fmod(overall_progress, 1.0); + + const double active_duration = iteration_duration * iteration_count; + + // 3. If all of the following conditions are true, + // * the simple iteration progress calculated above is zero, and + // * the animation effect is in the active phase or the after phase, and + // * the active time is equal to the active duration, and + // * the iteration count is not equal to zero. + // let the simple iteration progress be 1.0. + if (IsWithinEpsilon(simple_iteration_progress, 0.0) && + (phase == AnimationEffect::kPhaseActive || + phase == AnimationEffect::kPhaseAfter) && + IsWithinEpsilon(active_time, active_duration) && + !IsWithinEpsilon(iteration_count, 0.0)) { + simple_iteration_progress = 1.0; + } + + // 4. Return simple iteration progress. + return simple_iteration_progress; +} + +// https://drafts.csswg.org/web-animations/#calculating-the-current-iteration +static inline double CalculateCurrentIteration(AnimationEffect::Phase phase, + double active_time, + double iteration_duration, + double iteration_count, + double iteration_start) { + // 1. If the active time is unresolved, return unresolved. + if (IsNull(active_time)) + return NullValue(); + + // 2. If the animation effect is in the after phase and the iteration count + // is infinity, return infinity. + if (phase == AnimationEffect::kPhaseAfter && std::isinf(iteration_count)) { + return std::numeric_limits<double>::infinity(); + } + + const double overall_progress = CalculateOverallProgress( + phase, active_time, iteration_duration, iteration_count, iteration_start); + + // 3. If the simple iteration progress is 1.0, return floor(overall progress) + // - 1. + const double simple_iteration_progress = CalculateSimpleIterationProgress( + phase, overall_progress, iteration_start, active_time, iteration_duration, + iteration_count); + + if (simple_iteration_progress == 1.0) + return floor(overall_progress) - 1; + + // 4. Otherwise, return floor(overall progress). + return floor(overall_progress); } static inline double CalculateDirectedTime(double current_iteration, @@ -203,6 +286,9 @@ if (IsNull(iteration_time)) return NullValue(); + if (IsNull(current_iteration)) + return NullValue(); + DCHECK_GE(current_iteration, 0); DCHECK_GE(iteration_time, 0); DCHECK_LE(iteration_time, iteration_duration);
diff --git a/third_party/blink/renderer/core/animation/timing_calculations_test.cc b/third_party/blink/renderer/core/animation/timing_calculations_test.cc index 20058c4..7832d32 100644 --- a/third_party/blink/renderer/core/animation/timing_calculations_test.cc +++ b/third_party/blink/renderer/core/animation/timing_calculations_test.cc
@@ -120,24 +120,55 @@ } TEST(AnimationTimingCalculationsTest, CurrentIteration) { - Timing timing; + // If the active time is null. + EXPECT_TRUE(IsNull(CalculateCurrentIteration(AnimationEffect::kPhaseAfter, + /*active_time=*/NullValue(), + /*iteration_duration=*/1.0, + /*iteration_count=*/1.0, + /*iteration_start=*/1.0))); - // calculateCurrentIteration( - // iterationDuration, iterationTime, scaledActiveTime, timing) + // If the active time is zero. + EXPECT_EQ(0, CalculateCurrentIteration(AnimationEffect::kPhaseAfter, + /*active_time=*/0.0, + /*iteration_duration=*/1.0, + /*iteration_count=*/0.0, + /*iteration_start=*/0.0)); - // if the scaled active time is null - EXPECT_TRUE(IsNull(CalculateCurrentIteration(1, 1, NullValue(), timing))); + // If the iteration count is infinite. + const double inf = std::numeric_limits<double>::infinity(); + EXPECT_EQ(inf, CalculateCurrentIteration(AnimationEffect::kPhaseAfter, + /*active_time=*/1.0, + /*iteration_duration=*/1.0, + /*iteration_count=*/inf, + /*iteration_start=*/1.0)); - // if the scaled active time is zero - EXPECT_EQ(0, CalculateCurrentIteration(1, 1, 0, timing)); + // If iteration duration is zero, calculate progress based on iteration count. + EXPECT_EQ(3, CalculateCurrentIteration(AnimationEffect::kPhaseActive, + /*active_time=*/3.0, + /*iteration_duration=*/0.0, + /*iteration_count=*/3.0, + /*iteration_start=*/0.0)); + // ...unless in before phase, in which case progress is zero. + EXPECT_EQ(0, CalculateCurrentIteration(AnimationEffect::kPhaseBefore, + /*active_time=*/3.0, + /*iteration_duration=*/0.0, + /*iteration_count=*/3.0, + /*iteration_start=*/0.0)); - // if the iteration time equals the iteration duration - timing.iteration_start = 4; - timing.iteration_count = 7; - EXPECT_EQ(10, CalculateCurrentIteration(5, 5, 9, timing)); + // Hold the endpoint of the final iteration of ending precisely on an + // iteration boundary. + EXPECT_EQ(2, CalculateCurrentIteration(AnimationEffect::kPhaseAfter, + /*active_time=*/3.0, + /*iteration_duration=*/1.0, + /*iteration_count=*/3.0, + /*iteration_start=*/0.0)); - // otherwise - EXPECT_EQ(3, CalculateCurrentIteration(3.2, 3.1, 10, timing)); + // Otherwise. + EXPECT_EQ(2, CalculateCurrentIteration(AnimationEffect::kPhaseAfter, + /*active_time=*/2.5, + /*iteration_duration=*/1.0, + /*iteration_count=*/0.0, + /*iteration_start=*/0.0)); } TEST(AnimationTimingCalculationsTest, DirectedTime) {
diff --git a/third_party/blink/renderer/core/css/css_style_sheet.cc b/third_party/blink/renderer/core/css/css_style_sheet.cc index e011bff..77be14b 100644 --- a/third_party/blink/renderer/core/css/css_style_sheet.cc +++ b/third_party/blink/renderer/core/css/css_style_sheet.cc
@@ -413,11 +413,16 @@ child_rule_cssom_wrappers_.size() == contents_->RuleCount()); if (index >= length()) { - exception_state.ThrowDOMException( - DOMExceptionCode::kIndexSizeError, - "The index provided (" + String::Number(index) + - ") is larger than the maximum index (" + - String::Number(length() - 1) + ")."); + if (length()) { + exception_state.ThrowDOMException( + DOMExceptionCode::kIndexSizeError, + "The index provided (" + String::Number(index) + + ") is larger than the maximum index (" + + String::Number(length() - 1) + ")."); + } else { + exception_state.ThrowDOMException(DOMExceptionCode::kIndexSizeError, + "Style sheet is empty (length 0)."); + } return; } RuleMutationScope mutation_scope(this);
diff --git a/third_party/blink/renderer/core/exported/web_frame_test.cc b/third_party/blink/renderer/core/exported/web_frame_test.cc index 45788ecf..52d04f4f 100644 --- a/third_party/blink/renderer/core/exported/web_frame_test.cc +++ b/third_party/blink/renderer/core/exported/web_frame_test.cc
@@ -11076,6 +11076,18 @@ ->Url()); } +TEST_F(WebFrameTest, EmptyJavascriptFrameUrl) { + std::string url = "data:text/html,<iframe src=\"javascript:''\"></iframe>"; + frame_test_helpers::WebViewHelper helper; + helper.InitializeAndLoad(url); + RunPendingTasks(); + + LocalFrame* child = To<LocalFrame>( + helper.GetWebView()->GetPage()->MainFrame()->Tree().FirstChild()); + EXPECT_EQ(BlankURL(), child->GetDocument()->Url()); + EXPECT_EQ(BlankURL(), child->Loader().GetDocumentLoader()->Url()); +} + class TestResourcePriorityWebFrameClient : public frame_test_helpers::TestWebFrameClient { public:
diff --git a/third_party/blink/renderer/core/exported/web_view_impl.cc b/third_party/blink/renderer/core/exported/web_view_impl.cc index 8897879..d3ba936d 100644 --- a/third_party/blink/renderer/core/exported/web_view_impl.cc +++ b/third_party/blink/renderer/core/exported/web_view_impl.cc
@@ -1528,9 +1528,8 @@ } void WebViewImpl::BeginUpdateLayers() { - if (MainFrameImpl()) { + if (MainFrameImpl()) update_layers_start_time_.emplace(CurrentTimeTicks()); - } } void WebViewImpl::EndUpdateLayers() {
diff --git a/third_party/blink/renderer/core/exported/web_view_test.cc b/third_party/blink/renderer/core/exported/web_view_test.cc index 68df7b3..3bb72b4d 100644 --- a/third_party/blink/renderer/core/exported/web_view_test.cc +++ b/third_party/blink/renderer/core/exported/web_view_test.cc
@@ -3542,6 +3542,70 @@ web_view_helper.Reset(); // Remove dependency on locally scoped client. } +class ViewReusingWebViewClient : public frame_test_helpers::TestWebViewClient { + public: + ViewReusingWebViewClient() = default; + + // WebViewClient methods + WebView* CreateView(WebLocalFrame*, + const WebURLRequest&, + const WebWindowFeatures&, + const WebString& name, + WebNavigationPolicy, + bool, + WebSandboxFlags, + const FeaturePolicy::FeatureState&, + const SessionStorageNamespaceId&) override { + return web_view_; + } + + void SetWebView(WebView* view) { web_view_ = view; } + + private: + WebView* web_view_ = nullptr; +}; + +class NavigationPolicyWebFrameClient + : public frame_test_helpers::TestWebFrameClient { + public: + NavigationPolicyWebFrameClient() = default; + + void BeginNavigation(std::unique_ptr<WebNavigationInfo> info) override { + begin_navigation_called_ = true; + last_navigation_policy_ = info->navigation_policy; + } + + bool BeginNavigationWasCalled() const { return begin_navigation_called_; } + WebNavigationPolicy LastNavigationPolicy() const { + return last_navigation_policy_; + } + + private: + WebNavigationPolicy last_navigation_policy_ = kWebNavigationPolicyCurrentTab; + bool begin_navigation_called_ = false; +}; + +TEST_F(WebViewTest, + ReuseExistingWindowOnCreateViewUsesCorrectNavigationPolicy) { + ViewReusingWebViewClient view_client; + NavigationPolicyWebFrameClient frame_client; + frame_test_helpers::WebViewHelper web_view_helper; + WebViewImpl* web_view_impl = + web_view_helper.Initialize(&frame_client, &view_client); + view_client.SetWebView(web_view_impl); + LocalFrame* frame = To<LocalFrame>(web_view_impl->GetPage()->MainFrame()); + + // Request a new window, but the WebViewClient will decline to and instead + // return the current window. + WebURLRequest web_url_request(KURL("about:blank")); + FrameLoadRequest request(frame->GetDocument(), + web_url_request.ToResourceRequest(), "_blank"); + frame->Loader().StartNavigation(request); + ASSERT_TRUE(frame_client.BeginNavigationWasCalled()); + EXPECT_EQ(kWebNavigationPolicyCurrentTab, + frame_client.LastNavigationPolicy()); +} + TEST_F(WebViewTest, DispatchesFocusOutFocusInOnViewToggleFocus) { RegisterMockedHttpURLLoad("focusout_focusin_events.html"); WebViewImpl* web_view = web_view_helper_.InitializeAndLoad(
diff --git a/third_party/blink/renderer/core/frame/local_dom_window.cc b/third_party/blink/renderer/core/frame/local_dom_window.cc index b661fab5..5fc087b 100644 --- a/third_party/blink/renderer/core/frame/local_dom_window.cc +++ b/third_party/blink/renderer/core/frame/local_dom_window.cc
@@ -1477,6 +1477,35 @@ WebWindowFeatures window_features = GetWindowFeaturesFromString(features); + FrameLoadRequest frame_request(active_document, + ResourceRequest(completed_url), + target.IsEmpty() ? "_blank" : target); + frame_request.SetNavigationPolicy( + NavigationPolicyForCreateWindow(window_features)); + frame_request.SetFeaturesForWindowOpen(window_features); + frame_request.SetShouldSetOpener(window_features.noopener ? kNeverSetOpener + : kMaybeSetOpener); + + // Normally, FrameLoader would take care of setting the referrer for a + // navigation that is triggered from javascript. However, creating a window + // goes through sufficient processing that it eventually enters FrameLoader as + // an embedder-initiated navigation. FrameLoader assumes no responsibility + // for generating an embedder-initiated navigation's referrer, so we need to + // ensure the proper referrer is set now. + // TODO(domfarolino): Stop setting ResourceRequest's HTTP Referrer and store + // this is a separate member. See https://crbug.com/850813. + frame_request.GetResourceRequest().SetHttpReferrer( + SecurityPolicy::GenerateReferrer(active_document->GetReferrerPolicy(), + completed_url, + active_document->OutgoingReferrer())); + + frame_request.GetResourceRequest().SetHasUserGesture( + LocalFrame::HasTransientUserActivation(GetFrame())); + GetFrame()->MaybeLogAdClickNavigation(); + + if (const WebInputEvent* input_event = CurrentInputEvent::Get()) + frame_request.SetInputStartTime(input_event->TimeStamp()); + // Get the target frame for the special cases of _top and _parent. // In those cases, we schedule a location change right now and return early. Frame* target_frame = nullptr; @@ -1504,25 +1533,23 @@ } } - if (!target_frame) { - return CreateWindow(completed_url, target, window_features, - *incumbent_window, *GetFrame()); - } + bool created = false; + if (!target_frame) + target_frame = CreateNewWindow(*GetFrame(), frame_request, created); + if (!target_frame) + return nullptr; if (!active_document->GetFrame() || !active_document->GetFrame()->CanNavigate(*target_frame)) { return nullptr; } - if (!url_string.IsEmpty() && + if ((!completed_url.IsEmpty() || created) && !target_frame->DomWindow()->IsInsecureScriptAccess(*incumbent_window, completed_url)) { - FrameLoadRequest request(active_document, ResourceRequest(completed_url)); - request.GetResourceRequest().SetHasUserGesture( - LocalFrame::HasTransientUserActivation(GetFrame())); - if (const WebInputEvent* input_event = CurrentInputEvent::Get()) - request.SetInputStartTime(input_event->TimeStamp()); - target_frame->Navigate(request, WebFrameLoadType::kStandard); + frame_request.SetFrameName("_self"); + frame_request.SetNavigationPolicy(kNavigationPolicyCurrentTab); + target_frame->Navigate(frame_request, WebFrameLoadType::kStandard); } // TODO(japhet): window-open-noopener.html?_top and several tests in @@ -1538,7 +1565,8 @@ if (window_features.noopener) return nullptr; - target_frame->Client()->SetOpener(GetFrame()); + if (!created) + target_frame->Client()->SetOpener(GetFrame()); return target_frame->DomWindow(); }
diff --git a/third_party/blink/renderer/core/frame/local_frame_ukm_aggregator_test.cc b/third_party/blink/renderer/core/frame/local_frame_ukm_aggregator_test.cc index 5280e25..73210906 100644 --- a/third_party/blink/renderer/core/frame/local_frame_ukm_aggregator_test.cc +++ b/third_party/blink/renderer/core/frame/local_frame_ukm_aggregator_test.cc
@@ -237,6 +237,10 @@ elapsed_time += Now() - start_time; } + // Should be no more samples. + VerifyEntries(2u, millisecond_per_frame, millisecond_per_step, + expected_percentage); + // If we record, then the next interval is still shorter than the last frame, // we should sample again. Set a short sample period to generate 2 events // for the next update. The calculation below ensures we get 2 events for
diff --git a/third_party/blink/renderer/core/frame/web_frame_widget_impl.cc b/third_party/blink/renderer/core/frame/web_frame_widget_impl.cc index a33d2fb..d0cae95 100644 --- a/third_party/blink/renderer/core/frame/web_frame_widget_impl.cc +++ b/third_party/blink/renderer/core/frame/web_frame_widget_impl.cc
@@ -324,9 +324,8 @@ } void WebFrameWidgetImpl::BeginUpdateLayers() { - if (LocalRootImpl()) { + if (LocalRootImpl()) update_layers_start_time_.emplace(CurrentTimeTicks()); - } } void WebFrameWidgetImpl::EndUpdateLayers() {
diff --git a/third_party/blink/renderer/core/frame/web_view_frame_widget.cc b/third_party/blink/renderer/core/frame/web_view_frame_widget.cc index f90afed..06e907bf 100644 --- a/third_party/blink/renderer/core/frame/web_view_frame_widget.cc +++ b/third_party/blink/renderer/core/frame/web_view_frame_widget.cc
@@ -70,11 +70,11 @@ } void WebViewFrameWidget::BeginUpdateLayers() { - web_view_->BeginRafAlignedInput(); + web_view_->BeginUpdateLayers(); } void WebViewFrameWidget::EndUpdateLayers() { - web_view_->EndRafAlignedInput(); + web_view_->EndUpdateLayers(); } void WebViewFrameWidget::BeginCommitCompositorFrame() {
diff --git a/third_party/blink/renderer/core/inspector/BUILD.gn b/third_party/blink/renderer/core/inspector/BUILD.gn index 99953c85..e644e553 100644 --- a/third_party/blink/renderer/core/inspector/BUILD.gn +++ b/third_party/blink/renderer/core/inspector/BUILD.gn
@@ -12,8 +12,6 @@ blink_core_sources("inspector") { sources = [ - "add_string_to_digestor.cc", - "add_string_to_digestor.h", "console_message.cc", "console_message.h", "console_message_storage.cc",
diff --git a/third_party/blink/renderer/core/inspector/add_string_to_digestor.cc b/third_party/blink/renderer/core/inspector/add_string_to_digestor.cc deleted file mode 100644 index 0876479a..0000000 --- a/third_party/blink/renderer/core/inspector/add_string_to_digestor.cc +++ /dev/null
@@ -1,18 +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 "third_party/blink/renderer/core/inspector/add_string_to_digestor.h" - -#include "third_party/blink/public/platform/web_crypto.h" -#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h" - -namespace blink { - -void AddStringToDigestor(WebCryptoDigestor* digestor, const String& string) { - const CString c_string = string.Utf8(); - digestor->Consume(reinterpret_cast<const unsigned char*>(c_string.data()), - c_string.length()); -} - -} // namespace blink
diff --git a/third_party/blink/renderer/core/inspector/add_string_to_digestor.h b/third_party/blink/renderer/core/inspector/add_string_to_digestor.h deleted file mode 100644 index f795ad08..0000000 --- a/third_party/blink/renderer/core/inspector/add_string_to_digestor.h +++ /dev/null
@@ -1,17 +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 THIRD_PARTY_BLINK_RENDERER_CORE_INSPECTOR_ADD_STRING_TO_DIGESTOR_H_ -#define THIRD_PARTY_BLINK_RENDERER_CORE_INSPECTOR_ADD_STRING_TO_DIGESTOR_H_ - -namespace WTF { -class String; -} - -namespace blink { -class WebCryptoDigestor; -void AddStringToDigestor(WebCryptoDigestor*, const WTF::String&); -} // namespace blink - -#endif // THIRD_PARTY_BLINK_RENDERER_CORE_INSPECTOR_ADD_STRING_TO_DIGESTOR_H_
diff --git a/third_party/blink/renderer/core/inspector/dom_patch_support.cc b/third_party/blink/renderer/core/inspector/dom_patch_support.cc index 32ed885..bcae61a5 100644 --- a/third_party/blink/renderer/core/inspector/dom_patch_support.cc +++ b/third_party/blink/renderer/core/inspector/dom_patch_support.cc
@@ -45,7 +45,6 @@ #include "third_party/blink/renderer/core/html/html_document.h" #include "third_party/blink/renderer/core/html/html_head_element.h" #include "third_party/blink/renderer/core/html/parser/html_document_parser.h" -#include "third_party/blink/renderer/core/inspector/add_string_to_digestor.h" #include "third_party/blink/renderer/core/inspector/dom_editor.h" #include "third_party/blink/renderer/core/inspector/inspector_history.h" #include "third_party/blink/renderer/core/xml/parser/xml_document_parser.h" @@ -436,45 +435,43 @@ Node* node, UnusedNodesMap* unused_nodes_map) { Digest* digest = MakeGarbageCollected<Digest>(node); - - std::unique_ptr<WebCryptoDigestor> digestor = - CreateDigestor(kHashAlgorithmSha1); + Digestor digestor(kHashAlgorithmSha1); DigestValue digest_result; Node::NodeType node_type = node->getNodeType(); - digestor->Consume(reinterpret_cast<const unsigned char*>(&node_type), - sizeof(node_type)); - AddStringToDigestor(digestor.get(), node->nodeName()); - AddStringToDigestor(digestor.get(), node->nodeValue()); + digestor.Update( + {reinterpret_cast<const uint8_t*>(&node_type), sizeof(node_type)}); + digestor.UpdateUtf8(node->nodeName()); + digestor.UpdateUtf8(node->nodeValue()); if (node->IsElementNode()) { Element& element = ToElement(*node); Node* child = element.firstChild(); while (child) { Digest* child_info = CreateDigest(child, unused_nodes_map); - AddStringToDigestor(digestor.get(), child_info->sha1_); + digestor.UpdateUtf8(child_info->sha1_); child = child->nextSibling(); digest->children_.push_back(child_info); } AttributeCollection attributes = element.AttributesWithoutUpdate(); if (!attributes.IsEmpty()) { - std::unique_ptr<WebCryptoDigestor> attrs_digestor = - CreateDigestor(kHashAlgorithmSha1); + Digestor attrs_digestor(kHashAlgorithmSha1); for (auto& attribute : attributes) { - AddStringToDigestor(attrs_digestor.get(), - attribute.GetName().ToString()); - AddStringToDigestor(attrs_digestor.get(), - attribute.Value().GetString()); + attrs_digestor.UpdateUtf8(attribute.GetName().ToString()); + attrs_digestor.UpdateUtf8(attribute.Value().GetString()); } - FinishDigestor(attrs_digestor.get(), digest_result); + + attrs_digestor.Finish(digest_result); + DCHECK(!attrs_digestor.has_failed()); digest->attrs_sha1_ = Base64Encode(reinterpret_cast<const char*>(digest_result.data()), 10); - AddStringToDigestor(digestor.get(), digest->attrs_sha1_); - digest_result.clear(); + digestor.UpdateUtf8(digest->attrs_sha1_); } } - FinishDigestor(digestor.get(), digest_result); + + digestor.Finish(digest_result); + DCHECK(!digestor.has_failed()); digest->sha1_ = Base64Encode(reinterpret_cast<const char*>(digest_result.data()), 10);
diff --git a/third_party/blink/renderer/core/inspector/inspector_animation_agent.cc b/third_party/blink/renderer/core/inspector/inspector_animation_agent.cc index 2745117..4b33f1e 100644 --- a/third_party/blink/renderer/core/inspector/inspector_animation_agent.cc +++ b/third_party/blink/renderer/core/inspector/inspector_animation_agent.cc
@@ -24,7 +24,6 @@ #include "third_party/blink/renderer/core/css/css_style_rule.h" #include "third_party/blink/renderer/core/css/resolver/style_resolver.h" #include "third_party/blink/renderer/core/frame/local_frame.h" -#include "third_party/blink/renderer/core/inspector/add_string_to_digestor.h" #include "third_party/blink/renderer/core/inspector/identifiers_factory.h" #include "third_party/blink/renderer/core/inspector/inspected_frames.h" #include "third_party/blink/renderer/core/inspector/inspector_css_agent.h" @@ -424,10 +423,9 @@ Element* element = effect->target(); HeapVector<Member<CSSStyleDeclaration>> styles = css_agent_->MatchingStyles(element); - std::unique_ptr<WebCryptoDigestor> digestor = - CreateDigestor(kHashAlgorithmSha1); - AddStringToDigestor(digestor.get(), type); - AddStringToDigestor(digestor.get(), animation.id()); + Digestor digestor(kHashAlgorithmSha1); + digestor.UpdateUtf8(type); + digestor.UpdateUtf8(animation.id()); for (const CSSProperty* property : css_properties) { CSSStyleDeclaration* style = css_agent_->FindEffectiveDeclaration(*property, styles); @@ -435,14 +433,13 @@ if (!style || !style->ParentStyleSheet() || !style->parentRule() || style->parentRule()->type() != CSSRule::kStyleRule) continue; - AddStringToDigestor(digestor.get(), property->GetPropertyNameString()); - AddStringToDigestor(digestor.get(), - css_agent_->StyleSheetId(style->ParentStyleSheet())); - AddStringToDigestor(digestor.get(), - To<CSSStyleRule>(style->parentRule())->selectorText()); + digestor.UpdateUtf8(property->GetPropertyNameString()); + digestor.UpdateUtf8(css_agent_->StyleSheetId(style->ParentStyleSheet())); + digestor.UpdateUtf8(To<CSSStyleRule>(style->parentRule())->selectorText()); } DigestValue digest_result; - FinishDigestor(digestor.get(), digest_result); + digestor.Finish(digest_result); + DCHECK(!digestor.has_failed()); return Base64Encode(reinterpret_cast<const char*>(digest_result.data()), 10); }
diff --git a/third_party/blink/renderer/core/inspector/inspector_dom_snapshot_agent.cc b/third_party/blink/renderer/core/inspector/inspector_dom_snapshot_agent.cc index 985ae21..bb9a94d 100644 --- a/third_party/blink/renderer/core/inspector/inspector_dom_snapshot_agent.cc +++ b/third_party/blink/renderer/core/inspector/inspector_dom_snapshot_agent.cc
@@ -236,7 +236,7 @@ } Response InspectorDOMSnapshotAgent::getSnapshot( - std::unique_ptr<protocol::Array<String>> style_whitelist, + std::unique_ptr<protocol::Array<String>> style_filter, protocol::Maybe<bool> include_event_listeners, protocol::Maybe<bool> include_paint_order, protocol::Maybe<bool> include_user_agent_shadow_tree, @@ -256,15 +256,15 @@ computed_styles_ = protocol::Array<protocol::DOMSnapshot::ComputedStyle>::create(); computed_styles_map_ = std::make_unique<ComputedStylesMap>(); - css_property_whitelist_ = std::make_unique<CSSPropertyWhitelist>(); + css_property_filter_ = std::make_unique<CSSPropertyFilter>(); - // Look up the CSSPropertyIDs for each entry in |style_whitelist|. - for (wtf_size_t i = 0; i < style_whitelist->length(); i++) { - CSSPropertyID property_id = cssPropertyID(style_whitelist->get(i)); + // Look up the CSSPropertyIDs for each entry in |style_filter|. + for (wtf_size_t i = 0; i < style_filter->length(); i++) { + CSSPropertyID property_id = cssPropertyID(style_filter->get(i)); if (property_id == CSSPropertyID::kInvalid) continue; - css_property_whitelist_->push_back( - std::make_pair(style_whitelist->get(i), property_id)); + css_property_filter_->push_back( + std::make_pair(style_filter->get(i), property_id)); } if (include_paint_order.fromMaybe(false)) { @@ -282,7 +282,7 @@ *layout_tree_nodes = std::move(layout_tree_nodes_); *computed_styles = std::move(computed_styles_); computed_styles_map_.reset(); - css_property_whitelist_.reset(); + css_property_filter_.reset(); paint_order_map_.reset(); return Response::OK(); } @@ -296,13 +296,13 @@ documents_ = protocol::Array<protocol::DOMSnapshot::DocumentSnapshot>::create(); - css_property_whitelist_ = std::make_unique<CSSPropertyWhitelist>(); + css_property_filter_ = std::make_unique<CSSPropertyFilter>(); // Look up the CSSPropertyIDs for each entry in |computed_styles|. for (size_t i = 0; i < computed_styles->length(); i++) { CSSPropertyID property_id = cssPropertyID(computed_styles->get(i)); if (property_id == CSSPropertyID::kInvalid) continue; - css_property_whitelist_->push_back( + css_property_filter_->push_back( std::make_pair(computed_styles->get(i), property_id)); } @@ -318,7 +318,7 @@ // Extract results from state and reset. *documents = std::move(documents_); *strings = std::move(strings_); - css_property_whitelist_.reset(); + css_property_filter_.reset(); string_table_.clear(); document_order_map_.clear(); documents_.reset(); @@ -928,7 +928,7 @@ Vector<String> style; bool all_properties_empty = true; - for (const auto& pair : *css_property_whitelist_) { + for (const auto& pair : *css_property_filter_) { String value = computed_style_info->GetPropertyValue(pair.second); if (!value.IsEmpty()) all_properties_empty = false; @@ -951,7 +951,7 @@ if (style[i].IsEmpty()) continue; style_properties->addItem(protocol::DOMSnapshot::NameValue::create() - .setName((*css_property_whitelist_)[i].first) + .setName((*css_property_filter_)[i].first) .setValue(style[i]) .build()); } @@ -969,7 +969,7 @@ auto* computed_style_info = MakeGarbageCollected<CSSComputedStyleDeclaration>(node, true); std::unique_ptr<protocol::Array<int>> result = protocol::Array<int>::create(); - for (const auto& pair : *css_property_whitelist_) { + for (const auto& pair : *css_property_filter_) { String value = computed_style_info->GetPropertyValue(pair.second); result->addItem(AddString(value)); }
diff --git a/third_party/blink/renderer/core/inspector/inspector_dom_snapshot_agent.h b/third_party/blink/renderer/core/inspector/inspector_dom_snapshot_agent.h index 5e29bd3..d57733c 100644 --- a/third_party/blink/renderer/core/inspector/inspector_dom_snapshot_agent.h +++ b/third_party/blink/renderer/core/inspector/inspector_dom_snapshot_agent.h
@@ -41,7 +41,7 @@ protocol::Response enable() override; protocol::Response disable() override; protocol::Response getSnapshot( - std::unique_ptr<protocol::Array<String>> style_whitelist, + std::unique_ptr<protocol::Array<String>> style_filter, protocol::Maybe<bool> include_event_listeners, protocol::Maybe<bool> include_paint_order, protocol::Maybe<bool> include_user_agent_shadow_tree, @@ -118,7 +118,7 @@ // Returns the index of the ComputedStyle in |computed_styles_| for the given // Node. Adds a new ComputedStyle if necessary, but ensures no duplicates are // added to |computed_styles_|. Returns -1 if the node has no values for - // styles in |style_whitelist_|. + // styles in |style_filter_|. int GetStyleIndexForNode(Node*); std::unique_ptr<protocol::Array<int>> BuildStylesForNode(Node*); @@ -133,7 +133,7 @@ int, VectorStringHashTraits, VectorStringHashTraits>; - using CSSPropertyWhitelist = Vector<std::pair<String, CSSPropertyID>>; + using CSSPropertyFilter = Vector<std::pair<String, CSSPropertyID>>; using PaintOrderMap = WTF::HashMap<PaintLayer*, int>; using OriginUrlMap = WTF::HashMap<DOMNodeId, String>; @@ -154,8 +154,7 @@ // Maps a style string vector to an index in |computed_styles_|. Used to avoid // duplicate entries in |computed_styles_|. std::unique_ptr<ComputedStylesMap> computed_styles_map_; - std::unique_ptr<Vector<std::pair<String, CSSPropertyID>>> - css_property_whitelist_; + std::unique_ptr<CSSPropertyFilter> css_property_filter_; // Maps a PaintLayer to its paint order index. std::unique_ptr<PaintOrderMap> paint_order_map_; int next_paint_order_index_ = 0;
diff --git a/third_party/blink/renderer/core/loader/document_loader.cc b/third_party/blink/renderer/core/loader/document_loader.cc index bfa42d1b..ed8b5ae8 100644 --- a/third_party/blink/renderer/core/loader/document_loader.cc +++ b/third_party/blink/renderer/core/loader/document_loader.cc
@@ -1576,6 +1576,16 @@ Document* owner_document, GlobalObjectReusePolicy global_object_reuse_policy, const String& source) { + // This is necessary because extensions look at DocumentLoader::url_ when + // deciding whether to inject a content script. In the case where the content + // script can be injected into blank frames, and an iframe is created with + // a javascript url as its src attribute, url_ will be empty here as the + // javascript url was run in the context of the initial empty document. + // However, the document's url will be about:blank, and content scripts + // should be allowed to inject into this document. + if (url_.IsEmpty()) + url_ = BlankURL(); + InstallNewDocument(url, nullptr, owner_document, global_object_reuse_policy, MimeType(), response_.TextEncodingName(), InstallNewDocumentReason::kJavascriptURL,
diff --git a/third_party/blink/renderer/core/loader/frame_load_request.h b/third_party/blink/renderer/core/loader/frame_load_request.h index fdb5216..b8e69efa 100644 --- a/third_party/blink/renderer/core/loader/frame_load_request.h +++ b/third_party/blink/renderer/core/loader/frame_load_request.h
@@ -29,6 +29,7 @@ #include "services/network/public/mojom/request_context_frame_type.mojom-shared.h" #include "third_party/blink/public/mojom/blob/blob_url_store.mojom-blink.h" #include "third_party/blink/public/web/web_triggering_event_info.h" +#include "third_party/blink/public/web/web_window_features.h" #include "third_party/blink/renderer/core/dom/document.h" #include "third_party/blink/renderer/core/frame/frame_types.h" #include "third_party/blink/renderer/core/loader/frame_loader_types.h" @@ -146,6 +147,15 @@ base::TimeTicks GetInputStartTime() const { return input_start_time_; } + const WebWindowFeatures& GetWindowFeatures() const { + return window_features_; + } + void SetFeaturesForWindowOpen(const WebWindowFeatures& features) { + window_features_ = features; + is_window_open_ = true; + } + bool IsWindowOpen() const { return is_window_open_; } + private: Member<Document> origin_document_; ResourceRequest resource_request_; @@ -165,6 +175,8 @@ base::TimeTicks input_start_time_; network::mojom::RequestContextFrameType frame_type_ = network::mojom::RequestContextFrameType::kNone; + WebWindowFeatures window_features_; + bool is_window_open_ = false; }; } // namespace blink
diff --git a/third_party/blink/renderer/core/loader/frame_loader.cc b/third_party/blink/renderer/core/loader/frame_loader.cc index cec7e21..2fe7c306 100644 --- a/third_party/blink/renderer/core/loader/frame_loader.cc +++ b/third_party/blink/renderer/core/loader/frame_loader.cc
@@ -797,22 +797,32 @@ if (!PrepareRequestForThisFrame(request)) return; - Frame* target_frame = frame_->FindFrameForNavigation( - AtomicString(request.FrameName()), *frame_, url); + SetReferrerForFrameRequest(request); - // Downloads and navigations which specifically target a *new* frame - // (e.g. because of a ctrl-click) should ignore the target. - bool should_navigate_target_frame = - request.GetNavigationPolicy() == kNavigationPolicyCurrentTab; + // A GetNavigationPolicy() value other than kNavigationPolicyCurrentTab at + // this point indicates that a user event modified the navigation policy + // (e.g., a ctrl-click). Let the user's action override any target attribute. + if (request.GetNavigationPolicy() == kNavigationPolicyCurrentTab) { + Frame* target_frame = frame_->FindFrameForNavigation( + AtomicString(request.FrameName()), *frame_, url); + if (!target_frame) { + request.SetNavigationPolicy(kNavigationPolicyNewForegroundTab); + bool created = false; + target_frame = CreateNewWindow(*frame_.Get(), request, created); + request.SetNavigationPolicy(kNavigationPolicyCurrentTab); + if (!target_frame) + return; + } - if (target_frame && target_frame != frame_ && should_navigate_target_frame) { - bool was_in_same_page = target_frame->GetPage() == frame_->GetPage(); - request.SetFrameName("_self"); - target_frame->Navigate(request, frame_load_type); - Page* page = target_frame->GetPage(); - if (!was_in_same_page && page) - page->GetChromeClient().Focus(frame_); - return; + if (target_frame != frame_) { + bool was_in_same_page = target_frame->GetPage() == frame_->GetPage(); + request.SetFrameName("_self"); + target_frame->Navigate(request, frame_load_type); + Page* page = target_frame->GetPage(); + if (!was_in_same_page && page) + page->GetChromeClient().Focus(frame_); + return; + } } // Block renderer-initiated loads of data: and filesystem: URLs in the top @@ -837,16 +847,6 @@ return; } - SetReferrerForFrameRequest(request); - - if (!target_frame && !request.FrameName().IsEmpty() && - should_navigate_target_frame) { - request.SetFrameType(network::mojom::RequestContextFrameType::kAuxiliary); - request.SetNavigationPolicy(kNavigationPolicyNewForegroundTab); - CreateWindowForRequest(request, *frame_); - return; // Navigation will be handled by the new frame/window. - } - // TODO(dgozman): merge page dismissal check and FrameNavigationDisabler. if (!frame_->IsNavigationAllowed() || frame_->GetDocument()->PageDismissalEventBeingDispatched() !=
diff --git a/third_party/blink/renderer/core/page/create_window.cc b/third_party/blink/renderer/core/page/create_window.cc index 023516a..dab84de 100644 --- a/third_party/blink/renderer/core/page/create_window.cc +++ b/third_party/blink/renderer/core/page/create_window.cc
@@ -33,19 +33,15 @@ #include "third_party/blink/public/common/dom_storage/session_storage_namespace_id.h" #include "third_party/blink/public/common/features.h" #include "third_party/blink/public/common/frame/from_ad_state.h" -#include "third_party/blink/public/platform/web_input_event.h" -#include "third_party/blink/public/platform/web_url_request.h" #include "third_party/blink/public/web/web_view_client.h" #include "third_party/blink/public/web/web_window_features.h" #include "third_party/blink/renderer/core/core_initializer.h" #include "third_party/blink/renderer/core/dom/document.h" -#include "third_party/blink/renderer/core/events/current_input_event.h" #include "third_party/blink/renderer/core/exported/web_view_impl.h" #include "third_party/blink/renderer/core/frame/ad_tracker.h" #include "third_party/blink/renderer/core/frame/csp/content_security_policy.h" #include "third_party/blink/renderer/core/frame/frame_client.h" #include "third_party/blink/renderer/core/frame/local_frame.h" -#include "third_party/blink/renderer/core/frame/settings.h" #include "third_party/blink/renderer/core/inspector/console_message.h" #include "third_party/blink/renderer/core/loader/frame_load_request.h" #include "third_party/blink/renderer/core/page/chrome_client.h" @@ -53,8 +49,6 @@ #include "third_party/blink/renderer/core/probe/core_probes.h" #include "third_party/blink/renderer/platform/loader/fetch/resource_request.h" #include "third_party/blink/renderer/platform/weborigin/kurl.h" -#include "third_party/blink/renderer/platform/weborigin/security_origin.h" -#include "third_party/blink/renderer/platform/weborigin/security_policy.h" namespace blink { @@ -207,20 +201,43 @@ } } -static Frame* CreateNewWindow(LocalFrame& opener_frame, - const FrameLoadRequest& request, - const WebWindowFeatures& features, - bool& created) { +Frame* CreateNewWindow(LocalFrame& opener_frame, + FrameLoadRequest& request, + bool& created) { DCHECK(request.GetResourceRequest().RequestorOrigin() || opener_frame.GetDocument()->Url().IsEmpty()); - DCHECK_EQ(request.GetFrameType(), - network::mojom::RequestContextFrameType::kAuxiliary); + + // Exempting window.open() from this check here is necessary to support a + // special policy that will be removed in Chrome 82. + // See https://crbug.com/937569 + if (!request.IsWindowOpen() && + opener_frame.GetDocument()->PageDismissalEventBeingDispatched() != + Document::kNoDismissal) { + return nullptr; + } + + request.SetFrameType(network::mojom::RequestContextFrameType::kAuxiliary); const KURL& url = request.GetResourceRequest().Url(); + if (url.ProtocolIsJavaScript() && + opener_frame.GetDocument()->GetContentSecurityPolicy() && + !ContentSecurityPolicy::ShouldBypassMainWorld( + opener_frame.GetDocument())) { + String script_source = DecodeURLEscapeSequences( + url.GetString(), DecodeURLMode::kUTF8OrIsomorphic); + + if (!opener_frame.GetDocument()->GetContentSecurityPolicy()->AllowInline( + ContentSecurityPolicy::InlineType::kNavigation, + nullptr /* element */, script_source, String() /* nonce */, + opener_frame.GetDocument()->Url(), OrdinalNumber())) { + return nullptr; + } + } + + const WebWindowFeatures& features = request.GetWindowFeatures(); probe::WindowOpen(opener_frame.GetDocument(), url, request.FrameName(), features, LocalFrame::HasTransientUserActivation(&opener_frame)); - created = false; // Sandboxed frames cannot open new auxiliary browsing contexts. if (opener_frame.GetDocument()->IsSandboxed(WebSandboxFlags::kPopups)) { @@ -265,6 +282,16 @@ if (!page) return nullptr; + auto* new_local_frame = DynamicTo<LocalFrame>(page->MainFrame()); + if (request.GetShouldSendReferrer() == kMaybeSendReferrer) { + // TODO(japhet): Does network::mojom::ReferrerPolicy need to be proagated + // for RemoteFrames? + if (new_local_frame) { + new_local_frame->GetDocument()->SetReferrerPolicy( + opener_frame.GetDocument()->GetReferrerPolicy()); + } + } + if (page == old_page) { Frame* frame = &opener_frame.Tree().Top(); if (!opener_frame.CanNavigate(*frame)) @@ -299,122 +326,4 @@ return &frame; } -DOMWindow* CreateWindow(const KURL& completed_url, - const AtomicString& frame_name, - const WebWindowFeatures& window_features, - LocalDOMWindow& incumbent_window, - LocalFrame& opener_frame) { - LocalFrame* active_frame = incumbent_window.GetFrame(); - DCHECK(active_frame); - - if (completed_url.ProtocolIsJavaScript() && - opener_frame.GetDocument()->GetContentSecurityPolicy() && - !ContentSecurityPolicy::ShouldBypassMainWorld( - opener_frame.GetDocument())) { - String script_source = DecodeURLEscapeSequences( - completed_url.GetString(), DecodeURLMode::kUTF8OrIsomorphic); - - if (!opener_frame.GetDocument()->GetContentSecurityPolicy()->AllowInline( - ContentSecurityPolicy::InlineType::kNavigation, - nullptr /* element */, script_source, String() /* nonce */, - opener_frame.GetDocument()->Url(), OrdinalNumber())) { - return nullptr; - } - } - - FrameLoadRequest frame_request(incumbent_window.document(), - ResourceRequest(completed_url), frame_name); - frame_request.SetNavigationPolicy( - NavigationPolicyForCreateWindow(window_features)); - frame_request.SetShouldSetOpener(window_features.noopener ? kNeverSetOpener - : kMaybeSetOpener); - frame_request.SetFrameType( - network::mojom::RequestContextFrameType::kAuxiliary); - - // Normally, FrameLoader would take care of setting the referrer for a - // navigation that is triggered from javascript. However, creating a window - // goes through sufficient processing that it eventually enters FrameLoader as - // an embedder-initiated navigation. FrameLoader assumes no responsibility - // for generating an embedder-initiated navigation's referrer, so we need to - // ensure the proper referrer is set now. - // TODO(domfarolino): Stop setting ResourceRequest's HTTP Referrer and store - // this is a separate member. See https://crbug.com/850813. - frame_request.GetResourceRequest().SetHttpReferrer( - SecurityPolicy::GenerateReferrer( - active_frame->GetDocument()->GetReferrerPolicy(), completed_url, - active_frame->GetDocument()->OutgoingReferrer())); - - // Records HasUserGesture before the value is invalidated inside - // createWindow(LocalFrame& openerFrame, ...). - // This value will be set in ResourceRequest loaded in a new LocalFrame. - bool has_user_gesture = LocalFrame::HasTransientUserActivation(&opener_frame); - opener_frame.MaybeLogAdClickNavigation(); - - // We pass the opener frame for the lookupFrame in case the active frame is - // different from the opener frame, and the name references a frame relative - // to the opener frame. - bool created; - Frame* new_frame = - CreateNewWindow(opener_frame, frame_request, window_features, created); - if (!new_frame) - return nullptr; - if (new_frame->DomWindow()->IsInsecureScriptAccess(incumbent_window, - completed_url)) - return window_features.noopener ? nullptr : new_frame->DomWindow(); - - if (created || !completed_url.IsEmpty()) { - FrameLoadRequest request(incumbent_window.document(), - ResourceRequest(completed_url)); - request.GetResourceRequest().SetHasUserGesture(has_user_gesture); - if (const WebInputEvent* input_event = CurrentInputEvent::Get()) { - request.SetInputStartTime(input_event->TimeStamp()); - } - new_frame->Navigate(request, WebFrameLoadType::kStandard); - } - return window_features.noopener ? nullptr : new_frame->DomWindow(); -} - -void CreateWindowForRequest(const FrameLoadRequest& request, - LocalFrame& opener_frame) { - DCHECK(request.GetResourceRequest().RequestorOrigin() || - (opener_frame.GetDocument() && - opener_frame.GetDocument()->Url().IsEmpty())); - - if (opener_frame.GetDocument()->PageDismissalEventBeingDispatched() != - Document::kNoDismissal) - return; - - if (opener_frame.GetDocument() && - opener_frame.GetDocument()->IsSandboxed(WebSandboxFlags::kPopups)) - return; - - WebWindowFeatures features; - features.noopener = request.GetShouldSetOpener() == kNeverSetOpener; - bool created; - Frame* new_frame = CreateNewWindow(opener_frame, request, features, created); - if (!new_frame) - return; - auto* new_local_frame = DynamicTo<LocalFrame>(new_frame); - if (request.GetShouldSendReferrer() == kMaybeSendReferrer) { - // TODO(japhet): Does network::mojom::ReferrerPolicy need to be proagated - // for RemoteFrames? - if (new_local_frame) { - new_local_frame->GetDocument()->SetReferrerPolicy( - opener_frame.GetDocument()->GetReferrerPolicy()); - } - } - - // TODO(japhet): Form submissions on RemoteFrames don't work yet. - FrameLoadRequest new_request(nullptr, request.GetResourceRequest()); - new_request.SetForm(request.Form()); - if (const WebInputEvent* input_event = CurrentInputEvent::Get()) { - new_request.SetInputStartTime(input_event->TimeStamp()); - } - auto blob_url_token = request.GetBlobURLToken(); - if (blob_url_token) - new_request.SetBlobURLToken(std::move(blob_url_token)); - if (new_local_frame) - new_local_frame->Loader().StartNavigation(new_request); -} - } // namespace blink
diff --git a/third_party/blink/renderer/core/page/create_window.h b/third_party/blink/renderer/core/page/create_window.h index 11e635ae4..d11a26b 100644 --- a/third_party/blink/renderer/core/page/create_window.h +++ b/third_party/blink/renderer/core/page/create_window.h
@@ -28,23 +28,17 @@ #define THIRD_PARTY_BLINK_RENDERER_CORE_PAGE_CREATE_WINDOW_H_ #include "third_party/blink/public/web/web_window_features.h" -#include "third_party/blink/renderer/core/frame/local_dom_window.h" +#include "third_party/blink/renderer/core/core_export.h" #include "third_party/blink/renderer/platform/wtf/text/wtf_string.h" -// To avoid conflicts with the CreateWindow macro from the Windows SDK... -#undef CreateWindow - namespace blink { +class Frame; class LocalFrame; struct FrameLoadRequest; -DOMWindow* CreateWindow(const KURL& completed_url, - const AtomicString& frame_name, - const WebWindowFeatures&, - LocalDOMWindow& incumbent_window, - LocalFrame& opener_frame); - -void CreateWindowForRequest(const FrameLoadRequest&, LocalFrame& opener_frame); +Frame* CreateNewWindow(LocalFrame& opener_frame, + FrameLoadRequest&, + bool& created); CORE_EXPORT WebWindowFeatures GetWindowFeaturesFromString(const String&);
diff --git a/third_party/blink/renderer/core/page/scrolling/text_fragment_finder.cc b/third_party/blink/renderer/core/page/scrolling/text_fragment_finder.cc index a6bf09d..5f5e30e 100644 --- a/third_party/blink/renderer/core/page/scrolling/text_fragment_finder.cc +++ b/third_party/blink/renderer/core/page/scrolling/text_fragment_finder.cc
@@ -59,4 +59,12 @@ } } +PositionInFlatTree TextFragmentFinder::FindStart() { + return PositionInFlatTree(); +} + +PositionInFlatTree TextFragmentFinder::FindEnd() { + return PositionInFlatTree(); +} + } // namespace blink
diff --git a/third_party/blink/renderer/core/page/scrolling/text_fragment_finder.h b/third_party/blink/renderer/core/page/scrolling/text_fragment_finder.h index 34db3f7b..207dd4fc 100644 --- a/third_party/blink/renderer/core/page/scrolling/text_fragment_finder.h +++ b/third_party/blink/renderer/core/page/scrolling/text_fragment_finder.h
@@ -35,7 +35,8 @@ Client& client_; const TextFragmentSelector selector_; - String search_text_; + PositionInFlatTree FindStart(); + PositionInFlatTree FindEnd(); }; } // namespace blink
diff --git a/third_party/blink/renderer/core/style/computed_style.h b/third_party/blink/renderer/core/style/computed_style.h index 7580d12..fad2b51 100644 --- a/third_party/blink/renderer/core/style/computed_style.h +++ b/third_party/blink/renderer/core/style/computed_style.h
@@ -1993,7 +1993,8 @@ bool override_existing_colors); void ClearAppliedTextDecorations(); void RestoreParentTextDecorations(const ComputedStyle& parent_style); - const Vector<AppliedTextDecoration>& AppliedTextDecorations() const; + CORE_EXPORT const Vector<AppliedTextDecoration>& AppliedTextDecorations() + const; TextDecoration TextDecorationsInEffect() const; // Overflow utility functions.
diff --git a/third_party/blink/renderer/modules/accessibility/ax_layout_object.cc b/third_party/blink/renderer/modules/accessibility/ax_layout_object.cc index ef2b7527..5d6c628 100644 --- a/third_party/blink/renderer/modules/accessibility/ax_layout_object.cc +++ b/third_party/blink/renderer/modules/accessibility/ax_layout_object.cc
@@ -1099,25 +1099,72 @@ return static_cast<unsigned>(1 << static_cast<int>(text_style_enum)); } -int32_t AXLayoutObject::GetTextStyle() const { - if (!GetLayoutObject()) - return AXNodeObject::GetTextStyle(); - +void AXLayoutObject::GetTextStyleAndTextDecorationStyle( + int32_t* text_style, + ax::mojom::TextDecorationStyle* text_overline_style, + ax::mojom::TextDecorationStyle* text_strikethrough_style, + ax::mojom::TextDecorationStyle* text_underline_style) const { + if (!GetLayoutObject()) { + AXNodeObject::GetTextStyleAndTextDecorationStyle( + text_style, text_overline_style, text_strikethrough_style, + text_underline_style); + return; + } const ComputedStyle* style = GetLayoutObject()->Style(); - if (!style) - return AXNodeObject::GetTextStyle(); + if (!style) { + AXNodeObject::GetTextStyleAndTextDecorationStyle( + text_style, text_overline_style, text_strikethrough_style, + text_underline_style); + return; + } - int32_t text_style = 0; + *text_style = 0; + *text_overline_style = ax::mojom::TextDecorationStyle::kNone; + *text_strikethrough_style = ax::mojom::TextDecorationStyle::kNone; + *text_underline_style = ax::mojom::TextDecorationStyle::kNone; + if (style->GetFontWeight() == BoldWeightValue()) - text_style |= TextStyleFlag(ax::mojom::TextStyle::kBold); + *text_style |= TextStyleFlag(ax::mojom::TextStyle::kBold); if (style->GetFontDescription().Style() == ItalicSlopeValue()) - text_style |= TextStyleFlag(ax::mojom::TextStyle::kItalic); - if (style->GetTextDecoration() == TextDecoration::kUnderline) - text_style |= TextStyleFlag(ax::mojom::TextStyle::kUnderline); - if (style->GetTextDecoration() == TextDecoration::kLineThrough) - text_style |= TextStyleFlag(ax::mojom::TextStyle::kLineThrough); + *text_style |= TextStyleFlag(ax::mojom::TextStyle::kItalic); - return text_style; + for (const auto& decoration : style->AppliedTextDecorations()) { + if (EnumHasFlags(decoration.Lines(), TextDecoration::kOverline)) { + *text_style |= TextStyleFlag(ax::mojom::TextStyle::kOverline); + *text_overline_style = + TextDecorationStyleToAXTextDecorationStyle(decoration.Style()); + } + if (EnumHasFlags(decoration.Lines(), TextDecoration::kLineThrough)) { + *text_style |= TextStyleFlag(ax::mojom::TextStyle::kLineThrough); + *text_strikethrough_style = + TextDecorationStyleToAXTextDecorationStyle(decoration.Style()); + } + if (EnumHasFlags(decoration.Lines(), TextDecoration::kUnderline)) { + *text_style |= TextStyleFlag(ax::mojom::TextStyle::kUnderline); + *text_underline_style = + TextDecorationStyleToAXTextDecorationStyle(decoration.Style()); + } + } +} + +ax::mojom::TextDecorationStyle +AXLayoutObject::TextDecorationStyleToAXTextDecorationStyle( + const blink::ETextDecorationStyle text_decoration_style) { + switch (text_decoration_style) { + case ETextDecorationStyle::kDashed: + return ax::mojom::TextDecorationStyle::kDashed; + case ETextDecorationStyle::kSolid: + return ax::mojom::TextDecorationStyle::kSolid; + case ETextDecorationStyle::kDotted: + return ax::mojom::TextDecorationStyle::kDotted; + case ETextDecorationStyle::kDouble: + return ax::mojom::TextDecorationStyle::kDouble; + case ETextDecorationStyle::kWavy: + return ax::mojom::TextDecorationStyle::kWavy; + } + + NOTREACHED(); + return ax::mojom::TextDecorationStyle::kNone; } //
diff --git a/third_party/blink/renderer/modules/accessibility/ax_layout_object.h b/third_party/blink/renderer/modules/accessibility/ax_layout_object.h index d3d05a9..1dd5049 100644 --- a/third_party/blink/renderer/modules/accessibility/ax_layout_object.h +++ b/third_party/blink/renderer/modules/accessibility/ax_layout_object.h
@@ -109,7 +109,11 @@ ax::mojom::TextDirection GetTextDirection() const final; ax::mojom::TextPosition GetTextPosition() const final; int TextLength() const override; - int32_t GetTextStyle() const final; + void GetTextStyleAndTextDecorationStyle( + int32_t* text_style, + ax::mojom::TextDecorationStyle* text_overline_style, + ax::mojom::TextDecorationStyle* text_strikethrough_style, + ax::mojom::TextDecorationStyle* text_underline_style) const final; // Inline text boxes. void LoadInlineTextBoxes() override; @@ -240,6 +244,10 @@ bool HasAriaCellRole(Element*) const; bool IsPlaceholder() const; + static ax::mojom::TextDecorationStyle + TextDecorationStyleToAXTextDecorationStyle( + const ETextDecorationStyle text_decoration_style); + bool is_autofill_available_; DISALLOW_COPY_AND_ASSIGN(AXLayoutObject);
diff --git a/third_party/blink/renderer/modules/accessibility/ax_object.h b/third_party/blink/renderer/modules/accessibility/ax_object.h index 3500a9db..c9e07e16 100644 --- a/third_party/blink/renderer/modules/accessibility/ax_object.h +++ b/third_party/blink/renderer/modules/accessibility/ax_object.h
@@ -695,8 +695,16 @@ } virtual int TextLength() const { return 0; } - // Bitmask from ax::mojom::TextStyle. - virtual int32_t GetTextStyle() const { return 0; } + virtual void GetTextStyleAndTextDecorationStyle( + int32_t* text_style, + ax::mojom::TextDecorationStyle* text_overline_style, + ax::mojom::TextDecorationStyle* text_strikethrough_style, + ax::mojom::TextDecorationStyle* text_underline_style) const { + *text_style = 0; + *text_overline_style = ax::mojom::TextDecorationStyle::kNone; + *text_strikethrough_style = ax::mojom::TextDecorationStyle::kNone; + *text_underline_style = ax::mojom::TextDecorationStyle::kNone; + } virtual AXObjectVector RadioButtonsInGroup() const { return AXObjectVector();
diff --git a/third_party/blink/renderer/modules/csspaint/paint_worklet.cc b/third_party/blink/renderer/modules/csspaint/paint_worklet.cc index aba0caa..08b668f 100644 --- a/third_party/blink/renderer/modules/csspaint/paint_worklet.cc +++ b/third_party/blink/renderer/modules/csspaint/paint_worklet.cc
@@ -16,7 +16,6 @@ #include "third_party/blink/renderer/modules/csspaint/css_paint_definition.h" #include "third_party/blink/renderer/modules/csspaint/paint_worklet_global_scope.h" #include "third_party/blink/renderer/modules/csspaint/paint_worklet_messaging_proxy.h" -#include "third_party/blink/renderer/modules/csspaint/paint_worklet_proxy_client.h" #include "third_party/blink/renderer/platform/graphics/paint_generated_image.h" namespace blink { @@ -142,6 +141,7 @@ void PaintWorklet::Trace(blink::Visitor* visitor) { visitor->Trace(pending_generator_registry_); visitor->Trace(document_definition_map_); + visitor->Trace(proxy_client_); Worklet::Trace(visitor); Supplement<LocalDOMWindow>::Trace(visitor); } @@ -158,10 +158,13 @@ pending_generator_registry_, GetNumberOfGlobalScopes() + 1); } - PaintWorkletProxyClient* proxy_client = PaintWorkletProxyClient::Create( - To<Document>(GetExecutionContext()), worklet_id_); + if (!proxy_client_) { + proxy_client_ = PaintWorkletProxyClient::Create( + To<Document>(GetExecutionContext()), worklet_id_); + } + auto* worker_clients = MakeGarbageCollected<WorkerClients>(); - ProvidePaintWorkletProxyClientTo(worker_clients, proxy_client); + ProvidePaintWorkletProxyClientTo(worker_clients, proxy_client_); PaintWorkletMessagingProxy* proxy = MakeGarbageCollected<PaintWorkletMessagingProxy>(GetExecutionContext());
diff --git a/third_party/blink/renderer/modules/csspaint/paint_worklet.h b/third_party/blink/renderer/modules/csspaint/paint_worklet.h index 6fd1431..df9201c 100644 --- a/third_party/blink/renderer/modules/csspaint/paint_worklet.h +++ b/third_party/blink/renderer/modules/csspaint/paint_worklet.h
@@ -10,6 +10,7 @@ #include "third_party/blink/renderer/modules/csspaint/document_paint_definition.h" #include "third_party/blink/renderer/modules/csspaint/paint_worklet_global_scope_proxy.h" #include "third_party/blink/renderer/modules/csspaint/paint_worklet_pending_generator_registry.h" +#include "third_party/blink/renderer/modules/csspaint/paint_worklet_proxy_client.h" #include "third_party/blink/renderer/modules/modules_export.h" #include "third_party/blink/renderer/platform/heap/handle.h" @@ -87,6 +88,10 @@ // paint the image. int worklet_id_; + // The proxy client associated with this PaintWorklet. We keep a reference in + // to ensure that all global scopes get the same proxy client. + Member<PaintWorkletProxyClient> proxy_client_; + DISALLOW_COPY_AND_ASSIGN(PaintWorklet); };
diff --git a/third_party/blink/renderer/modules/csspaint/paint_worklet_global_scope.cc b/third_party/blink/renderer/modules/csspaint/paint_worklet_global_scope.cc index ec592cc..8f872e7c 100644 --- a/third_party/blink/renderer/modules/csspaint/paint_worklet_global_scope.cc +++ b/third_party/blink/renderer/modules/csspaint/paint_worklet_global_scope.cc
@@ -280,7 +280,7 @@ if (PaintWorkletProxyClient* proxy_client = PaintWorkletProxyClient::From(Clients())) { - proxy_client->SetGlobalScope(this); + proxy_client->AddGlobalScope(this); registered_ = true; } }
diff --git a/third_party/blink/renderer/modules/csspaint/paint_worklet_global_scope_test.cc b/third_party/blink/renderer/modules/csspaint/paint_worklet_global_scope_test.cc index f6be94d..c7e7b380 100644 --- a/third_party/blink/renderer/modules/csspaint/paint_worklet_global_scope_test.cc +++ b/third_party/blink/renderer/modules/csspaint/paint_worklet_global_scope_test.cc
@@ -70,11 +70,8 @@ PaintWorkletProxyClient* proxy_client, base::WaitableEvent* waitable_event) { ASSERT_TRUE(thread->IsCurrentThread()); - proxy_client->SetGlobalScopeForTesting( - static_cast<PaintWorkletGlobalScope*>( - To<WorkletGlobalScope>(thread->GlobalScope()))); CrossThreadPersistent<PaintWorkletGlobalScope> global_scope = - proxy_client->global_scope_; + To<PaintWorkletGlobalScope>(thread->GlobalScope()); ScriptState* script_state = global_scope->ScriptController()->GetScriptState(); ASSERT_TRUE(script_state); @@ -84,8 +81,7 @@ ScriptState::Scope scope(script_state); { - // registerPaint() with a valid class definition should define an - // animator. + // registerPaint() with a valid class definition should define a painter. String source_code = R"JS( registerPaint('test', class { @@ -101,8 +97,8 @@ } { - // registerPaint() with a null class definition should fail to define - // an painter. + // registerPaint() with a null class definition should fail to define a + // painter. String source_code = "registerPaint('null', null);"; ASSERT_FALSE(global_scope->ScriptController()->Evaluate( ScriptSourceCode(source_code), SanitizeScriptErrors::kDoNotSanitize));
diff --git a/third_party/blink/renderer/modules/csspaint/paint_worklet_proxy_client.cc b/third_party/blink/renderer/modules/csspaint/paint_worklet_proxy_client.cc index 17e16d9..915f8cb 100644 --- a/third_party/blink/renderer/modules/csspaint/paint_worklet_proxy_client.cc +++ b/third_party/blink/renderer/modules/csspaint/paint_worklet_proxy_client.cc
@@ -11,6 +11,7 @@ #include "third_party/blink/renderer/core/frame/web_local_frame_impl.h" #include "third_party/blink/renderer/core/workers/worker_thread.h" #include "third_party/blink/renderer/modules/csspaint/css_paint_definition.h" +#include "third_party/blink/renderer/modules/csspaint/paint_worklet.h" #include "third_party/blink/renderer/platform/cross_thread_functional.h" #include "third_party/blink/renderer/platform/graphics/image.h" #include "third_party/blink/renderer/platform/graphics/paint_worklet_paint_dispatcher.h" @@ -46,23 +47,25 @@ PaintWorkletPainter::Trace(visitor); } -void PaintWorkletProxyClient::SetGlobalScopeForTesting( - PaintWorkletGlobalScope* global_scope) { - DCHECK(global_scope); - DCHECK(global_scope->IsContextThread()); - global_scope_ = global_scope; -} - -void PaintWorkletProxyClient::SetGlobalScope(WorkletGlobalScope* global_scope) { +void PaintWorkletProxyClient::AddGlobalScope(WorkletGlobalScope* global_scope) { DCHECK(global_scope); DCHECK(global_scope->IsContextThread()); if (state_ == RunState::kDisposed) return; DCHECK(state_ == RunState::kUninitialized); - global_scope_ = static_cast<PaintWorkletGlobalScope*>(global_scope); + global_scopes_.push_back(To<PaintWorkletGlobalScope>(global_scope)); + + // Wait for all global scopes to be set before registering. + if (global_scopes_.size() < PaintWorklet::kNumGlobalScopes) { + return; + } + + // All the global scopes that share a single PaintWorkletProxyClient are + // running on the same thread, and so we can just grab the task runner from + // the last one to call this function and use that. scoped_refptr<base::SingleThreadTaskRunner> global_scope_runner = - global_scope_->GetThread()->GetTaskRunner(TaskType::kMiscPlatformAPI); + global_scope->GetThread()->GetTaskRunner(TaskType::kMiscPlatformAPI); state_ = RunState::kWorking; compositor_paintee_->RegisterPaintWorkletPainter(this, global_scope_runner); @@ -71,28 +74,38 @@ void PaintWorkletProxyClient::Dispose() { if (state_ == RunState::kWorking) { compositor_paintee_->UnregisterPaintWorkletPainter(this); - - DCHECK(global_scope_); - DCHECK(global_scope_->IsContextThread()); - - // At worklet scope termination break the reference cycle between - // PaintWorkletGlobalScope and PaintWorkletProxyClient. - global_scope_ = nullptr; } - compositor_paintee_ = nullptr; - DCHECK(state_ != RunState::kDisposed); state_ = RunState::kDisposed; + + // At worklet scope termination break the reference cycle between + // PaintWorkletGlobalScope and PaintWorkletProxyClient. + global_scopes_.clear(); } sk_sp<PaintRecord> PaintWorkletProxyClient::Paint( CompositorPaintWorkletInput* compositor_input) { - if (!global_scope_) + // TODO: Can this happen? We don't register till all are here. + if (global_scopes_.IsEmpty()) return sk_make_sp<PaintRecord>(); + + // PaintWorklets are stateless by spec. There are two ways script might try to + // inject state: + // * From one PaintWorklet to another, in the same frame. + // * Inside the same PaintWorklet, across frames. + // + // To discourage both of these, we randomize selection of the global scope. + // TODO(smcgruer): Once we are passing bundles of PaintWorklets here, we + // should shuffle the bundle randomly and then assign half to the first global + // scope, and half to the rest. + DCHECK_EQ(global_scopes_.size(), PaintWorklet::kNumGlobalScopes); + PaintWorkletGlobalScope* global_scope = + global_scopes_[base::RandInt(0, PaintWorklet::kNumGlobalScopes - 1)]; + PaintWorkletInput* input = static_cast<PaintWorkletInput*>(compositor_input); CSSPaintDefinition* definition = - global_scope_->FindDefinition(input->NameCopy()); + global_scope->FindDefinition(input->NameCopy()); return definition->Paint(FloatSize(input->GetSize()), input->EffectiveZoom(), nullptr, nullptr);
diff --git a/third_party/blink/renderer/modules/csspaint/paint_worklet_proxy_client.h b/third_party/blink/renderer/modules/csspaint/paint_worklet_proxy_client.h index 94e604da..d7d5f311 100644 --- a/third_party/blink/renderer/modules/csspaint/paint_worklet_proxy_client.h +++ b/third_party/blink/renderer/modules/csspaint/paint_worklet_proxy_client.h
@@ -24,8 +24,6 @@ // // This is constructed on the main thread but it is used in the worklet backing // thread. -// -// TODO(smcgruer): Add the dispatcher logic. class MODULES_EXPORT PaintWorkletProxyClient : public GarbageCollectedFinalized<PaintWorkletProxyClient>, public Supplement<WorkerClients>, @@ -49,8 +47,11 @@ int GetWorkletId() const override { return worklet_id_; } sk_sp<PaintRecord> Paint(CompositorPaintWorkletInput*) override; - virtual void SetGlobalScope(WorkletGlobalScope*); - void SetGlobalScopeForTesting(PaintWorkletGlobalScope*); + virtual void AddGlobalScope(WorkletGlobalScope*); + const Vector<CrossThreadPersistent<PaintWorkletGlobalScope>>& + GetGlobalScopesForTesting() const { + return global_scopes_; + } void Dispose(); static PaintWorkletProxyClient* From(WorkerClients*); @@ -60,11 +61,10 @@ friend class PaintWorkletProxyClientTest; FRIEND_TEST_ALL_PREFIXES(PaintWorkletProxyClientTest, PaintWorkletProxyClientConstruction); - FRIEND_TEST_ALL_PREFIXES(PaintWorkletProxyClientTest, SetGlobalScope); scoped_refptr<PaintWorkletPaintDispatcher> compositor_paintee_; const int worklet_id_; - CrossThreadPersistent<PaintWorkletGlobalScope> global_scope_; + Vector<CrossThreadPersistent<PaintWorkletGlobalScope>> global_scopes_; enum RunState { kUninitialized, kWorking, kDisposed } state_; };
diff --git a/third_party/blink/renderer/modules/csspaint/paint_worklet_proxy_client_test.cc b/third_party/blink/renderer/modules/csspaint/paint_worklet_proxy_client_test.cc index be13087c..aaa9ea8 100644 --- a/third_party/blink/renderer/modules/csspaint/paint_worklet_proxy_client_test.cc +++ b/third_party/blink/renderer/modules/csspaint/paint_worklet_proxy_client_test.cc
@@ -15,6 +15,7 @@ #include "third_party/blink/renderer/core/css/cssom/paint_worklet_input.h" #include "third_party/blink/renderer/core/testing/core_unit_test_helper.h" #include "third_party/blink/renderer/core/workers/worker_reporting_proxy.h" +#include "third_party/blink/renderer/modules/csspaint/paint_worklet.h" #include "third_party/blink/renderer/modules/csspaint/paint_worklet_global_scope.h" #include "third_party/blink/renderer/modules/worklet/worklet_thread_test_common.h" #include "third_party/blink/renderer/platform/graphics/paint_worklet_paint_dispatcher.h" @@ -34,22 +35,29 @@ reporting_proxy_ = std::make_unique<WorkerReportingProxy>(); } - void SetAndCheckGlobalScope(WorkerThread* thread, + void RunAddGlobalScopesTest(WorkerThread* thread, PaintWorkletProxyClient* proxy_client, base::WaitableEvent* waitable_event) { - proxy_client->SetGlobalScope(To<WorkletGlobalScope>(thread->GlobalScope())); - EXPECT_EQ(proxy_client->global_scope_, - To<WorkletGlobalScope>(thread->GlobalScope())); - EXPECT_EQ(dispatcher_->painter_map_.size(), 1u); - waitable_event->Signal(); - } + // For this test, we cheat and reuse the same global scope object from a + // single WorkerThread. In real code these would be different global scopes. - void SetGlobalScopeForTesting(WorkerThread* thread, - PaintWorkletProxyClient* proxy_client, - base::WaitableEvent* waitable_event) { - proxy_client->SetGlobalScopeForTesting( - static_cast<PaintWorkletGlobalScope*>( - To<WorkletGlobalScope>(thread->GlobalScope()))); + // First, add all but one of the global scopes. The proxy client should not + // yet register itself. + for (size_t i = 0; i < PaintWorklet::kNumGlobalScopes - 1; i++) { + proxy_client->AddGlobalScope( + To<WorkletGlobalScope>(thread->GlobalScope())); + } + + EXPECT_EQ(proxy_client->global_scopes_.size(), + PaintWorklet::kNumGlobalScopes - 1); + EXPECT_EQ(dispatcher_->painter_map_.size(), 0u); + + // Now add the final global scope. This should trigger the registration. + proxy_client->AddGlobalScope(To<WorkletGlobalScope>(thread->GlobalScope())); + EXPECT_EQ(proxy_client->global_scopes_.size(), + PaintWorklet::kNumGlobalScopes); + EXPECT_EQ(dispatcher_->painter_map_.size(), 1u); + waitable_event->Signal(); } @@ -57,40 +65,47 @@ void (PaintWorkletProxyClientTest::*)(WorkerThread*, PaintWorkletProxyClient*, base::WaitableEvent*); - void RunTestOnWorkletThread(TestCallback callback) { - std::unique_ptr<WorkerThread> worklet = - CreateThreadAndProvidePaintWorkletProxyClient( - &GetDocument(), reporting_proxy_.get(), proxy_client_); + void RunMultipleGlobalScopeTestsOnWorklet(TestCallback callback) { + // PaintWorklet is stateless, and this is enforced via having multiple + // global scopes (which are switched between). To mimic the real world, + // create multiple WorkerThread for this. Note that the underlying thread + // may be shared even though they are unique WorkerThread instances! + Vector<std::unique_ptr<WorkerThread>> worklet_threads; + for (size_t i = 0; i < PaintWorklet::kNumGlobalScopes; i++) { + worklet_threads.push_back(CreateThreadAndProvidePaintWorkletProxyClient( + &GetDocument(), reporting_proxy_.get(), proxy_client_)); + } + + // Now let the test actually run. We only run the test on the first worklet + // thread currently; this suffices since they share the proxy. base::WaitableEvent waitable_event; PostCrossThreadTask( - *worklet->GetTaskRunner(TaskType::kInternalTest), FROM_HERE, + *worklet_threads[0]->GetTaskRunner(TaskType::kInternalTest), FROM_HERE, CrossThreadBind( callback, CrossThreadUnretained(this), - CrossThreadUnretained(worklet.get()), + CrossThreadUnretained(worklet_threads[0].get()), CrossThreadPersistent<PaintWorkletProxyClient>(proxy_client_), CrossThreadUnretained(&waitable_event))); waitable_event.Wait(); waitable_event.Reset(); - worklet->Terminate(); - worklet->WaitForShutdownForTesting(); + // And finally clean up. + for (size_t i = 0; i < PaintWorklet::kNumGlobalScopes; i++) { + worklet_threads[i]->Terminate(); + worklet_threads[i]->WaitForShutdownForTesting(); + } } void RunPaintOnWorklet(WorkerThread* thread, PaintWorkletProxyClient* proxy_client, base::WaitableEvent* waitable_event) { - // The "registerPaint" script calls the real SetGlobalScope, so at this - // moment all we need is setting the |global_scope_| without any other - // things. - proxy_client->SetGlobalScopeForTesting( - static_cast<PaintWorkletGlobalScope*>( - To<WorkletGlobalScope>(thread->GlobalScope()))); - CrossThreadPersistent<PaintWorkletGlobalScope> global_scope = - proxy_client->global_scope_; - global_scope->ScriptController()->Evaluate( - ScriptSourceCode("registerPaint('foo', class { paint() { } });"), - SanitizeScriptErrors::kDoNotSanitize); + // Make sure to register the painter on all global scopes. + for (const auto& global_scope : proxy_client->GetGlobalScopesForTesting()) { + global_scope->ScriptController()->Evaluate( + ScriptSourceCode("registerPaint('foo', class { paint() { } });"), + SanitizeScriptErrors::kDoNotSanitize); + } PaintWorkletStylePropertyMap::CrossThreadData data; scoped_refptr<PaintWorkletInput> input = @@ -122,22 +137,20 @@ EXPECT_NE(proxy_client->compositor_paintee_, nullptr); } -TEST_F(PaintWorkletProxyClientTest, SetGlobalScope) { +TEST_F(PaintWorkletProxyClientTest, AddGlobalScope) { ScopedOffMainThreadCSSPaintForTest off_main_thread_css_paint(true); // Global scopes must be created on worker threads. std::unique_ptr<WorkerThread> worklet_thread = CreateThreadAndProvidePaintWorkletProxyClient( &GetDocument(), reporting_proxy_.get(), proxy_client_); - EXPECT_EQ(proxy_client_->global_scope_, nullptr); + EXPECT_TRUE(proxy_client_->GetGlobalScopesForTesting().IsEmpty()); - // Register global scopes with proxy client. This step must be performed on - // the worker threads. base::WaitableEvent waitable_event; PostCrossThreadTask( *worklet_thread->GetTaskRunner(TaskType::kInternalTest), FROM_HERE, CrossThreadBind( - &PaintWorkletProxyClientTest::SetAndCheckGlobalScope, + &PaintWorkletProxyClientTest::RunAddGlobalScopesTest, CrossThreadUnretained(this), CrossThreadUnretained(worklet_thread.get()), CrossThreadPersistent<PaintWorkletProxyClient>(proxy_client_), @@ -150,7 +163,8 @@ TEST_F(PaintWorkletProxyClientTest, Paint) { ScopedOffMainThreadCSSPaintForTest off_main_thread_css_paint(true); - RunTestOnWorkletThread(&PaintWorkletProxyClientTest::RunPaintOnWorklet); + RunMultipleGlobalScopeTestsOnWorklet( + &PaintWorkletProxyClientTest::RunPaintOnWorklet); } } // namespace blink
diff --git a/third_party/blink/renderer/modules/document_metadata/copyless_paste_extractor.cc b/third_party/blink/renderer/modules/document_metadata/copyless_paste_extractor.cc index aa5e35a..b8483db 100644 --- a/third_party/blink/renderer/modules/document_metadata/copyless_paste_extractor.cc +++ b/third_party/blink/renderer/modules/document_metadata/copyless_paste_extractor.cc
@@ -53,7 +53,7 @@ constexpr char kJSONLDKeyType[] = "@type"; constexpr char kJSONLDKeyGraph[] = "@graph"; -bool isWhitelistedType(AtomicString type) { +bool IsSupportedType(AtomicString type) { DEFINE_STATIC_LOCAL(HashSet<AtomicString>, elements, ({// Common types that include addresses. "AutoDealer", "Hotel", "LocalBusiness", "Organization", @@ -64,9 +64,9 @@ return type && elements.Contains(type); } -void extractEntity(const JSONObject&, Entity&, int recursionLevel); +void ExtractEntity(const JSONObject&, Entity&, int recursionLevel); -bool parseRepeatedValue(const JSONArray& arr, +bool ParseRepeatedValue(const JSONArray& arr, Values& values, int recursionLevel) { if (arr.size() < 1) { @@ -136,7 +136,7 @@ } break; case JSONValue::ValueType::kTypeObject: values.get_entity_values().push_back(Entity::New()); - extractEntity(*(JSONObject::Cast(innerVal)), + ExtractEntity(*(JSONObject::Cast(innerVal)), *(values.get_entity_values().at(j)), recursionLevel + 1); break; default: @@ -146,7 +146,7 @@ return true; } -void extractEntity(const JSONObject& val, Entity& entity, int recursionLevel) { +void ExtractEntity(const JSONObject& val, Entity& entity, int recursionLevel) { if (recursionLevel >= kMaxDepth) { return; } @@ -200,12 +200,12 @@ property->values->set_entity_values(Vector<EntityPtr>()); property->values->get_entity_values().push_back(Entity::New()); - extractEntity(*(val.GetJSONObject(entry.first)), + ExtractEntity(*(val.GetJSONObject(entry.first)), *(property->values->get_entity_values().at(0)), recursionLevel + 1); } break; case JSONValue::ValueType::kTypeArray: - addProperty = parseRepeatedValue(*(val.GetArray(entry.first)), + addProperty = ParseRepeatedValue(*(val.GetArray(entry.first)), *(property->values), recursionLevel); break; default: @@ -216,43 +216,43 @@ } } -void extractTopLevelEntity(const JSONObject& val, Vector<EntityPtr>& entities) { +void ExtractTopLevelEntity(const JSONObject& val, Vector<EntityPtr>& entities) { // Now we have a JSONObject which corresponds to a single (possibly nested) // entity. EntityPtr entity = Entity::New(); String type; val.GetString(kJSONLDKeyType, &type); - if (!isWhitelistedType(AtomicString(type))) { + if (!IsSupportedType(AtomicString(type))) { return; } - extractEntity(val, *entity, 0); + ExtractEntity(val, *entity, 0); entities.push_back(std::move(entity)); } -void extractEntitiesFromArray(const JSONArray& arr, +void ExtractEntitiesFromArray(const JSONArray& arr, Vector<EntityPtr>& entities) { for (wtf_size_t i = 0; i < arr.size(); ++i) { const JSONValue* val = arr.at(i); if (val->GetType() == JSONValue::ValueType::kTypeObject) { - extractTopLevelEntity(*(JSONObject::Cast(val)), entities); + ExtractTopLevelEntity(*(JSONObject::Cast(val)), entities); } } } -void extractEntityFromTopLevelObject(const JSONObject& val, +void ExtractEntityFromTopLevelObject(const JSONObject& val, Vector<EntityPtr>& entities) { const JSONArray* graph = val.GetArray(kJSONLDKeyGraph); if (graph) { - extractEntitiesFromArray(*graph, entities); + ExtractEntitiesFromArray(*graph, entities); } - extractTopLevelEntity(val, entities); + ExtractTopLevelEntity(val, entities); } // ExtractionStatus is used in UMA, hence is append-only. // kCount must be the last entry. enum ExtractionStatus { kOK, kEmpty, kParseFailure, kWrongType, kCount }; -ExtractionStatus extractMetadata(const Element& root, +ExtractionStatus ExtractMetadata(const Element& root, Vector<EntityPtr>& entities) { for (Element& element : ElementTraversal::DescendantsOf(root)) { if (element.HasTagName(html_names::kScriptTag) && @@ -264,10 +264,10 @@ } switch (json->GetType()) { case JSONValue::ValueType::kTypeArray: - extractEntitiesFromArray(*(JSONArray::Cast(json.get())), entities); + ExtractEntitiesFromArray(*(JSONArray::Cast(json.get())), entities); break; case JSONValue::ValueType::kTypeObject: - extractEntityFromTopLevelObject(*(JSONObject::Cast(json.get())), + ExtractEntityFromTopLevelObject(*(JSONObject::Cast(json.get())), entities); break; default: @@ -283,8 +283,8 @@ } // namespace -WebPagePtr CopylessPasteExtractor::extract(const Document& document) { - TRACE_EVENT0("blink", "CopylessPasteExtractor::extract"); +WebPagePtr CopylessPasteExtractor::Extract(const Document& document) { + TRACE_EVENT0("blink", "CopylessPasteExtractor::Extract"); if (!document.GetFrame() || !document.GetFrame()->IsMainFrame()) return nullptr; @@ -297,7 +297,7 @@ // Traverse the DOM tree and extract the metadata. TimeTicks start_time = CurrentTimeTicks(); - ExtractionStatus status = extractMetadata(*html, page->entities); + ExtractionStatus status = ExtractMetadata(*html, page->entities); TimeDelta elapsed_time = CurrentTimeTicks() - start_time; DEFINE_STATIC_LOCAL(EnumerationHistogram, status_histogram, @@ -306,14 +306,14 @@ if (status != kOK) { DEFINE_STATIC_LOCAL( - CustomCountHistogram, extractionHistogram, + CustomCountHistogram, extraction_histogram, ("CopylessPaste.ExtractionFailedUs", 1, 1000 * 1000, 50)); - extractionHistogram.CountMicroseconds(elapsed_time); + extraction_histogram.CountMicroseconds(elapsed_time); return nullptr; } - DEFINE_STATIC_LOCAL(CustomCountHistogram, extractionHistogram, + DEFINE_STATIC_LOCAL(CustomCountHistogram, extraction_histogram, ("CopylessPaste.ExtractionUs", 1, 1000 * 1000, 50)); - extractionHistogram.CountMicroseconds(elapsed_time); + extraction_histogram.CountMicroseconds(elapsed_time); page->url = document.Url(); page->title = document.title();
diff --git a/third_party/blink/renderer/modules/document_metadata/copyless_paste_extractor.h b/third_party/blink/renderer/modules/document_metadata/copyless_paste_extractor.h index dcb8a4e..7adc74a 100644 --- a/third_party/blink/renderer/modules/document_metadata/copyless_paste_extractor.h +++ b/third_party/blink/renderer/modules/document_metadata/copyless_paste_extractor.h
@@ -20,7 +20,7 @@ STATIC_ONLY(CopylessPasteExtractor); public: - static mojom::document_metadata::blink::WebPagePtr extract(const Document&); + static mojom::document_metadata::blink::WebPagePtr Extract(const Document&); }; } // namespace blink
diff --git a/third_party/blink/renderer/modules/document_metadata/copyless_paste_extractor_test.cc b/third_party/blink/renderer/modules/document_metadata/copyless_paste_extractor_test.cc index 20a22753..001f9e9 100644 --- a/third_party/blink/renderer/modules/document_metadata/copyless_paste_extractor_test.cc +++ b/third_party/blink/renderer/modules/document_metadata/copyless_paste_extractor_test.cc
@@ -37,7 +37,7 @@ } WebPagePtr Extract() { - return CopylessPasteExtractor::extract(GetDocument()); + return CopylessPasteExtractor::Extract(GetDocument()); } void SetHTMLInnerHTML(const String&); @@ -46,15 +46,15 @@ void SetTitle(const String&); - PropertyPtr createStringProperty(const String& name, const String& value); + PropertyPtr CreateStringProperty(const String& name, const String& value); - PropertyPtr createBooleanProperty(const String& name, const bool& value); + PropertyPtr CreateBooleanProperty(const String& name, const bool& value); - PropertyPtr createLongProperty(const String& name, const int64_t& value); + PropertyPtr CreateLongProperty(const String& name, const int64_t& value); - PropertyPtr createEntityProperty(const String& name, EntityPtr value); + PropertyPtr CreateEntityProperty(const String& name, EntityPtr value); - WebPagePtr createWebPage(const String& url, const String& title); + WebPagePtr CreateWebPage(const String& url, const String& title); }; void CopylessPasteExtractorTest::SetHTMLInnerHTML(const String& html_content) { @@ -69,7 +69,7 @@ GetDocument().setTitle(title); } -PropertyPtr CopylessPasteExtractorTest::createStringProperty( +PropertyPtr CopylessPasteExtractorTest::CreateStringProperty( const String& name, const String& value) { PropertyPtr property = Property::New(); @@ -79,7 +79,7 @@ return property; } -PropertyPtr CopylessPasteExtractorTest::createBooleanProperty( +PropertyPtr CopylessPasteExtractorTest::CreateBooleanProperty( const String& name, const bool& value) { PropertyPtr property = Property::New(); @@ -89,7 +89,7 @@ return property; } -PropertyPtr CopylessPasteExtractorTest::createLongProperty( +PropertyPtr CopylessPasteExtractorTest::CreateLongProperty( const String& name, const int64_t& value) { PropertyPtr property = Property::New(); @@ -99,7 +99,7 @@ return property; } -PropertyPtr CopylessPasteExtractorTest::createEntityProperty(const String& name, +PropertyPtr CopylessPasteExtractorTest::CreateEntityProperty(const String& name, EntityPtr value) { PropertyPtr property = Property::New(); property->name = name; @@ -109,7 +109,7 @@ return property; } -WebPagePtr CopylessPasteExtractorTest::createWebPage(const String& url, +WebPagePtr CopylessPasteExtractorTest::CreateWebPage(const String& url, const String& title) { WebPagePtr page = WebPage::New(); page->url = blink::KURL(url); @@ -140,12 +140,12 @@ ASSERT_FALSE(extracted.is_null()); WebPagePtr expected = - createWebPage("http://www.test.com/", "My neat website about cool stuff"); + CreateWebPage("http://www.test.com/", "My neat website about cool stuff"); EntityPtr restaurant = Entity::New(); restaurant->type = "Restaurant"; restaurant->properties.push_back( - createStringProperty("name", "Special characters for ya >_<;")); + CreateStringProperty("name", "Special characters for ya >_<;")); expected->entities.push_back(std::move(restaurant)); EXPECT_EQ(expected, extracted); @@ -171,12 +171,12 @@ ASSERT_FALSE(extracted.is_null()); WebPagePtr expected = - createWebPage("http://www.test.com/", "My neat website about cool stuff"); + CreateWebPage("http://www.test.com/", "My neat website about cool stuff"); EntityPtr restaurant = Entity::New(); restaurant->type = "Restaurant"; restaurant->properties.push_back( - createStringProperty("name", "Special characters for ya >_<;")); + CreateStringProperty("name", "Special characters for ya >_<;")); expected->entities.push_back(std::move(restaurant)); EXPECT_EQ(expected, extracted); @@ -201,11 +201,11 @@ ASSERT_FALSE(extracted.is_null()); WebPagePtr expected = - createWebPage("http://www.test.com/", "My neat website about cool stuff"); + CreateWebPage("http://www.test.com/", "My neat website about cool stuff"); EntityPtr restaurant = Entity::New(); restaurant->type = "Restaurant"; - restaurant->properties.push_back(createBooleanProperty("open", true)); + restaurant->properties.push_back(CreateBooleanProperty("open", true)); expected->entities.push_back(std::move(restaurant)); EXPECT_EQ(expected, extracted); @@ -230,11 +230,11 @@ ASSERT_FALSE(extracted.is_null()); WebPagePtr expected = - createWebPage("http://www.test.com/", "My neat website about cool stuff"); + CreateWebPage("http://www.test.com/", "My neat website about cool stuff"); EntityPtr restaurant = Entity::New(); restaurant->type = "Restaurant"; - restaurant->properties.push_back(createLongProperty("long", 1ll)); + restaurant->properties.push_back(CreateLongProperty("long", 1ll)); expected->entities.push_back(std::move(restaurant)); EXPECT_EQ(expected, extracted); @@ -259,11 +259,11 @@ ASSERT_FALSE(extracted.is_null()); WebPagePtr expected = - createWebPage("http://www.test.com/", "My neat website about cool stuff"); + CreateWebPage("http://www.test.com/", "My neat website about cool stuff"); EntityPtr restaurant = Entity::New(); restaurant->type = "Restaurant"; - restaurant->properties.push_back(createStringProperty("double", "1.5")); + restaurant->properties.push_back(CreateStringProperty("double", "1.5")); expected->entities.push_back(std::move(restaurant)); EXPECT_EQ(expected, extracted); @@ -307,13 +307,13 @@ ASSERT_FALSE(extracted.is_null()); WebPagePtr expected = - createWebPage("http://www.test.com/", "My neat website about cool stuff"); + CreateWebPage("http://www.test.com/", "My neat website about cool stuff"); for (int i = 0; i < 3; ++i) { EntityPtr restaurant = Entity::New(); restaurant->type = "Restaurant"; restaurant->properties.push_back( - createStringProperty("name", "Special characters for ya >_<;")); + CreateStringProperty("name", "Special characters for ya >_<;")); expected->entities.push_back(std::move(restaurant)); } @@ -344,22 +344,22 @@ ASSERT_FALSE(extracted.is_null()); WebPagePtr expected = - createWebPage("http://www.test.com/", "My neat website about cool stuff"); + CreateWebPage("http://www.test.com/", "My neat website about cool stuff"); EntityPtr restaurant = Entity::New(); restaurant->type = "Restaurant"; restaurant->properties.push_back( - createStringProperty("name", "Ye ol greasy diner")); + CreateStringProperty("name", "Ye ol greasy diner")); EntityPtr address = Entity::New(); address->type = "Thing"; address->properties.push_back( - createStringProperty("streetAddress", "123 Big Oak Road")); + CreateStringProperty("streetAddress", "123 Big Oak Road")); address->properties.push_back( - createStringProperty("addressLocality", "San Francisco")); + CreateStringProperty("addressLocality", "San Francisco")); restaurant->properties.push_back( - createEntityProperty("address", std::move(address))); + CreateEntityProperty("address", std::move(address))); expected->entities.push_back(std::move(restaurant)); EXPECT_EQ(expected, extracted); @@ -384,7 +384,7 @@ ASSERT_FALSE(extracted.is_null()); WebPagePtr expected = - createWebPage("http://www.test.com/", "My neat website about cool stuff"); + CreateWebPage("http://www.test.com/", "My neat website about cool stuff"); EntityPtr restaurant = Entity::New(); restaurant->type = "Restaurant"; @@ -434,12 +434,12 @@ ASSERT_FALSE(extracted.is_null()); WebPagePtr expected = - createWebPage("http://www.test.com/", "My neat website about cool stuff"); + CreateWebPage("http://www.test.com/", "My neat website about cool stuff"); EntityPtr restaurant = Entity::New(); restaurant->type = "Restaurant"; restaurant->properties.push_back( - createStringProperty("name", "Ye ol greasy diner")); + CreateStringProperty("name", "Ye ol greasy diner")); PropertyPtr addressProperty = Property::New(); addressProperty->name = "address"; @@ -449,9 +449,9 @@ EntityPtr address = Entity::New(); address->type = "Thing"; address->properties.push_back( - createStringProperty("streetAddress", "123 Big Oak Road")); + CreateStringProperty("streetAddress", "123 Big Oak Road")); address->properties.push_back( - createStringProperty("addressLocality", "San Francisco")); + CreateStringProperty("addressLocality", "San Francisco")); addressProperty->values->get_entity_values().push_back(std::move(address)); } restaurant->properties.push_back(std::move(addressProperty)); @@ -488,12 +488,12 @@ ASSERT_FALSE(extracted.is_null()); WebPagePtr expected = - createWebPage("http://www.test.com/", "My neat website about cool stuff"); + CreateWebPage("http://www.test.com/", "My neat website about cool stuff"); EntityPtr restaurant = Entity::New(); restaurant->type = "Restaurant"; restaurant->properties.push_back( - createStringProperty("name", maxLengthString.ToString())); + CreateStringProperty("name", maxLengthString.ToString())); expected->entities.push_back(std::move(restaurant)); EXPECT_EQ(expected, extracted); @@ -517,7 +517,7 @@ ASSERT_TRUE(extracted.is_null()); } -TEST_F(CopylessPasteExtractorTest, enforceTypeWhitelist) { +TEST_F(CopylessPasteExtractorTest, UnhandledTypeIgnored) { SetHTMLInnerHTML( "<body>" "<script type=\"application/ld+json\">" @@ -565,7 +565,7 @@ ASSERT_FALSE(extracted.is_null()); WebPagePtr expected = - createWebPage("http://www.test.com/", "My neat website about cool stuff"); + CreateWebPage("http://www.test.com/", "My neat website about cool stuff"); EntityPtr restaurant = Entity::New(); restaurant->type = "Restaurant"; @@ -612,14 +612,14 @@ ASSERT_FALSE(extracted.is_null()); WebPagePtr expected = - createWebPage("http://www.test.com/", "My neat website about cool stuff"); + CreateWebPage("http://www.test.com/", "My neat website about cool stuff"); EntityPtr restaurant = Entity::New(); restaurant->type = "Restaurant"; for (int i = 0; i < 19; ++i) { restaurant->properties.push_back( - createStringProperty(String::Number(i), "a")); + CreateStringProperty(String::Number(i), "a")); } expected->entities.push_back(std::move(restaurant)); @@ -645,7 +645,7 @@ ASSERT_FALSE(extracted.is_null()); WebPagePtr expected = - createWebPage("http://www.test.com/", "My neat website about cool stuff"); + CreateWebPage("http://www.test.com/", "My neat website about cool stuff"); EntityPtr restaurant = Entity::New(); restaurant->type = "Restaurant"; @@ -674,7 +674,7 @@ ASSERT_FALSE(extracted.is_null()); WebPagePtr expected = - createWebPage("http://www.test.com/", "My neat website about cool stuff"); + CreateWebPage("http://www.test.com/", "My neat website about cool stuff"); EntityPtr restaurant = Entity::New(); restaurant->type = "Restaurant"; @@ -703,7 +703,7 @@ ASSERT_FALSE(extracted.is_null()); WebPagePtr expected = - createWebPage("http://www.test.com/", "My neat website about cool stuff"); + CreateWebPage("http://www.test.com/", "My neat website about cool stuff"); EntityPtr restaurant = Entity::New(); restaurant->type = "Restaurant"; @@ -741,12 +741,12 @@ ASSERT_FALSE(extracted.is_null()); WebPagePtr expected = - createWebPage("http://www.test.com/", "My neat website about cool stuff"); + CreateWebPage("http://www.test.com/", "My neat website about cool stuff"); EntityPtr restaurant = Entity::New(); restaurant->type = "Restaurant"; restaurant->properties.push_back( - createStringProperty("name", "Ye ol greasy diner")); + CreateStringProperty("name", "Ye ol greasy diner")); EntityPtr entity1 = Entity::New(); entity1->type = "Thing"; @@ -757,12 +757,12 @@ EntityPtr entity3 = Entity::New(); entity3->type = "Thing"; - entity2->properties.push_back(createEntityProperty("3", std::move(entity3))); + entity2->properties.push_back(CreateEntityProperty("3", std::move(entity3))); - entity1->properties.push_back(createEntityProperty("2", std::move(entity2))); + entity1->properties.push_back(CreateEntityProperty("2", std::move(entity2))); restaurant->properties.push_back( - createEntityProperty("1", std::move(entity1))); + CreateEntityProperty("1", std::move(entity1))); expected->entities.push_back(std::move(restaurant)); EXPECT_EQ(expected, extracted); @@ -794,12 +794,12 @@ ASSERT_FALSE(extracted.is_null()); WebPagePtr expected = - createWebPage("http://www.test.com/", "My neat website about cool stuff"); + CreateWebPage("http://www.test.com/", "My neat website about cool stuff"); EntityPtr restaurant = Entity::New(); restaurant->type = "Restaurant"; restaurant->properties.push_back( - createStringProperty("name", "Ye ol greasy diner")); + CreateStringProperty("name", "Ye ol greasy diner")); EntityPtr entity1 = Entity::New(); entity1->type = "Thing"; @@ -810,14 +810,14 @@ EntityPtr entity3 = Entity::New(); entity3->type = "Thing"; - entity3->properties.push_back(createLongProperty("4", 5)); + entity3->properties.push_back(CreateLongProperty("4", 5)); - entity2->properties.push_back(createEntityProperty("3", std::move(entity3))); + entity2->properties.push_back(CreateEntityProperty("3", std::move(entity3))); - entity1->properties.push_back(createEntityProperty("2", std::move(entity2))); + entity1->properties.push_back(CreateEntityProperty("2", std::move(entity2))); restaurant->properties.push_back( - createEntityProperty("1", std::move(entity1))); + CreateEntityProperty("1", std::move(entity1))); expected->entities.push_back(std::move(restaurant)); EXPECT_EQ(expected, extracted);
diff --git a/third_party/blink/renderer/modules/document_metadata/copyless_paste_server.cc b/third_party/blink/renderer/modules/document_metadata/copyless_paste_server.cc index a7a8f1bf..08d9bc0 100644 --- a/third_party/blink/renderer/modules/document_metadata/copyless_paste_server.cc +++ b/third_party/blink/renderer/modules/document_metadata/copyless_paste_server.cc
@@ -32,7 +32,7 @@ return; } std::move(callback).Run( - CopylessPasteExtractor::extract(*frame_->GetDocument())); + CopylessPasteExtractor::Extract(*frame_->GetDocument())); } } // namespace blink
diff --git a/third_party/blink/renderer/modules/exported/web_ax_object.cc b/third_party/blink/renderer/modules/exported/web_ax_object.cc index 0d4d396..b74634c 100644 --- a/third_party/blink/renderer/modules/exported/web_ax_object.cc +++ b/third_party/blink/renderer/modules/exported/web_ax_object.cc
@@ -1029,11 +1029,21 @@ return private_->GetTextPosition(); } -int WebAXObject::TextStyle() const { - if (IsDetached()) - return 0; - - return private_->GetTextStyle(); +void WebAXObject::GetTextStyleAndTextDecorationStyle( + int32_t* text_style, + ax::mojom::TextDecorationStyle* text_overline_style, + ax::mojom::TextDecorationStyle* text_strikethrough_style, + ax::mojom::TextDecorationStyle* text_underline_style) const { + if (IsDetached()) { + *text_style = 0; + *text_overline_style = ax::mojom::TextDecorationStyle::kNone; + *text_strikethrough_style = ax::mojom::TextDecorationStyle::kNone; + *text_underline_style = ax::mojom::TextDecorationStyle::kNone; + return; + } + private_->GetTextStyleAndTextDecorationStyle(text_style, text_overline_style, + text_strikethrough_style, + text_underline_style); } WebURL WebAXObject::Url() const {
diff --git a/third_party/blink/renderer/modules/gamepad/gamepad.cc b/third_party/blink/renderer/modules/gamepad/gamepad.cc index e8c5d87..6d842d6a 100644 --- a/third_party/blink/renderer/modules/gamepad/gamepad.cc +++ b/third_party/blink/renderer/modules/gamepad/gamepad.cc
@@ -27,12 +27,10 @@ #include <algorithm> -#include "third_party/blink/renderer/modules/gamepad/navigator_gamepad.h" - namespace blink { -Gamepad::Gamepad(NavigatorGamepad* navigator_gamepad, unsigned index) - : navigator_gamepad_(navigator_gamepad), +Gamepad::Gamepad(Client* client, unsigned index) + : client_(client), index_(index), timestamp_(0.0), has_vibration_actuator_(false), @@ -87,11 +85,7 @@ } GamepadHapticActuator* Gamepad::vibrationActuator() const { - // A disconnected gamepad may share the same index as a newly-connected - // gamepad. Return nullptr for disconnected gamepads to avoid returning the - // actuator for the connected gamepad. - return connected_ ? navigator_gamepad_->GetVibrationActuator(index_) - : nullptr; + return client_->GetVibrationActuatorForGamepad(*this); } void Gamepad::SetVibrationActuatorInfo( @@ -130,7 +124,7 @@ } void Gamepad::Trace(blink::Visitor* visitor) { - visitor->Trace(navigator_gamepad_); + visitor->Trace(client_); visitor->Trace(buttons_); visitor->Trace(pose_); ScriptWrappable::Trace(visitor);
diff --git a/third_party/blink/renderer/modules/gamepad/gamepad.h b/third_party/blink/renderer/modules/gamepad/gamepad.h index d98a65a..955d448b 100644 --- a/third_party/blink/renderer/modules/gamepad/gamepad.h +++ b/third_party/blink/renderer/modules/gamepad/gamepad.h
@@ -39,13 +39,19 @@ namespace blink { -class NavigatorGamepad; - class MODULES_EXPORT Gamepad final : public ScriptWrappable { DEFINE_WRAPPERTYPEINFO(); public: - explicit Gamepad(NavigatorGamepad* navigator_gamepad, unsigned index); + // Objects implementing this interface are garbage-collected. + class Client : public GarbageCollectedMixin { + public: + virtual GamepadHapticActuator* GetVibrationActuatorForGamepad( + const Gamepad&) = 0; + virtual ~Client() = default; + }; + + explicit Gamepad(Client* client, unsigned index); ~Gamepad() override; typedef Vector<double> DoubleVector; @@ -91,8 +97,7 @@ void Trace(blink::Visitor*) override; private: - // A reference to the NavigatorGamepad that created this gamepad. - Member<NavigatorGamepad> navigator_gamepad_; + Member<Client> client_; // A string identifying the gamepad model. String id_;
diff --git a/third_party/blink/renderer/modules/gamepad/navigator_gamepad.cc b/third_party/blink/renderer/modules/gamepad/navigator_gamepad.cc index 2b825836..bc3319b 100644 --- a/third_party/blink/renderer/modules/gamepad/navigator_gamepad.cc +++ b/third_party/blink/renderer/modules/gamepad/navigator_gamepad.cc
@@ -192,17 +192,22 @@ } } -GamepadHapticActuator* NavigatorGamepad::GetVibrationActuator( - uint32_t pad_index) { - auto* gamepad = gamepads_->item(pad_index); - if (!gamepad || !gamepad->HasVibrationActuator()) +GamepadHapticActuator* NavigatorGamepad::GetVibrationActuatorForGamepad( + const Gamepad& gamepad) { + if (!gamepad.connected()) { return nullptr; + } + + uint32_t pad_index = gamepad.index(); + if (!gamepad.HasVibrationActuator()) { + return nullptr; + } if (!vibration_actuators_[pad_index]) { ExecutionContext* context = DomWindow() ? DomWindow()->GetExecutionContext() : nullptr; auto* actuator = GamepadHapticActuator::Create(context, pad_index); - actuator->SetType(gamepad->GetVibrationActuatorType()); + actuator->SetType(gamepad.GetVibrationActuatorType()); vibration_actuators_[pad_index] = actuator; } return vibration_actuators_[pad_index].Get(); @@ -216,6 +221,7 @@ Supplement<Navigator>::Trace(visitor); DOMWindowClient::Trace(visitor); PlatformEventController::Trace(visitor); + Gamepad::Client::Trace(visitor); } bool NavigatorGamepad::StartUpdatingIfAttached() {
diff --git a/third_party/blink/renderer/modules/gamepad/navigator_gamepad.h b/third_party/blink/renderer/modules/gamepad/navigator_gamepad.h index 6422223..f901c378 100644 --- a/third_party/blink/renderer/modules/gamepad/navigator_gamepad.h +++ b/third_party/blink/renderer/modules/gamepad/navigator_gamepad.h
@@ -31,6 +31,7 @@ #include "third_party/blink/renderer/core/frame/local_dom_window.h" #include "third_party/blink/renderer/core/frame/navigator.h" #include "third_party/blink/renderer/core/frame/platform_event_controller.h" +#include "third_party/blink/renderer/modules/gamepad/gamepad.h" #include "third_party/blink/renderer/modules/modules_export.h" #include "third_party/blink/renderer/platform/heap/member.h" #include "third_party/blink/renderer/platform/supplementable.h" @@ -43,7 +44,6 @@ namespace blink { class Document; -class Gamepad; class GamepadDispatcher; class GamepadHapticActuator; class GamepadList; @@ -54,7 +54,8 @@ public Supplement<Navigator>, public DOMWindowClient, public PlatformEventController, - public LocalDOMWindow::EventListenerObserver { + public LocalDOMWindow::EventListenerObserver, + public Gamepad::Client { USING_GARBAGE_COLLECTED_MIXIN(NavigatorGamepad); public: @@ -69,8 +70,6 @@ static GamepadList* getGamepads(Navigator&); GamepadList* Gamepads(); - GamepadHapticActuator* GetVibrationActuator(uint32_t pad_index); - void Trace(blink::Visitor*) override; private: @@ -96,6 +95,10 @@ void DidRemoveEventListener(LocalDOMWindow*, const AtomicString&) override; void DidRemoveAllEventListeners(LocalDOMWindow*) override; + // Gamepad::Client + GamepadHapticActuator* GetVibrationActuatorForGamepad( + const Gamepad&) override; + // A reference to the buffer containing the last-received gamepad state. May // be nullptr if no data has been received yet. Do not overwrite this buffer // as it may have already been returned to the page. Instead, write to
diff --git a/third_party/blink/renderer/platform/BUILD.gn b/third_party/blink/renderer/platform/BUILD.gn index b340388..1218dfc 100644 --- a/third_party/blink/renderer/platform/BUILD.gn +++ b/third_party/blink/renderer/platform/BUILD.gn
@@ -199,6 +199,7 @@ "//third_party/blink/renderer/platform/heap:blink_heap_buildflags", "//third_party/blink/renderer/platform/network:make_generated", "//third_party/blink/renderer/platform/wtf", + "//third_party/boringssl", "//third_party/iccjpeg", "//third_party/libpng", "//third_party/libwebp", @@ -1450,6 +1451,7 @@ "//base/allocator:buildflags", "//components/viz/client", "//components/viz/common", + "//crypto", "//device/vr/public/mojom:mojom_blink", "//gin", "//media",
diff --git a/third_party/blink/renderer/platform/DEPS b/third_party/blink/renderer/platform/DEPS index 4d7a6859..4c6a6c2 100644 --- a/third_party/blink/renderer/platform/DEPS +++ b/third_party/blink/renderer/platform/DEPS
@@ -76,6 +76,10 @@ ] specific_include_rules = { + "crypto\.(cc|h)": [ + "+crypto", + "+third_party/boringssl/src/include", + ], "web_url_error\.cc": [ "+net/base/net_errors.h" ],
diff --git a/third_party/blink/renderer/platform/crypto.cc b/third_party/blink/renderer/platform/crypto.cc index 2e522aa1..d19f51e 100644 --- a/third_party/blink/renderer/platform/crypto.cc +++ b/third_party/blink/renderer/platform/crypto.cc
@@ -4,67 +4,77 @@ #include "third_party/blink/renderer/platform/crypto.h" -#include <memory> -#include "third_party/blink/public/platform/platform.h" -#include "third_party/blink/public/platform/web_crypto.h" -#include "third_party/blink/public/platform/web_crypto_algorithm.h" +#include "base/numerics/safe_conversions.h" +#include "crypto/openssl_util.h" +#include "third_party/blink/renderer/platform/wtf/text/string_utf8_adaptor.h" namespace blink { -static WebCryptoAlgorithmId ToWebCryptoAlgorithmId(HashAlgorithm algorithm) { +Digestor::Digestor(HashAlgorithm algorithm) { + crypto::EnsureOpenSSLInit(); + crypto::OpenSSLErrStackTracer err_tracer(FROM_HERE); + + const EVP_MD* evp_md = nullptr; switch (algorithm) { case kHashAlgorithmSha1: - return kWebCryptoAlgorithmIdSha1; + evp_md = EVP_sha1(); + break; case kHashAlgorithmSha256: - return kWebCryptoAlgorithmIdSha256; + evp_md = EVP_sha256(); + break; case kHashAlgorithmSha384: - return kWebCryptoAlgorithmIdSha384; + evp_md = EVP_sha384(); + break; case kHashAlgorithmSha512: - return kWebCryptoAlgorithmIdSha512; - }; + evp_md = EVP_sha512(); + break; + } - NOTREACHED(); - return kWebCryptoAlgorithmIdSha256; + has_failed_ = + !evp_md || !EVP_DigestInit_ex(digest_context_.get(), evp_md, nullptr); +} + +Digestor::~Digestor() = default; + +bool Digestor::Update(base::span<const uint8_t> data) { + if (has_failed_) + return false; + + crypto::OpenSSLErrStackTracer err_tracer(FROM_HERE); + has_failed_ = + !EVP_DigestUpdate(digest_context_.get(), data.data(), data.size()); + return !has_failed_; +} + +bool Digestor::UpdateUtf8(const String& string, WTF::UTF8ConversionMode mode) { + StringUTF8Adaptor utf8(string, mode); + return Update(base::as_bytes(base::make_span(utf8))); +} + +bool Digestor::Finish(DigestValue& digest_result) { + if (has_failed_) + return false; + + crypto::OpenSSLErrStackTracer err_tracer(FROM_HERE); + const size_t expected_size = EVP_MD_CTX_size(digest_context_.get()); + DCHECK_LE(expected_size, static_cast<size_t>(EVP_MAX_MD_SIZE)); + digest_result.resize(base::checked_cast<wtf_size_t>(expected_size)); + + unsigned result_size; + has_failed_ = !EVP_DigestFinal_ex(digest_context_.get(), digest_result.data(), + &result_size) || + result_size != expected_size; + return !has_failed_; } bool ComputeDigest(HashAlgorithm algorithm, const char* digestable, size_t length, DigestValue& digest_result) { - WebCryptoAlgorithmId algorithm_id = ToWebCryptoAlgorithmId(algorithm); - WebCrypto* crypto = Platform::Current()->Crypto(); - unsigned char* result; - unsigned result_size; - - DCHECK(crypto); - - std::unique_ptr<WebCryptoDigestor> digestor = - crypto->CreateDigestor(algorithm_id); - DCHECK(digestor); - if (!digestor->Consume(reinterpret_cast<const unsigned char*>(digestable), - length) || - !digestor->Finish(result, result_size)) - return false; - - digest_result.Append(static_cast<uint8_t*>(result), result_size); - return true; -} - -std::unique_ptr<WebCryptoDigestor> CreateDigestor(HashAlgorithm algorithm) { - return Platform::Current()->Crypto()->CreateDigestor( - ToWebCryptoAlgorithmId(algorithm)); -} - -void FinishDigestor(WebCryptoDigestor* digestor, DigestValue& digest_result) { - unsigned char* result = nullptr; - unsigned result_size = 0; - - if (!digestor->Finish(result, result_size)) - return; - - DCHECK(result); - - digest_result.Append(static_cast<uint8_t*>(result), result_size); + Digestor digestor(algorithm); + digestor.Update(base::as_bytes(base::make_span(digestable, length))); + digestor.Finish(digest_result); + return !digestor.has_failed(); } } // namespace blink
diff --git a/third_party/blink/renderer/platform/crypto.h b/third_party/blink/renderer/platform/crypto.h index 03a6372a..09258ac 100644 --- a/third_party/blink/renderer/platform/crypto.h +++ b/third_party/blink/renderer/platform/crypto.h
@@ -2,29 +2,23 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// TODO(jww) The original Blink-style header guard for this file conflicts with -// the header guard in Source/modules/crypto/Crypto.h, so this is a -// Chromium-style header guard instead. There is now a bug -// (https://crbug.com/360121) to track a proposal to change all header guards -// to a similar style. Thus, whenever that is resolved, this header guard -// should be changed to whatever style is agreed upon. -#ifndef SOURCE_PLATFORM_CRYPTO_H_ -#define SOURCE_PLATFORM_CRYPTO_H_ +#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_CRYPTO_H_ +#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_CRYPTO_H_ -#include <memory> -#include "third_party/blink/public/platform/web_crypto.h" +#include "base/containers/span.h" #include "third_party/blink/renderer/platform/platform_export.h" #include "third_party/blink/renderer/platform/wtf/allocator.h" -#include "third_party/blink/renderer/platform/wtf/hash_set.h" +#include "third_party/blink/renderer/platform/wtf/hash_functions.h" #include "third_party/blink/renderer/platform/wtf/text/string_hasher.h" +#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h" #include "third_party/blink/renderer/platform/wtf/vector.h" +#include "third_party/boringssl/src/include/openssl/digest.h" namespace blink { static const size_t kMaxDigestSize = 64; typedef Vector<uint8_t, kMaxDigestSize> DigestValue; -const size_t kSha1HashSize = 20; enum HashAlgorithm { kHashAlgorithmSha1, kHashAlgorithmSha256, @@ -36,11 +30,25 @@ const char* digestable, size_t length, DigestValue& digest_result); -// Note: this will never return null. -PLATFORM_EXPORT std::unique_ptr<WebCryptoDigestor> CreateDigestor( - HashAlgorithm); -PLATFORM_EXPORT void FinishDigestor(WebCryptoDigestor*, - DigestValue& digest_result); + +class PLATFORM_EXPORT Digestor { + public: + explicit Digestor(HashAlgorithm); + ~Digestor(); + + bool has_failed() const { return has_failed_; } + + // Return false on failure. These do nothing once the |has_failed_| flag is + // set. This object cannot be reused; do not update it after Finish. + bool Update(base::span<const uint8_t>); + bool UpdateUtf8(const String&, + WTF::UTF8ConversionMode = WTF::kLenientUTF8Conversion); + bool Finish(DigestValue&); + + private: + bssl::ScopedEVP_MD_CTX digest_context_; + bool has_failed_ = false; +}; } // namespace blink @@ -74,4 +82,5 @@ }; } // namespace WTF -#endif // SOURCE_PLATFORM_CRYPTO_H_ + +#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_CRYPTO_H_
diff --git a/third_party/blink/renderer/platform/loader/fetch/source_keyed_cached_metadata_handler_test.cc b/third_party/blink/renderer/platform/loader/fetch/source_keyed_cached_metadata_handler_test.cc index c2f4782..ad6e774 100644 --- a/third_party/blink/renderer/platform/loader/fetch/source_keyed_cached_metadata_handler_test.cc +++ b/third_party/blink/renderer/platform/loader/fetch/source_keyed_cached_metadata_handler_test.cc
@@ -7,6 +7,7 @@ #include <array> #include "testing/gtest/include/gtest/gtest.h" +#include "third_party/blink/public/platform/web_crypto.h" #include "third_party/blink/renderer/platform/crypto.h" #include "third_party/blink/renderer/platform/testing/testing_platform_support_with_mock_scheduler.h" #include "third_party/blink/renderer/platform/wtf/std_lib_extras.h" @@ -15,56 +16,6 @@ namespace { -class MockSha256WebCryptoDigestor : public WebCryptoDigestor { - public: - bool Consume(const unsigned char* data, unsigned data_size) override { - String key(data, data_size); - - auto it = kMapOfHashes.find(key); - - if (it != kMapOfHashes.end()) { - hash_exists_ = true; - hash_ = it->value; - } - - return hash_exists_; - } - - bool Finish(unsigned char*& result_data, - unsigned& result_data_size) override { - if (hash_exists_) { - result_data = hash_.data(); - result_data_size = hash_.size(); - } - return hash_exists_; - } - - private: - Vector<unsigned char> hash_; - bool hash_exists_; - - HashMap<String, Vector<unsigned char>> kMapOfHashes = { - {"source1", - Vector<unsigned char>{0xc4, 0xd5, 0xe4, 0x35, 0x74, 0x89, 0x3c, 0x3c, - 0xc3, 0xd4, 0xba, 0xba, 0x65, 0x58, 0x92, 0x48, - 0x47, 0x9a, 0x9f, 0xbf, 0xaf, 0x1f, 0x60, 0x8e, - 0xb1, 0x54, 0x1e, 0xc0, 0xc6, 0xfe, 0x63, 0x6f}}, - {"source2", - Vector<unsigned char>{0x99, 0x2f, 0x4e, 0xb2, 0x41, 0xee, 0x6e, 0xef, - 0xe4, 0x92, 0x80, 0x25, 0xa2, 0x74, 0x7d, 0xb0, - 0x8b, 0x91, 0x98, 0x34, 0xc9, 0x3c, 0x5f, 0x57, - 0x41, 0x72, 0x5f, 0xa2, 0x6b, 0x63, 0x38, 0x41}}}; -}; - -// Mock WebCrypto implementation for digest calculation. -class MockDigestWebCrypto : public WebCrypto { - std::unique_ptr<WebCryptoDigestor> CreateDigestor( - WebCryptoAlgorithmId algorithm_id) override { - EXPECT_EQ(algorithm_id, WebCryptoAlgorithmId::kWebCryptoAlgorithmIdSha256); - return std::make_unique<MockSha256WebCryptoDigestor>(); - } -}; - // Structure holding cache metadata sent to the platform. struct CacheMetadataEntry { CacheMetadataEntry(const WebURL& url, @@ -87,8 +38,6 @@ SourceKeyedCachedMetadataHandlerMockPlatform() {} ~SourceKeyedCachedMetadataHandlerMockPlatform() override = default; - WebCrypto* Crypto() override { return &mock_web_crypto_; } - void CacheMetadata(blink::mojom::CodeCacheType cache_type, const WebURL& url, base::Time response_time, @@ -118,7 +67,6 @@ } private: - MockDigestWebCrypto mock_web_crypto_; Vector<CacheMetadataEntry> cache_entries_; };
diff --git a/third_party/blink/renderer/platform/loader/subresource_integrity_test.cc b/third_party/blink/renderer/platform/loader/subresource_integrity_test.cc index 3f62128..447a3d7 100644 --- a/third_party/blink/renderer/platform/loader/subresource_integrity_test.cc +++ b/third_party/blink/renderer/platform/loader/subresource_integrity_test.cc
@@ -32,22 +32,6 @@ namespace blink { static const char kBasicScript[] = "alert('test');"; -static unsigned char kSha256Hash[] = { - 0x18, 0x01, 0x78, 0xf1, 0x03, 0xa8, 0xc5, 0x1b, 0xee, 0xd2, 0x06, - 0x40, 0x99, 0x08, 0xaf, 0x51, 0xd2, 0x4f, 0xc8, 0x16, 0x9c, 0xab, - 0x39, 0xc1, 0x01, 0x7c, 0x27, 0x91, 0xfa, 0x66, 0x41, 0x7e}; -static unsigned char kSha384Hash[] = { - 0x9d, 0xea, 0x77, 0x5e, 0x9b, 0xe1, 0x53, 0x1a, 0x42, 0x30, 0xe5, 0x57, - 0x20, 0x53, 0xde, 0x71, 0x38, 0x40, 0xa9, 0xd6, 0x3f, 0xb9, 0x57, 0xa2, - 0x0f, 0x89, 0x17, 0x4a, 0xa5, 0xe9, 0xc7, 0x46, 0x09, 0x51, 0x65, 0x38, - 0x7d, 0x34, 0xda, 0x16, 0x07, 0x22, 0x4e, 0xe6, 0x64, 0xed, 0xf9, 0x84}; -static unsigned char kSha512Hash[] = { - 0x4d, 0x79, 0x09, 0xc3, 0x5f, 0x0f, 0xaa, 0x55, 0x65, 0x11, 0x45, - 0xd7, 0x8d, 0xe5, 0xdb, 0x19, 0xeb, 0x68, 0xa7, 0x54, 0xca, 0x07, - 0x7c, 0x18, 0x40, 0x8a, 0x75, 0xfe, 0x28, 0x71, 0x08, 0xe1, 0x46, - 0x51, 0xf1, 0xbd, 0x4d, 0x83, 0x9a, 0x03, 0x53, 0x25, 0x92, 0x94, - 0xc0, 0xa9, 0x25, 0x7a, 0xc9, 0xa7, 0xaf, 0x2c, 0xef, 0x13, 0x8f, - 0x9a, 0x60, 0x1f, 0x52, 0x66, 0x67, 0xef, 0x88, 0xb4}; static const char kSha256Integrity[] = "sha256-GAF48QOoxRvu0gZAmQivUdJPyBacqznBAXwnkfpmQX4="; static const char kSha256IntegrityLenientSyntax[] = @@ -551,29 +535,6 @@ {url, FetchRequestMode::kCors, FetchResponseType::kDefault, kOk}, }; - MockWebCryptoDigestorFactory factory_sha256( - kBasicScript, strlen(kBasicScript), kSha256Hash, sizeof(kSha256Hash)); - MockWebCryptoDigestorFactory factory_sha384( - kBasicScript, strlen(kBasicScript), kSha384Hash, sizeof(kSha384Hash)); - MockWebCryptoDigestorFactory factory_sha512( - kBasicScript, strlen(kBasicScript), kSha512Hash, sizeof(kSha512Hash)); - - CryptoTestingPlatformSupport::SetMockCryptoScope mock_crypto_scope( - *platform_.GetTestingPlatformSupport()); - - EXPECT_CALL(mock_crypto_scope.MockCrypto(), - CreateDigestorProxy(kWebCryptoAlgorithmIdSha256)) - .WillRepeatedly(testing::InvokeWithoutArgs( - &factory_sha256, &MockWebCryptoDigestorFactory::Create)); - EXPECT_CALL(mock_crypto_scope.MockCrypto(), - CreateDigestorProxy(kWebCryptoAlgorithmIdSha384)) - .WillRepeatedly(testing::InvokeWithoutArgs( - &factory_sha384, &MockWebCryptoDigestorFactory::Create)); - EXPECT_CALL(mock_crypto_scope.MockCrypto(), - CreateDigestorProxy(kWebCryptoAlgorithmIdSha512)) - .WillRepeatedly(testing::InvokeWithoutArgs( - &factory_sha512, &MockWebCryptoDigestorFactory::Create)); - for (const auto& test : cases) { SCOPED_TRACE(testing::Message() << ", target: " << test.url.BaseAsString()
diff --git a/third_party/blink/renderer/platform/testing/mock_web_crypto.cc b/third_party/blink/renderer/platform/testing/mock_web_crypto.cc index 7b0ecee..a4997ec 100644 --- a/third_party/blink/renderer/platform/testing/mock_web_crypto.cc +++ b/third_party/blink/renderer/platform/testing/mock_web_crypto.cc
@@ -11,12 +11,6 @@ namespace blink { -using testing::_; -using testing::DoAll; -using testing::InSequence; -using testing::Return; -using testing::SetArgReferee; - // MemEq(p, len) expects memcmp(arg, p, len) == 0, where |arg| is the argument // to be matched. MATCHER_P2(MemEq, @@ -28,43 +22,4 @@ return memcmp(arg, p, len) == 0; } -void MockWebCryptoDigestor::ExpectConsumeAndFinish(const void* input_data, - unsigned input_length, - void* output_data, - unsigned output_length) { - InSequence s; - - // Consume should be called with a memory region equal to |input_data|. - EXPECT_CALL(*this, Consume(MemEq(input_data, input_length), input_length)) - .WillOnce(Return(true)); - - // Finish(unsigned char*& result_data, unsigned& result_data_size) { - // result_data = output_data; - // result_data_size = output_length; - // return true; - // } - EXPECT_CALL(*this, Finish(_, _)) - .WillOnce( - DoAll(SetArgReferee<0>(static_cast<unsigned char*>(output_data)), - SetArgReferee<1>(output_length), Return(true))); -} - -MockWebCryptoDigestorFactory::MockWebCryptoDigestorFactory( - const void* input_data, - unsigned input_length, - void* output_data, - unsigned output_length) - : input_data_(input_data), - input_length_(input_length), - output_data_(output_data), - output_length_(output_length) {} - -MockWebCryptoDigestor* MockWebCryptoDigestorFactory::Create() { - std::unique_ptr<MockWebCryptoDigestor> digestor( - MockWebCryptoDigestor::Create()); - digestor->ExpectConsumeAndFinish(input_data_, input_length_, output_data_, - output_length_); - return digestor.release(); -} - } // namespace blink
diff --git a/third_party/blink/renderer/platform/testing/mock_web_crypto.h b/third_party/blink/renderer/platform/testing/mock_web_crypto.h index 11f839df..86a601c 100644 --- a/third_party/blink/renderer/platform/testing/mock_web_crypto.h +++ b/third_party/blink/renderer/platform/testing/mock_web_crypto.h
@@ -100,7 +100,6 @@ WebCryptoKeyUsageMask, WebCryptoResult, scoped_refptr<base::SingleThreadTaskRunner>)); - MOCK_METHOD1(CreateDigestorProxy, WebCryptoDigestor*(WebCryptoAlgorithmId)); MOCK_METHOD7(DeserializeKeyForClone, bool(const WebCryptoKeyAlgorithm&, WebCryptoKeyType, @@ -113,53 +112,9 @@ bool(const WebCryptoKey&, WebVector<unsigned char>&)); protected: - std::unique_ptr<WebCryptoDigestor> CreateDigestor( - WebCryptoAlgorithmId id) override { - return std::unique_ptr<WebCryptoDigestor>(CreateDigestorProxy(id)); - } - DISALLOW_COPY_AND_ASSIGN(MockWebCrypto); }; -class MockWebCryptoDigestor : public WebCryptoDigestor { - public: - ~MockWebCryptoDigestor() override = default; - - static MockWebCryptoDigestor* Create() { - return new testing::StrictMock<MockWebCryptoDigestor>(); - } - - void ExpectConsumeAndFinish(const void* input_data, - unsigned input_length, - void* output_data, - unsigned output_length); - - MOCK_METHOD2(Consume, bool(const unsigned char*, unsigned)); - MOCK_METHOD2(Finish, bool(unsigned char*&, unsigned&)); - - protected: - MockWebCryptoDigestor() = default; - - DISALLOW_COPY_AND_ASSIGN(MockWebCryptoDigestor); -}; - -class MockWebCryptoDigestorFactory final { - STACK_ALLOCATED(); - - public: - MockWebCryptoDigestorFactory(const void* input_data, - unsigned input_length, - void* output_data, - unsigned output_length); - MockWebCryptoDigestor* Create(); - - private: - const void* const input_data_; - const unsigned input_length_; - void* const output_data_; - const unsigned output_length_; -}; - } // namespace blink #endif
diff --git a/third_party/blink/web_tests/TestExpectations b/third_party/blink/web_tests/TestExpectations index 8471582..bfdbbfd7 100644 --- a/third_party/blink/web_tests/TestExpectations +++ b/third_party/blink/web_tests/TestExpectations
@@ -451,6 +451,69 @@ crbug.com/949909 external/wpt/css/css-text-decor/text-emphasis-style-open-001.xht [ Failure ] crbug.com/949909 external/wpt/css/css-text-decor/text-emphasis-style-shape-001.xht [ Failure ] crbug.com/949909 external/wpt/css/css-text-decor/text-emphasis-style-string-001.xht [ Failure ] +crbug.com/949909 external/wpt/css/css-text-decor/text-underline-position-073-manual.html [ Skip ] +crbug.com/949909 external/wpt/css/css-text-decor/text-underline-position-072-manual.html [ Skip ] +crbug.com/949909 external/wpt/css/css-text-decor/text-underline-position-071-manual.html [ Skip ] +crbug.com/949909 external/wpt/css/css-text-decor/text-underline-position-020-manual.html [ Skip ] +crbug.com/949909 external/wpt/css/css-text-decor/text-underline-position-076-manual.html [ Skip ] +crbug.com/949909 external/wpt/css/css-text-decor/text-underline-position-075-manual.html [ Skip ] +crbug.com/949909 external/wpt/css/css-text-decor/text-underline-position-021-manual.html [ Skip ] +crbug.com/949909 external/wpt/css/css-text-decor/text-underline-position-022-manual.html [ Skip ] +crbug.com/949909 external/wpt/css/css-text-decor/text-underline-position-074-manual.html [ Skip ] +crbug.com/949909 external/wpt/css/css-text-decor/text-underline-position-019-manual.html [ Skip ] +crbug.com/949909 external/wpt/css/css-text-decor/text-decoration-line-048-manual.html [ Skip ] +crbug.com/949909 external/wpt/css/css-text-decor/text-decoration-line-046a-manual.html [ Skip ] +crbug.com/949909 external/wpt/css/css-text-decor/text-decoration-line-082-manual.html [ Skip ] +crbug.com/949909 external/wpt/css/css-text-decor/text-decoration-line-048a-manual.html [ Skip ] +crbug.com/949909 external/wpt/css/css-text-decor/text-decoration-line-085-manual.html [ Skip ] +crbug.com/949909 external/wpt/css/css-text-decor/text-decoration-line-044-manual.html [ Skip ] +crbug.com/949909 external/wpt/css/css-text-decor/text-decoration-line-097a-manual.html [ Skip ] +crbug.com/949909 external/wpt/css/css-text-decor/text-decoration-line-001-manual.html [ Skip ] +crbug.com/949909 external/wpt/css/css-text-decor/text-decoration-line-090-manual.html [ Skip ] +crbug.com/949909 external/wpt/css/css-text-decor/text-decoration-line-092-manual.html [ Skip ] +crbug.com/949909 external/wpt/css/css-text-decor/text-decoration-line-095-manual.html [ Skip ] +crbug.com/949909 external/wpt/css/css-text-decor/text-decoration-line-095a-manual.html [ Skip ] +crbug.com/949909 external/wpt/css/css-text-decor/text-decoration-line-040a-manual.html [ Skip ] +crbug.com/949909 external/wpt/css/css-text-decor/text-decoration-line-096a-manual.html [ Skip ] +crbug.com/949909 external/wpt/css/css-text-decor/text-decoration-line-002-manual.html [ Skip ] +crbug.com/949909 external/wpt/css/css-text-decor/text-decoration-line-045-manual.html [ Skip ] +crbug.com/949909 external/wpt/css/css-text-decor/text-decoration-line-091-manual.html [ Skip ] +crbug.com/949909 external/wpt/css/css-text-decor/text-decoration-line-092a-manual.html [ Skip ] +crbug.com/949909 external/wpt/css/css-text-decor/text-decoration-line-004-manual.html [ Skip ] +crbug.com/949909 external/wpt/css/css-text-decor/text-decoration-line-096-manual.html [ Skip ] +crbug.com/949909 external/wpt/css/css-text-decor/text-decoration-line-040-manual.html [ Skip ] +crbug.com/949909 external/wpt/css/css-text-decor/text-decoration-line-003-manual.html [ Skip ] +crbug.com/949909 external/wpt/css/css-text-decor/text-decoration-line-091a-manual.html [ Skip ] +crbug.com/949909 external/wpt/css/css-text-decor/text-decoration-line-097-manual.html [ Skip ] +crbug.com/949909 external/wpt/css/css-text-decor/text-decoration-line-041-manual.html [ Skip ] +crbug.com/949909 external/wpt/css/css-text-decor/text-decoration-line-049-manual.html [ Skip ] +crbug.com/949909 external/wpt/css/css-text-decor/text-decoration-line-090a-manual.html [ Skip ] +crbug.com/949909 external/wpt/css/css-text-decor/text-decoration-090a-manual.html [ Skip ] +crbug.com/949909 external/wpt/css/css-text-decor/text-decoration-046a-manual.html [ Skip ] +crbug.com/949909 external/wpt/css/css-text-decor/text-decoration-091-manual.html [ Skip ] +crbug.com/949909 external/wpt/css/css-text-decor/text-decoration-048a-manual.html [ Skip ] +crbug.com/949909 external/wpt/css/css-text-decor/text-decoration-092a-manual.html [ Skip ] +crbug.com/949909 external/wpt/css/css-text-decor/text-decoration-096-manual.html [ Skip ] +crbug.com/949909 external/wpt/css/css-text-decor/text-decoration-048-manual.html [ Skip ] +crbug.com/949909 external/wpt/css/css-text-decor/text-decoration-003-manual.html [ Skip ] +crbug.com/949909 external/wpt/css/css-text-decor/text-decoration-091a-manual.html [ Skip ] +crbug.com/949909 external/wpt/css/css-text-decor/text-decoration-002-manual.html [ Skip ] +crbug.com/949909 external/wpt/css/css-text-decor/text-decoration-090-manual.html [ Skip ] +crbug.com/949909 external/wpt/css/css-text-decor/text-decoration-092-manual.html [ Skip ] +crbug.com/949909 external/wpt/css/css-text-decor/text-decoration-095a-manual.html [ Skip ] +crbug.com/949909 external/wpt/css/css-text-decor/text-decoration-049-manual.html [ Skip ] +crbug.com/949909 external/wpt/css/css-text-decor/text-decoration-041-manual.html [ Skip ] +crbug.com/949909 external/wpt/css/css-text-decor/text-decoration-040a-manual.html [ Skip ] +crbug.com/949909 external/wpt/css/css-text-decor/text-decoration-097-manual.html [ Skip ] +crbug.com/949909 external/wpt/css/css-text-decor/text-decoration-001-manual.html [ Skip ] +crbug.com/949909 external/wpt/css/css-text-decor/text-decoration-045-manual.html [ Skip ] +crbug.com/949909 external/wpt/css/css-text-decor/text-decoration-082-manual.html [ Skip ] +crbug.com/949909 external/wpt/css/css-text-decor/text-decoration-096a-manual.html [ Skip ] +crbug.com/949909 external/wpt/css/css-text-decor/text-decoration-044-manual.html [ Skip ] +crbug.com/949909 external/wpt/css/css-text-decor/text-decoration-004-manual.html [ Skip ] +crbug.com/949909 external/wpt/css/css-text-decor/text-decoration-085-manual.html [ Skip ] +crbug.com/949909 external/wpt/css/css-text-decor/text-decoration-097a-manual.html [ Skip ] +crbug.com/949909 external/wpt/css/css-text-decor/text-decoration-040-manual.html [ Skip ] crbug.com/722825 media/controls/video-enter-exit-fullscreen-while-hovering-shows-controls.html [ Timeout Pass ] crbug.com/722825 virtual/video-surface-layer/media/controls/video-enter-exit-fullscreen-while-hovering-shows-controls.html [ Timeout Pass ] @@ -3065,69 +3128,6 @@ crbug.com/626703 [ Mac10.12 ] external/wpt/html/semantics/embedded-content/media-elements/track/track-element/track-cue-negative-duration.html [ Timeout ] crbug.com/626703 [ Mac10.13 ] external/wpt/html/semantics/embedded-content/media-elements/track/track-element/track-cue-negative-duration.html [ Timeout ] crbug.com/626703 [ Retina ] external/wpt/html/semantics/embedded-content/media-elements/track/track-element/track-cue-negative-duration.html [ Timeout ] -crbug.com/626703 external/wpt/css/css-text-decor/text-underline-position-073-manual.html [ Skip ] -crbug.com/626703 external/wpt/css/css-text-decor/text-underline-position-072-manual.html [ Skip ] -crbug.com/626703 external/wpt/css/css-text-decor/text-underline-position-071-manual.html [ Skip ] -crbug.com/626703 external/wpt/css/css-text-decor/text-underline-position-020-manual.html [ Skip ] -crbug.com/626703 external/wpt/css/css-text-decor/text-underline-position-076-manual.html [ Skip ] -crbug.com/626703 external/wpt/css/css-text-decor/text-underline-position-075-manual.html [ Skip ] -crbug.com/626703 external/wpt/css/css-text-decor/text-underline-position-021-manual.html [ Skip ] -crbug.com/626703 external/wpt/css/css-text-decor/text-underline-position-022-manual.html [ Skip ] -crbug.com/626703 external/wpt/css/css-text-decor/text-underline-position-074-manual.html [ Skip ] -crbug.com/626703 external/wpt/css/css-text-decor/text-underline-position-019-manual.html [ Skip ] -crbug.com/626703 external/wpt/css/css-text-decor/text-decoration-line-048-manual.html [ Skip ] -crbug.com/626703 external/wpt/css/css-text-decor/text-decoration-line-046a-manual.html [ Skip ] -crbug.com/626703 external/wpt/css/css-text-decor/text-decoration-line-082-manual.html [ Skip ] -crbug.com/626703 external/wpt/css/css-text-decor/text-decoration-line-048a-manual.html [ Skip ] -crbug.com/626703 external/wpt/css/css-text-decor/text-decoration-line-085-manual.html [ Skip ] -crbug.com/626703 external/wpt/css/css-text-decor/text-decoration-line-044-manual.html [ Skip ] -crbug.com/626703 external/wpt/css/css-text-decor/text-decoration-line-097a-manual.html [ Skip ] -crbug.com/626703 external/wpt/css/css-text-decor/text-decoration-line-001-manual.html [ Skip ] -crbug.com/626703 external/wpt/css/css-text-decor/text-decoration-line-090-manual.html [ Skip ] -crbug.com/626703 external/wpt/css/css-text-decor/text-decoration-line-092-manual.html [ Skip ] -crbug.com/626703 external/wpt/css/css-text-decor/text-decoration-line-095-manual.html [ Skip ] -crbug.com/626703 external/wpt/css/css-text-decor/text-decoration-line-095a-manual.html [ Skip ] -crbug.com/626703 external/wpt/css/css-text-decor/text-decoration-line-040a-manual.html [ Skip ] -crbug.com/626703 external/wpt/css/css-text-decor/text-decoration-line-096a-manual.html [ Skip ] -crbug.com/626703 external/wpt/css/css-text-decor/text-decoration-line-002-manual.html [ Skip ] -crbug.com/626703 external/wpt/css/css-text-decor/text-decoration-line-045-manual.html [ Skip ] -crbug.com/626703 external/wpt/css/css-text-decor/text-decoration-line-091-manual.html [ Skip ] -crbug.com/626703 external/wpt/css/css-text-decor/text-decoration-line-092a-manual.html [ Skip ] -crbug.com/626703 external/wpt/css/css-text-decor/text-decoration-line-004-manual.html [ Skip ] -crbug.com/626703 external/wpt/css/css-text-decor/text-decoration-line-096-manual.html [ Skip ] -crbug.com/626703 external/wpt/css/css-text-decor/text-decoration-line-040-manual.html [ Skip ] -crbug.com/626703 external/wpt/css/css-text-decor/text-decoration-line-003-manual.html [ Skip ] -crbug.com/626703 external/wpt/css/css-text-decor/text-decoration-line-091a-manual.html [ Skip ] -crbug.com/626703 external/wpt/css/css-text-decor/text-decoration-line-097-manual.html [ Skip ] -crbug.com/626703 external/wpt/css/css-text-decor/text-decoration-line-041-manual.html [ Skip ] -crbug.com/626703 external/wpt/css/css-text-decor/text-decoration-line-049-manual.html [ Skip ] -crbug.com/626703 external/wpt/css/css-text-decor/text-decoration-line-090a-manual.html [ Skip ] -crbug.com/626703 external/wpt/css/css-text-decor/text-decoration-090a-manual.html [ Skip ] -crbug.com/626703 external/wpt/css/css-text-decor/text-decoration-046a-manual.html [ Skip ] -crbug.com/626703 external/wpt/css/css-text-decor/text-decoration-091-manual.html [ Skip ] -crbug.com/626703 external/wpt/css/css-text-decor/text-decoration-048a-manual.html [ Skip ] -crbug.com/626703 external/wpt/css/css-text-decor/text-decoration-092a-manual.html [ Skip ] -crbug.com/626703 external/wpt/css/css-text-decor/text-decoration-096-manual.html [ Skip ] -crbug.com/626703 external/wpt/css/css-text-decor/text-decoration-048-manual.html [ Skip ] -crbug.com/626703 external/wpt/css/css-text-decor/text-decoration-003-manual.html [ Skip ] -crbug.com/626703 external/wpt/css/css-text-decor/text-decoration-091a-manual.html [ Skip ] -crbug.com/626703 external/wpt/css/css-text-decor/text-decoration-002-manual.html [ Skip ] -crbug.com/626703 external/wpt/css/css-text-decor/text-decoration-090-manual.html [ Skip ] -crbug.com/626703 external/wpt/css/css-text-decor/text-decoration-092-manual.html [ Skip ] -crbug.com/626703 external/wpt/css/css-text-decor/text-decoration-095a-manual.html [ Skip ] -crbug.com/626703 external/wpt/css/css-text-decor/text-decoration-049-manual.html [ Skip ] -crbug.com/626703 external/wpt/css/css-text-decor/text-decoration-041-manual.html [ Skip ] -crbug.com/626703 external/wpt/css/css-text-decor/text-decoration-040a-manual.html [ Skip ] -crbug.com/626703 external/wpt/css/css-text-decor/text-decoration-097-manual.html [ Skip ] -crbug.com/626703 external/wpt/css/css-text-decor/text-decoration-001-manual.html [ Skip ] -crbug.com/626703 external/wpt/css/css-text-decor/text-decoration-045-manual.html [ Skip ] -crbug.com/626703 external/wpt/css/css-text-decor/text-decoration-082-manual.html [ Skip ] -crbug.com/626703 external/wpt/css/css-text-decor/text-decoration-096a-manual.html [ Skip ] -crbug.com/626703 external/wpt/css/css-text-decor/text-decoration-044-manual.html [ Skip ] -crbug.com/626703 external/wpt/css/css-text-decor/text-decoration-004-manual.html [ Skip ] -crbug.com/626703 external/wpt/css/css-text-decor/text-decoration-085-manual.html [ Skip ] -crbug.com/626703 external/wpt/css/css-text-decor/text-decoration-097a-manual.html [ Skip ] -crbug.com/626703 external/wpt/css/css-text-decor/text-decoration-040-manual.html [ Skip ] crbug.com/626703 external/wpt/html/semantics/embedded-content/media-elements/src_object_blob.html [ Timeout ] crbug.com/626703 [ Mac10.12 ] external/wpt/service-workers/service-worker/websocket.https.html [ Timeout ] crbug.com/626703 [ Mac10.12 ] external/wpt/websockets/Secure-Close-4999-reason.any.html [ Timeout ] @@ -4277,6 +4277,10 @@ crbug.com/943487 external/wpt/wasm/webapi/origin.sub.any.serviceworker.html [ Timeout Pass ] crbug.com/943487 external/wpt/wasm/webapi/rejected-arg.any.sharedworker.html [ Timeout Pass ] +# Shared memory growth temporarily disabled. +crbug.com/951795 external/wpt/wasm/jsapi/grow.any.html [ Pass Failure ] +crbug.com/951795 external/wpt/wasm/jsapi/grow.any.worker.html [ Pass Failure ] + crbug.com/792435 external/wpt/css/css-multicol/multicol-rule-004.xht [ Failure ] crbug.com/792437 external/wpt/css/css-multicol/multicol-rule-inset-000.xht [ Failure ] crbug.com/792437 external/wpt/css/css-multicol/multicol-rule-outset-000.xht [ Failure ]
diff --git a/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_6.json b/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_6.json index 9137c65..b9d36aa 100644 --- a/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_6.json +++ b/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_6.json
@@ -145670,6 +145670,11 @@ {} ] ], + "css/css-text-decor/parsing/text-decoration-line-valid-expected.txt": [ + [ + {} + ] + ], "css/css-text-decor/reference/line-through-vertical-ref.html": [ [ {} @@ -232972,6 +232977,18 @@ {} ] ], + "css/css-text-decor/parsing/text-decoration-line-invalid.html": [ + [ + "css/css-text-decor/parsing/text-decoration-line-invalid.html", + {} + ] + ], + "css/css-text-decor/parsing/text-decoration-line-valid.html": [ + [ + "css/css-text-decor/parsing/text-decoration-line-valid.html", + {} + ] + ], "css/css-text-decor/text-decoration-serialization.tentative.html": [ [ "css/css-text-decor/text-decoration-serialization.tentative.html", @@ -239006,12 +239023,6 @@ {} ] ], - "css/cssom/stylesheet-deleterule-error.html": [ - [ - "css/cssom/stylesheet-deleterule-error.html", - {} - ] - ], "css/cssom/stylesheet-same-origin.sub.html": [ [ "css/cssom/stylesheet-same-origin.sub.html", @@ -392565,6 +392576,18 @@ "633c5c00392711f1fe1911a07f9cf53c3cd702e9", "reftest" ], + "css/css-text-decor/parsing/text-decoration-line-invalid.html": [ + "ec8d792c0a803dc726b01e762602b717befc5426", + "testharness" + ], + "css/css-text-decor/parsing/text-decoration-line-valid-expected.txt": [ + "caf82fb7d2c3bfcd254b51b135aa0e5a09a03415", + "support" + ], + "css/css-text-decor/parsing/text-decoration-line-valid.html": [ + "3dd2d0c834ec8c7340b093a4c7a1272fb35a8a26", + "testharness" + ], "css/css-text-decor/reference/line-through-vertical-ref.html": [ "979512787a18ec9cbed7e9baf4b2cbd57ab99d33", "support" @@ -417393,10 +417416,6 @@ "e86a9a16e3bc9c0a22277996ad1fbb5f273d4bdd", "testharness" ], - "css/cssom/stylesheet-deleterule-error.html": [ - "e01aa015c737d59e90923795fa730e9599e1ee6e", - "testharness" - ], "css/cssom/stylesheet-replacedata-dynamic-ref.html": [ "bc9cadebf15d720e9c89b8072c0ef36eca962343", "support"
diff --git a/third_party/blink/web_tests/external/wpt/css/css-text-decor/parsing/text-decoration-line-invalid.html b/third_party/blink/web_tests/external/wpt/css/css-text-decor/parsing/text-decoration-line-invalid.html new file mode 100644 index 0000000..ec8d792 --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/css/css-text-decor/parsing/text-decoration-line-invalid.html
@@ -0,0 +1,25 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>CSS Text Decoration Test: Parsing text-decoration-line with invalid values</title> +<link rel="author" title="Manuel Rego Casasnovas" href="mailto:rego@igalia.com"> +<link rel="help" href="https://drafts.csswg.org/css-text-decor-4/#text-decoration-line-property"> +<meta name="assert" content="text-decoration-line supports only the grammar 'none | [ underline || overline || line-through || blink ] | spelling-error | grammar-error'."> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/css/support/parsing-testcommon.js"></script> +<script> +test_invalid_value("text-decoration-line", "auto"); +test_invalid_value("text-decoration-line", "null"); +test_invalid_value("text-decoration-line", "noone"); +test_invalid_value("text-decoration-line", "under-line"); +test_invalid_value("text-decoration-line", "over-line"); +test_invalid_value("text-decoration-line", "linethrough"); +test_invalid_value("text-decoration-line", "none underline"); +test_invalid_value("text-decoration-line", "none spelling-error"); +test_invalid_value("text-decoration-line", "underline underline"); +test_invalid_value("text-decoration-line", "underline none overline"); +test_invalid_value("text-decoration-line", "blink line-through blink"); +test_invalid_value("text-decoration-line", "spelling-error overline"); +test_invalid_value("text-decoration-line", "spelling-error grammar-error"); +test_invalid_value("text-decoration-line", "blink underline line-through grammar-error"); +</script>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-text-decor/parsing/text-decoration-line-valid-expected.txt b/third_party/blink/web_tests/external/wpt/css/css-text-decor/parsing/text-decoration-line-valid-expected.txt new file mode 100644 index 0000000..caf82fb7 --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/css/css-text-decor/parsing/text-decoration-line-valid-expected.txt
@@ -0,0 +1,71 @@ +This is a testharness.js-based test. +Found 67 tests; 16 PASS, 51 FAIL, 0 TIMEOUT, 0 NOTRUN. +PASS e.style['text-decoration-line'] = "none" should set the property value +PASS e.style['text-decoration-line'] = "underline" should set the property value +PASS e.style['text-decoration-line'] = "overline" should set the property value +PASS e.style['text-decoration-line'] = "line-through" should set the property value +PASS e.style['text-decoration-line'] = "blink" should set the property value +PASS e.style['text-decoration-line'] = "underline overline" should set the property value +FAIL e.style['text-decoration-line'] = "overline underline" should set the property value assert_equals: serialization should be canonical expected "underline overline" but got "overline underline" +PASS e.style['text-decoration-line'] = "underline line-through" should set the property value +FAIL e.style['text-decoration-line'] = "line-through underline" should set the property value assert_equals: serialization should be canonical expected "underline line-through" but got "line-through underline" +PASS e.style['text-decoration-line'] = "underline blink" should set the property value +FAIL e.style['text-decoration-line'] = "blink underline" should set the property value assert_equals: serialization should be canonical expected "underline blink" but got "blink underline" +PASS e.style['text-decoration-line'] = "overline line-through" should set the property value +FAIL e.style['text-decoration-line'] = "line-through overline" should set the property value assert_equals: serialization should be canonical expected "overline line-through" but got "line-through overline" +PASS e.style['text-decoration-line'] = "overline blink" should set the property value +FAIL e.style['text-decoration-line'] = "blink overline" should set the property value assert_equals: serialization should be canonical expected "overline blink" but got "blink overline" +PASS e.style['text-decoration-line'] = "line-through blink" should set the property value +FAIL e.style['text-decoration-line'] = "blink line-through" should set the property value assert_equals: serialization should be canonical expected "line-through blink" but got "blink line-through" +PASS e.style['text-decoration-line'] = "underline overline line-through" should set the property value +FAIL e.style['text-decoration-line'] = "underline line-through overline" should set the property value assert_equals: serialization should be canonical expected "underline overline line-through" but got "underline line-through overline" +FAIL e.style['text-decoration-line'] = "overline underline line-through" should set the property value assert_equals: serialization should be canonical expected "underline overline line-through" but got "overline underline line-through" +FAIL e.style['text-decoration-line'] = "overline line-through underline" should set the property value assert_equals: serialization should be canonical expected "underline overline line-through" but got "overline line-through underline" +FAIL e.style['text-decoration-line'] = "line-through underline overline" should set the property value assert_equals: serialization should be canonical expected "underline overline line-through" but got "line-through underline overline" +FAIL e.style['text-decoration-line'] = "line-through overline underline" should set the property value assert_equals: serialization should be canonical expected "underline overline line-through" but got "line-through overline underline" +PASS e.style['text-decoration-line'] = "underline overline blink" should set the property value +FAIL e.style['text-decoration-line'] = "underline blink overline" should set the property value assert_equals: serialization should be canonical expected "underline overline blink" but got "underline blink overline" +FAIL e.style['text-decoration-line'] = "overline underline blink" should set the property value assert_equals: serialization should be canonical expected "underline overline blink" but got "overline underline blink" +FAIL e.style['text-decoration-line'] = "overline blink underline" should set the property value assert_equals: serialization should be canonical expected "underline overline blink" but got "overline blink underline" +FAIL e.style['text-decoration-line'] = "blink underline overline" should set the property value assert_equals: serialization should be canonical expected "underline overline blink" but got "blink underline overline" +FAIL e.style['text-decoration-line'] = "blink overline underline" should set the property value assert_equals: serialization should be canonical expected "underline overline blink" but got "blink overline underline" +PASS e.style['text-decoration-line'] = "underline line-through blink" should set the property value +FAIL e.style['text-decoration-line'] = "underline blink line-through" should set the property value assert_equals: serialization should be canonical expected "underline line-through blink" but got "underline blink line-through" +FAIL e.style['text-decoration-line'] = "line-through underline blink" should set the property value assert_equals: serialization should be canonical expected "underline line-through blink" but got "line-through underline blink" +FAIL e.style['text-decoration-line'] = "line-through blink underline" should set the property value assert_equals: serialization should be canonical expected "underline line-through blink" but got "line-through blink underline" +FAIL e.style['text-decoration-line'] = "blink underline line-through" should set the property value assert_equals: serialization should be canonical expected "underline line-through blink" but got "blink underline line-through" +FAIL e.style['text-decoration-line'] = "blink line-through underline" should set the property value assert_equals: serialization should be canonical expected "underline line-through blink" but got "blink line-through underline" +PASS e.style['text-decoration-line'] = "overline line-through blink" should set the property value +FAIL e.style['text-decoration-line'] = "overline blink line-through" should set the property value assert_equals: serialization should be canonical expected "overline line-through blink" but got "overline blink line-through" +FAIL e.style['text-decoration-line'] = "line-through overline blink" should set the property value assert_equals: serialization should be canonical expected "overline line-through blink" but got "line-through overline blink" +FAIL e.style['text-decoration-line'] = "line-through blink overline" should set the property value assert_equals: serialization should be canonical expected "overline line-through blink" but got "line-through blink overline" +FAIL e.style['text-decoration-line'] = "blink overline line-through" should set the property value assert_equals: serialization should be canonical expected "overline line-through blink" but got "blink overline line-through" +FAIL e.style['text-decoration-line'] = "blink line-through overline" should set the property value assert_equals: serialization should be canonical expected "overline line-through blink" but got "blink line-through overline" +PASS e.style['text-decoration-line'] = "underline overline line-through blink" should set the property value +FAIL e.style['text-decoration-line'] = "underline overline blink line-through" should set the property value assert_equals: serialization should be canonical expected "underline overline line-through blink" but got "underline overline blink line-through" +FAIL e.style['text-decoration-line'] = "underline line-through overline blink" should set the property value assert_equals: serialization should be canonical expected "underline overline line-through blink" but got "underline line-through overline blink" +FAIL e.style['text-decoration-line'] = "underline line-through blink overline" should set the property value assert_equals: serialization should be canonical expected "underline overline line-through blink" but got "underline line-through blink overline" +FAIL e.style['text-decoration-line'] = "underline blink overline line-through" should set the property value assert_equals: serialization should be canonical expected "underline overline line-through blink" but got "underline blink overline line-through" +FAIL e.style['text-decoration-line'] = "underline blink line-through overline" should set the property value assert_equals: serialization should be canonical expected "underline overline line-through blink" but got "underline blink line-through overline" +FAIL e.style['text-decoration-line'] = "overline underline line-through blink" should set the property value assert_equals: serialization should be canonical expected "underline overline line-through blink" but got "overline underline line-through blink" +FAIL e.style['text-decoration-line'] = "overline underline blink line-through" should set the property value assert_equals: serialization should be canonical expected "underline overline line-through blink" but got "overline underline blink line-through" +FAIL e.style['text-decoration-line'] = "overline line-through underline blink" should set the property value assert_equals: serialization should be canonical expected "underline overline line-through blink" but got "overline line-through underline blink" +FAIL e.style['text-decoration-line'] = "overline line-through blink underline" should set the property value assert_equals: serialization should be canonical expected "underline overline line-through blink" but got "overline line-through blink underline" +FAIL e.style['text-decoration-line'] = "overline blink underline line-through" should set the property value assert_equals: serialization should be canonical expected "underline overline line-through blink" but got "overline blink underline line-through" +FAIL e.style['text-decoration-line'] = "overline blink line-through underline" should set the property value assert_equals: serialization should be canonical expected "underline overline line-through blink" but got "overline blink line-through underline" +FAIL e.style['text-decoration-line'] = "line-through underline overline blink" should set the property value assert_equals: serialization should be canonical expected "underline overline line-through blink" but got "line-through underline overline blink" +FAIL e.style['text-decoration-line'] = "line-through underline blink overline" should set the property value assert_equals: serialization should be canonical expected "underline overline line-through blink" but got "line-through underline blink overline" +FAIL e.style['text-decoration-line'] = "line-through overline underline blink" should set the property value assert_equals: serialization should be canonical expected "underline overline line-through blink" but got "line-through overline underline blink" +FAIL e.style['text-decoration-line'] = "line-through overline blink underline" should set the property value assert_equals: serialization should be canonical expected "underline overline line-through blink" but got "line-through overline blink underline" +FAIL e.style['text-decoration-line'] = "line-through blink underline overline" should set the property value assert_equals: serialization should be canonical expected "underline overline line-through blink" but got "line-through blink underline overline" +FAIL e.style['text-decoration-line'] = "line-through blink overline underline" should set the property value assert_equals: serialization should be canonical expected "underline overline line-through blink" but got "line-through blink overline underline" +FAIL e.style['text-decoration-line'] = "blink underline overline line-through" should set the property value assert_equals: serialization should be canonical expected "underline overline line-through blink" but got "blink underline overline line-through" +FAIL e.style['text-decoration-line'] = "blink underline line-through overline" should set the property value assert_equals: serialization should be canonical expected "underline overline line-through blink" but got "blink underline line-through overline" +FAIL e.style['text-decoration-line'] = "blink overline underline line-through" should set the property value assert_equals: serialization should be canonical expected "underline overline line-through blink" but got "blink overline underline line-through" +FAIL e.style['text-decoration-line'] = "blink overline line-through underline" should set the property value assert_equals: serialization should be canonical expected "underline overline line-through blink" but got "blink overline line-through underline" +FAIL e.style['text-decoration-line'] = "blink line-through underline overline" should set the property value assert_equals: serialization should be canonical expected "underline overline line-through blink" but got "blink line-through underline overline" +FAIL e.style['text-decoration-line'] = "blink line-through overline underline" should set the property value assert_equals: serialization should be canonical expected "underline overline line-through blink" but got "blink line-through overline underline" +FAIL e.style['text-decoration-line'] = "spelling-error" should set the property value assert_not_equals: property should be set got disallowed value "" +FAIL e.style['text-decoration-line'] = "grammar-error" should set the property value assert_not_equals: property should be set got disallowed value "" +Harness: the test ran to completion. +
diff --git a/third_party/blink/web_tests/external/wpt/css/css-text-decor/parsing/text-decoration-line-valid.html b/third_party/blink/web_tests/external/wpt/css/css-text-decor/parsing/text-decoration-line-valid.html new file mode 100644 index 0000000..3dd2d0c8 --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/css/css-text-decor/parsing/text-decoration-line-valid.html
@@ -0,0 +1,86 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>CSS Text Decoration Test: Parsing text-decoration-line with valid values</title> +<link rel="author" title="Manuel Rego Casasnovas" href="mailto:rego@igalia.com"> +<link rel="help" href="https://drafts.csswg.org/css-text-decor-4/#text-decoration-line-property"> +<meta name="assert" content="text-decoration-line supports the full grammar 'none | [ underline || overline || line-through || blink ] | spelling-error | grammar-error'."> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/css/support/parsing-testcommon.js"></script> +<script> +// none +test_valid_value("text-decoration-line", "none"); + +// underline || overline || line-through || blink +test_valid_value("text-decoration-line", "underline"); +test_valid_value("text-decoration-line", "overline"); +test_valid_value("text-decoration-line", "line-through"); +test_valid_value("text-decoration-line", "blink"); +test_valid_value("text-decoration-line", "underline overline"); +test_valid_value("text-decoration-line", "overline underline", "underline overline"); +test_valid_value("text-decoration-line", "underline line-through"); +test_valid_value("text-decoration-line", "line-through underline", "underline line-through"); +test_valid_value("text-decoration-line", "underline blink"); +test_valid_value("text-decoration-line", "blink underline", "underline blink"); +test_valid_value("text-decoration-line", "overline line-through"); +test_valid_value("text-decoration-line", "line-through overline", "overline line-through"); +test_valid_value("text-decoration-line", "overline blink"); +test_valid_value("text-decoration-line", "blink overline", "overline blink"); +test_valid_value("text-decoration-line", "line-through blink"); +test_valid_value("text-decoration-line", "blink line-through", "line-through blink"); +test_valid_value("text-decoration-line", "underline overline line-through"); +test_valid_value("text-decoration-line", "underline line-through overline", "underline overline line-through"); +test_valid_value("text-decoration-line", "overline underline line-through", "underline overline line-through"); +test_valid_value("text-decoration-line", "overline line-through underline", "underline overline line-through"); +test_valid_value("text-decoration-line", "line-through underline overline", "underline overline line-through"); +test_valid_value("text-decoration-line", "line-through overline underline", "underline overline line-through"); +test_valid_value("text-decoration-line", "underline overline blink"); +test_valid_value("text-decoration-line", "underline blink overline", "underline overline blink"); +test_valid_value("text-decoration-line", "overline underline blink", "underline overline blink"); +test_valid_value("text-decoration-line", "overline blink underline", "underline overline blink"); +test_valid_value("text-decoration-line", "blink underline overline", "underline overline blink"); +test_valid_value("text-decoration-line", "blink overline underline", "underline overline blink"); +test_valid_value("text-decoration-line", "underline line-through blink"); +test_valid_value("text-decoration-line", "underline blink line-through", "underline line-through blink"); +test_valid_value("text-decoration-line", "line-through underline blink", "underline line-through blink"); +test_valid_value("text-decoration-line", "line-through blink underline", "underline line-through blink"); +test_valid_value("text-decoration-line", "blink underline line-through", "underline line-through blink"); +test_valid_value("text-decoration-line", "blink line-through underline", "underline line-through blink"); +test_valid_value("text-decoration-line", "overline line-through blink"); +test_valid_value("text-decoration-line", "overline blink line-through", "overline line-through blink"); +test_valid_value("text-decoration-line", "line-through overline blink", "overline line-through blink"); +test_valid_value("text-decoration-line", "line-through blink overline", "overline line-through blink"); +test_valid_value("text-decoration-line", "blink overline line-through", "overline line-through blink"); +test_valid_value("text-decoration-line", "blink line-through overline", "overline line-through blink"); +test_valid_value("text-decoration-line", "underline overline line-through blink"); +test_valid_value("text-decoration-line", "underline overline blink line-through", "underline overline line-through blink"); +test_valid_value("text-decoration-line", "underline line-through overline blink", "underline overline line-through blink"); +test_valid_value("text-decoration-line", "underline line-through blink overline", "underline overline line-through blink"); +test_valid_value("text-decoration-line", "underline blink overline line-through", "underline overline line-through blink"); +test_valid_value("text-decoration-line", "underline blink line-through overline", "underline overline line-through blink"); +test_valid_value("text-decoration-line", "overline underline line-through blink", "underline overline line-through blink"); +test_valid_value("text-decoration-line", "overline underline blink line-through", "underline overline line-through blink"); +test_valid_value("text-decoration-line", "overline line-through underline blink", "underline overline line-through blink"); +test_valid_value("text-decoration-line", "overline line-through blink underline", "underline overline line-through blink"); +test_valid_value("text-decoration-line", "overline blink underline line-through", "underline overline line-through blink"); +test_valid_value("text-decoration-line", "overline blink line-through underline", "underline overline line-through blink"); +test_valid_value("text-decoration-line", "line-through underline overline blink", "underline overline line-through blink"); +test_valid_value("text-decoration-line", "line-through underline blink overline", "underline overline line-through blink"); +test_valid_value("text-decoration-line", "line-through overline underline blink", "underline overline line-through blink"); +test_valid_value("text-decoration-line", "line-through overline blink underline", "underline overline line-through blink"); +test_valid_value("text-decoration-line", "line-through blink underline overline", "underline overline line-through blink"); +test_valid_value("text-decoration-line", "line-through blink overline underline", "underline overline line-through blink"); +test_valid_value("text-decoration-line", "blink underline overline line-through", "underline overline line-through blink"); +test_valid_value("text-decoration-line", "blink underline line-through overline", "underline overline line-through blink"); +test_valid_value("text-decoration-line", "blink overline underline line-through", "underline overline line-through blink"); +test_valid_value("text-decoration-line", "blink overline line-through underline", "underline overline line-through blink"); +test_valid_value("text-decoration-line", "blink line-through underline overline", "underline overline line-through blink"); +test_valid_value("text-decoration-line", "blink line-through overline underline", "underline overline line-through blink"); + +// spelling-error +test_valid_value("text-decoration-line", "spelling-error"); + +// grammar-error +test_valid_value("text-decoration-line", "grammar-error"); + +</script>
diff --git a/third_party/blink/web_tests/external/wpt/web-animations/interfaces/Animation/style-change-events-expected.txt b/third_party/blink/web_tests/external/wpt/web-animations/interfaces/Animation/style-change-events-expected.txt index 3826b238..89e308f 100644 --- a/third_party/blink/web_tests/external/wpt/web-animations/interfaces/Animation/style-change-events-expected.txt +++ b/third_party/blink/web_tests/external/wpt/web-animations/interfaces/Animation/style-change-events-expected.txt
@@ -1,5 +1,6 @@ This is a testharness.js-based test. -FAIL All property keys are recognized assert_in_array: Test property 'pending' should be one of the properties on Animation value "pending" not in array ["startTime", "currentTime", "playbackRate", "playState", "id", "onfinish", "oncancel", "finish", "play", "pause", "reverse", "cancel", "finished", "ready", "timeline", "effect", "Animation constructor"] +FAIL All property keys are recognized assert_in_array: Test property 'pending' should be one of the properties on Animation value "pending" not in array ["effect", "startTime", "currentTime", "playbackRate", "playState", "id", "onfinish", "oncancel", "finish", "play", "pause", "reverse", "cancel", "finished", "ready", "timeline", "Animation constructor"] +PASS Animation.effect does NOT trigger a style change event PASS Animation.startTime does NOT trigger a style change event PASS Animation.currentTime does NOT trigger a style change event PASS Animation.playbackRate does NOT trigger a style change event @@ -15,7 +16,6 @@ PASS Animation.finished does NOT trigger a style change event PASS Animation.ready does NOT trigger a style change event FAIL Animation.timeline does NOT trigger a style change event promise_test: Unhandled rejection with value: object "TypeError: Cannot assign to read only property 'timeline' of object '#<Animation>'" -PASS Animation.effect does NOT trigger a style change event PASS Animation.Animation constructor does NOT trigger a style change event Harness: the test ran to completion.
diff --git a/third_party/blink/web_tests/external/wpt/web-animations/timing-model/animation-effects/current-iteration-expected.txt b/third_party/blink/web_tests/external/wpt/web-animations/timing-model/animation-effects/current-iteration-expected.txt deleted file mode 100644 index 06c302aa..0000000 --- a/third_party/blink/web_tests/external/wpt/web-animations/timing-model/animation-effects/current-iteration-expected.txt +++ /dev/null
@@ -1,55 +0,0 @@ -This is a testharness.js-based test. -Found 51 tests; 41 PASS, 10 FAIL, 0 TIMEOUT, 0 NOTRUN. -PASS Test currentIteration during before and after phase when fill is none -PASS Test zero iterations: iterations:0 iterationStart:0 duration:0 delay:1 fill:both -PASS Test zero iterations: iterations:0 iterationStart:0 duration:100 delay:1 fill:both -PASS Test zero iterations: iterations:0 iterationStart:0 duration:Infinity delay:1 fill:both -PASS Test zero iterations: iterations:0 iterationStart:2.5 duration:0 delay:1 fill:both -PASS Test zero iterations: iterations:0 iterationStart:2.5 duration:100 delay:1 fill:both -FAIL Test zero iterations: iterations:0 iterationStart:2.5 duration:Infinity delay:1 fill:both assert_equals: Value of currentIteration in the before phase expected 2 but got 1.5 -PASS Test zero iterations: iterations:0 iterationStart:3 duration:0 delay:1 fill:both -PASS Test zero iterations: iterations:0 iterationStart:3 duration:100 delay:1 fill:both -FAIL Test zero iterations: iterations:0 iterationStart:3 duration:Infinity delay:1 fill:both assert_equals: Value of currentIteration in the before phase expected 3 but got 2 -PASS Test integer iterations: iterations:3 iterationStart:0 duration:0 delay:1 fill:both -PASS Test integer iterations: iterations:3 iterationStart:0 duration:100 delay:1 fill:both -PASS Test integer iterations: iterations:3 iterationStart:0 duration:Infinity delay:1 fill:both -PASS Test integer iterations: iterations:3 iterationStart:2.5 duration:0 delay:1 fill:both -PASS Test integer iterations: iterations:3 iterationStart:2.5 duration:100 delay:1 fill:both -FAIL Test integer iterations: iterations:3 iterationStart:2.5 duration:Infinity delay:1 fill:both assert_equals: Value of currentIteration in the before phase expected 2 but got 4.5 -PASS Test integer iterations: iterations:3 iterationStart:3 duration:0 delay:1 fill:both -PASS Test integer iterations: iterations:3 iterationStart:3 duration:100 delay:1 fill:both -FAIL Test integer iterations: iterations:3 iterationStart:3 duration:Infinity delay:1 fill:both assert_equals: Value of currentIteration in the before phase expected 3 but got 5 -PASS Test fractional iterations: iterations:3.5 iterationStart:0 duration:0 delay:1 fill:both -PASS Test fractional iterations: iterations:3.5 iterationStart:0 duration:100 delay:1 fill:both -PASS Test fractional iterations: iterations:3.5 iterationStart:0 duration:Infinity delay:1 fill:both -PASS Test fractional iterations: iterations:3.5 iterationStart:2.5 duration:0 delay:1 fill:both -FAIL Test fractional iterations: iterations:3.5 iterationStart:2.5 duration:100 delay:1 fill:both assert_equals: Value of currentIteration in the after phase expected 5 but got 6 -FAIL Test fractional iterations: iterations:3.5 iterationStart:2.5 duration:Infinity delay:1 fill:both assert_equals: Value of currentIteration in the before phase expected 2 but got 5 -PASS Test fractional iterations: iterations:3.5 iterationStart:3 duration:0 delay:1 fill:both -PASS Test fractional iterations: iterations:3.5 iterationStart:3 duration:100 delay:1 fill:both -FAIL Test fractional iterations: iterations:3.5 iterationStart:3 duration:Infinity delay:1 fill:both assert_equals: Value of currentIteration in the before phase expected 3 but got 5.5 -PASS Test infinity iterations: iterations:Infinity iterationStart:0 duration:0 delay:1 fill:both -PASS Test infinity iterations: iterations:Infinity iterationStart:0 duration:100 delay:1 fill:both -PASS Test infinity iterations: iterations:Infinity iterationStart:0 duration:Infinity delay:1 fill:both -PASS Test infinity iterations: iterations:Infinity iterationStart:2.5 duration:0 delay:1 fill:both -PASS Test infinity iterations: iterations:Infinity iterationStart:2.5 duration:100 delay:1 fill:both -FAIL Test infinity iterations: iterations:Infinity iterationStart:2.5 duration:Infinity delay:1 fill:both assert_equals: Value of currentIteration in the before phase expected 2 but got Infinity -PASS Test infinity iterations: iterations:Infinity iterationStart:3 duration:0 delay:1 fill:both -PASS Test infinity iterations: iterations:Infinity iterationStart:3 duration:100 delay:1 fill:both -FAIL Test infinity iterations: iterations:Infinity iterationStart:3 duration:Infinity delay:1 fill:both assert_equals: Value of currentIteration in the before phase expected 3 but got Infinity -PASS Test end delay: duration:100 delay:1 fill:both endDelay:50 -PASS Test end delay: duration:100 delay:1 fill:both endDelay:-50 -PASS Test end delay: duration:100 delay:1 fill:both endDelay:-100 -PASS Test end delay: duration:100 delay:1 fill:both endDelay:-200 -PASS Test end delay: iterationStart:0.5 duration:100 delay:1 fill:both endDelay:50 -FAIL Test end delay: iterationStart:0.5 duration:100 delay:1 fill:both endDelay:-50 assert_equals: Value of currentIteration in the after phase expected 1 but got 0.5 -PASS Test end delay: iterationStart:0.5 duration:100 delay:1 fill:both endDelay:-100 -PASS Test end delay: iterations:2 duration:100 delay:1 fill:both endDelay:-100 -PASS Test end delay: iterations:1 iterationStart:2 duration:100 delay:1 fill:both endDelay:-50 -PASS Test end delay: iterations:1 iterationStart:2 duration:100 delay:1 fill:both endDelay:-100 -PASS Test negative playback rate: duration:1 delay:1 fill:both playbackRate:-1 -PASS Test negative playback rate: duration:1 delay:1 iterations:2 fill:both playbackRate:-1 -PASS Test negative playback rate: duration:0 delay:1 fill:both playbackRate:-1 -PASS Test negative playback rate: duration:0 iterations:0 delay:1 fill:both playbackRate:-1 -Harness: the test ran to completion. -
diff --git a/third_party/blink/web_tests/fast/events/popup-blocked-to-post-blank-expected.txt b/third_party/blink/web_tests/fast/events/popup-blocked-to-post-blank-expected.txt index 1c6ee54..5305a4e3 100644 --- a/third_party/blink/web_tests/fast/events/popup-blocked-to-post-blank-expected.txt +++ b/third_party/blink/web_tests/fast/events/popup-blocked-to-post-blank-expected.txt
@@ -1,2 +1 @@ -CONSOLE ERROR: line 15: Not allowed to navigate top frame to data URL: data:text/html,<script>alert(window)</script>? If the POST pop-up was not blocked then there will be an ALERT containing a Window object. Otherwise, the test passes.
diff --git a/third_party/blink/web_tests/fast/frames/sandboxed-iframe-navigation-targetlink-expected.txt b/third_party/blink/web_tests/fast/frames/sandboxed-iframe-navigation-targetlink-expected.txt index cbcb138..06a635cf 100644 --- a/third_party/blink/web_tests/fast/frames/sandboxed-iframe-navigation-targetlink-expected.txt +++ b/third_party/blink/web_tests/fast/frames/sandboxed-iframe-navigation-targetlink-expected.txt
@@ -1,5 +1,6 @@ CONSOLE ERROR: line 18: Unsafe JavaScript attempt to initiate navigation for frame with URL 'about:blank' from frame with URL 'sandboxed-iframe-navigation-targetlink.html'. The frame attempting navigation is sandboxed, and is therefore disallowed from navigating its ancestors. +CONSOLE ERROR: line 18: Blocked opening 'sandboxed-iframe-navigated.html' in a new window because the request was made in a sandboxed frame whose 'allow-popups' permission is not set. This test verifies that a sandboxed IFrame cannot open a link in another frame using the target attribute of a link. This is done by loading ten non-sandboxed IFrames, and a single sandboxed one. In addition each of these frames have a target frame (so, 22 frames in total). Expect ten frames to be able to open a link in their corresponding target frame, but the sandboxed one to not be one of them.
diff --git a/third_party/blink/web_tests/http/tests/security/sandboxed-iframe-form-top-expected.txt b/third_party/blink/web_tests/http/tests/security/sandboxed-iframe-form-top-expected.txt index de8060f..ad4c764 100644 --- a/third_party/blink/web_tests/http/tests/security/sandboxed-iframe-form-top-expected.txt +++ b/third_party/blink/web_tests/http/tests/security/sandboxed-iframe-form-top-expected.txt
@@ -1,5 +1,6 @@ CONSOLE ERROR: line 8: Unsafe JavaScript attempt to initiate navigation for frame with URL 'http://127.0.0.1:8000/security/sandboxed-iframe-form-top.html' from frame with URL 'http://127.0.0.1:8000/security/resources/sandboxed-iframe-form-top.html'. The frame attempting navigation of the top-level window is sandboxed, but the flag of 'allow-top-navigation' or 'allow-top-navigation-by-user-activation' is not set. +CONSOLE ERROR: line 8: Blocked opening 'http://127.0.0.1:8000/security/resources/fail.html?' in a new window because the request was made in a sandboxed frame whose 'allow-popups' permission is not set. This tests passes if the sandboxed frame cannot navigate the top frame. PASS
diff --git a/third_party/blink/web_tests/virtual/mouseevent_fractional/fast/events/popup-blocked-to-post-blank-expected.txt b/third_party/blink/web_tests/virtual/mouseevent_fractional/fast/events/popup-blocked-to-post-blank-expected.txt deleted file mode 100644 index 1c6ee54..0000000 --- a/third_party/blink/web_tests/virtual/mouseevent_fractional/fast/events/popup-blocked-to-post-blank-expected.txt +++ /dev/null
@@ -1,2 +0,0 @@ -CONSOLE ERROR: line 15: Not allowed to navigate top frame to data URL: data:text/html,<script>alert(window)</script>? -If the POST pop-up was not blocked then there will be an ALERT containing a Window object. Otherwise, the test passes.
diff --git a/third_party/blink/web_tests/virtual/stable/webexposed/global-interface-listing-expected.txt b/third_party/blink/web_tests/virtual/stable/webexposed/global-interface-listing-expected.txt index 77e5379..461e561 100644 --- a/third_party/blink/web_tests/virtual/stable/webexposed/global-interface-listing-expected.txt +++ b/third_party/blink/web_tests/virtual/stable/webexposed/global-interface-listing-expected.txt
@@ -42,6 +42,7 @@ interface Animation : EventTarget attribute @@toStringTag getter currentTime + getter effect getter id getter oncancel getter onfinish @@ -55,11 +56,18 @@ method play method reverse setter currentTime + setter effect setter id setter oncancel setter onfinish setter playbackRate setter startTime +interface AnimationEffect + attribute @@toStringTag + method constructor + method getComputedTiming + method getTiming + method updateTiming interface AnimationEvent : Event attribute @@toStringTag getter animationName @@ -3726,6 +3734,11 @@ method has method keys method values +interface KeyframeEffect : AnimationEffect + attribute @@toStringTag + getter target + method constructor + setter target interface LinearAccelerationSensor : Accelerometer attribute @@toStringTag method constructor
diff --git a/third_party/blink/web_tests/virtual/user-activation-v2/fast/events/popup-blocked-to-post-blank-expected.txt b/third_party/blink/web_tests/virtual/user-activation-v2/fast/events/popup-blocked-to-post-blank-expected.txt deleted file mode 100644 index 1c6ee54..0000000 --- a/third_party/blink/web_tests/virtual/user-activation-v2/fast/events/popup-blocked-to-post-blank-expected.txt +++ /dev/null
@@ -1,2 +0,0 @@ -CONSOLE ERROR: line 15: Not allowed to navigate top frame to data URL: data:text/html,<script>alert(window)</script>? -If the POST pop-up was not blocked then there will be an ALERT containing a Window object. Otherwise, the test passes.
diff --git a/third_party/inspector_protocol/README.chromium b/third_party/inspector_protocol/README.chromium index 0b53c5a..3aa6bb47 100644 --- a/third_party/inspector_protocol/README.chromium +++ b/third_party/inspector_protocol/README.chromium
@@ -2,7 +2,7 @@ Short Name: inspector_protocol URL: https://chromium.googlesource.com/deps/inspector_protocol/ Version: 0 -Revision: 454b6bcf7d08535681bbd967030f5248bcbc8e6a +Revision: a8dc93daa67c3bfad9c56aa9641ab2f3bc6d3fc0 License: BSD License File: LICENSE Security Critical: no
diff --git a/third_party/inspector_protocol/encoding/encoding.cc b/third_party/inspector_protocol/encoding/encoding.cc index e84ceda..ceda4437 100644 --- a/third_party/inspector_protocol/encoding/encoding.cc +++ b/third_party/inspector_protocol/encoding/encoding.cc
@@ -80,8 +80,8 @@ // Writes the bytes for |v| to |out|, starting with the most significant byte. // See also: https://commandcenter.blogspot.com/2012/04/byte-order-fallacy.html -template <typename T> -void WriteBytesMostSignificantByteFirst(T v, std::vector<uint8_t>* out) { +template <typename T, class C> +void WriteBytesMostSignificantByteFirst(T v, C* out) { for (int shift_bytes = sizeof(T) - 1; shift_bytes >= 0; --shift_bytes) out->push_back(0xff & (v >> (shift_bytes * 8))); } @@ -151,9 +151,8 @@ // Writes the start of a token with |type|. The |value| may indicate the size, // or it may be the payload if the value is an unsigned integer. -void WriteTokenStart(MajorType type, - uint64_t value, - std::vector<uint8_t>* encoded) { +template <class C> +void WriteTokenStartTmpl(MajorType type, uint64_t value, C* encoded) { if (value < 24) { // Values 0-23 are encoded directly into the additional info of the // initial byte. @@ -183,6 +182,14 @@ encoded->push_back(EncodeInitialByte(type, kAdditionalInformation8Bytes)); WriteBytesMostSignificantByteFirst<uint64_t>(value, encoded); } +void WriteTokenStart(MajorType type, + uint64_t value, + std::vector<uint8_t>* encoded) { + WriteTokenStartTmpl(type, value, encoded); +} +void WriteTokenStart(MajorType type, uint64_t value, std::string* encoded) { + WriteTokenStartTmpl(type, value, encoded); +} } // namespace internals // ============================================================================= @@ -226,7 +233,8 @@ return kStopByte; } -void EncodeInt32(int32_t value, std::vector<uint8_t>* out) { +template <class C> +void EncodeInt32Tmpl(int32_t value, C* out) { if (value >= 0) { internals::WriteTokenStart(MajorType::UNSIGNED, value, out); } else { @@ -234,8 +242,15 @@ internals::WriteTokenStart(MajorType::NEGATIVE, representation, out); } } +void EncodeInt32(int32_t value, std::vector<uint8_t>* out) { + EncodeInt32Tmpl(value, out); +} +void EncodeInt32(int32_t value, std::string* out) { + EncodeInt32Tmpl(value, out); +} -void EncodeString16(span<uint16_t> in, std::vector<uint8_t>* out) { +template <class C> +void EncodeString16Tmpl(span<uint16_t> in, C* out) { uint64_t byte_length = static_cast<uint64_t>(in.size_bytes()); internals::WriteTokenStart(MajorType::BYTE_STRING, byte_length, out); // When emitting UTF16 characters, we always write the least significant byte @@ -252,14 +267,28 @@ out->push_back(two_bytes >> 8); } } +void EncodeString16(span<uint16_t> in, std::vector<uint8_t>* out) { + EncodeString16Tmpl(in, out); +} +void EncodeString16(span<uint16_t> in, std::string* out) { + EncodeString16Tmpl(in, out); +} -void EncodeString8(span<uint8_t> in, std::vector<uint8_t>* out) { +template <class C> +void EncodeString8Tmpl(span<uint8_t> in, C* out) { internals::WriteTokenStart(MajorType::STRING, static_cast<uint64_t>(in.size_bytes()), out); out->insert(out->end(), in.begin(), in.end()); } +void EncodeString8(span<uint8_t> in, std::vector<uint8_t>* out) { + EncodeString8Tmpl(in, out); +} +void EncodeString8(span<uint8_t> in, std::string* out) { + EncodeString8Tmpl(in, out); +} -void EncodeFromLatin1(span<uint8_t> latin1, std::vector<uint8_t>* out) { +template <class C> +void EncodeFromLatin1Tmpl(span<uint8_t> latin1, C* out) { for (std::ptrdiff_t ii = 0; ii < latin1.size(); ++ii) { if (latin1[ii] <= 127) continue; @@ -279,8 +308,15 @@ } EncodeString8(latin1, out); } +void EncodeFromLatin1(span<uint8_t> latin1, std::vector<uint8_t>* out) { + EncodeFromLatin1Tmpl(latin1, out); +} +void EncodeFromLatin1(span<uint8_t> latin1, std::string* out) { + EncodeFromLatin1Tmpl(latin1, out); +} -void EncodeFromUTF16(span<uint16_t> utf16, std::vector<uint8_t>* out) { +template <class C> +void EncodeFromUTF16Tmpl(span<uint16_t> utf16, C* out) { // If there's at least one non-ASCII char, encode as STRING16 (UTF16). for (uint16_t ch : utf16) { if (ch <= 127) @@ -293,13 +329,26 @@ static_cast<uint64_t>(utf16.size()), out); out->insert(out->end(), utf16.begin(), utf16.end()); } +void EncodeFromUTF16(span<uint16_t> utf16, std::vector<uint8_t>* out) { + EncodeFromUTF16Tmpl(utf16, out); +} +void EncodeFromUTF16(span<uint16_t> utf16, std::string* out) { + EncodeFromUTF16Tmpl(utf16, out); +} -void EncodeBinary(span<uint8_t> in, std::vector<uint8_t>* out) { +template <class C> +void EncodeBinaryTmpl(span<uint8_t> in, C* out) { out->push_back(kExpectedConversionToBase64Tag); uint64_t byte_length = static_cast<uint64_t>(in.size_bytes()); internals::WriteTokenStart(MajorType::BYTE_STRING, byte_length, out); out->insert(out->end(), in.begin(), in.end()); } +void EncodeBinary(span<uint8_t> in, std::vector<uint8_t>* out) { + EncodeBinaryTmpl(in, out); +} +void EncodeBinary(span<uint8_t> in, std::string* out) { + EncodeBinaryTmpl(in, out); +} // A double is encoded with a specific initial byte // (kInitialByteForDouble) plus the 64 bits of payload for its value. @@ -310,7 +359,8 @@ // bit wide length, plus a 32 bit length for that string. constexpr std::ptrdiff_t kEncodedEnvelopeHeaderSize = 1 + 1 + sizeof(uint32_t); -void EncodeDouble(double value, std::vector<uint8_t>* out) { +template <class C> +void EncodeDoubleTmpl(double value, C* out) { // The additional_info=27 indicates 64 bits for the double follow. // See RFC 7049 Section 2.3, Table 1. out->push_back(kInitialByteForDouble); @@ -321,44 +371,68 @@ reinterpret.from_double = value; WriteBytesMostSignificantByteFirst<uint64_t>(reinterpret.to_uint64, out); } +void EncodeDouble(double value, std::vector<uint8_t>* out) { + EncodeDoubleTmpl(value, out); +} +void EncodeDouble(double value, std::string* out) { + EncodeDoubleTmpl(value, out); +} // ============================================================================= // cbor::EnvelopeEncoder - for wrapping submessages // ============================================================================= -void EnvelopeEncoder::EncodeStart(std::vector<uint8_t>* out) { - assert(byte_size_pos_ == 0); +template <class C> +void EncodeStartTmpl(C* out, std::size_t& byte_size_pos) { + assert(byte_size_pos == 0); out->push_back(kInitialByteForEnvelope); out->push_back(kInitialByteFor32BitLengthByteString); - byte_size_pos_ = out->size(); + byte_size_pos = out->size(); out->resize(out->size() + sizeof(uint32_t)); } -bool EnvelopeEncoder::EncodeStop(std::vector<uint8_t>* out) { - assert(byte_size_pos_ != 0); +void EnvelopeEncoder::EncodeStart(std::vector<uint8_t>* out) { + EncodeStartTmpl<std::vector<uint8_t>>(out, byte_size_pos_); +} + +void EnvelopeEncoder::EncodeStart(std::string* out) { + EncodeStartTmpl<std::string>(out, byte_size_pos_); +} + +template <class C> +bool EncodeStopTmpl(C* out, std::size_t& byte_size_pos) { + assert(byte_size_pos != 0); // The byte size is the size of the payload, that is, all the // bytes that were written past the byte size position itself. - uint64_t byte_size = out->size() - (byte_size_pos_ + sizeof(uint32_t)); + uint64_t byte_size = out->size() - (byte_size_pos + sizeof(uint32_t)); // We store exactly 4 bytes, so at most INT32MAX, with most significant // byte first. if (byte_size > std::numeric_limits<uint32_t>::max()) return false; for (int shift_bytes = sizeof(uint32_t) - 1; shift_bytes >= 0; --shift_bytes) { - (*out)[byte_size_pos_++] = 0xff & (byte_size >> (shift_bytes * 8)); + (*out)[byte_size_pos++] = 0xff & (byte_size >> (shift_bytes * 8)); } return true; } +bool EnvelopeEncoder::EncodeStop(std::vector<uint8_t>* out) { + return EncodeStopTmpl(out, byte_size_pos_); +} + +bool EnvelopeEncoder::EncodeStop(std::string* out) { + return EncodeStopTmpl(out, byte_size_pos_); +} + // ============================================================================= // cbor::NewCBOREncoder - for encoding from a streaming parser // ============================================================================= namespace { +template <class C> class CBOREncoder : public StreamingParserHandler { public: - CBOREncoder(std::vector<uint8_t>* out, Status* status) - : out_(out), status_(status) { + CBOREncoder(C* out, Status* status) : out_(out), status_(status) { *status_ = Status(); } @@ -419,7 +493,7 @@ } private: - std::vector<uint8_t>* out_; + C* out_; std::vector<EnvelopeEncoder> envelopes_; Status* status_; }; @@ -428,7 +502,13 @@ std::unique_ptr<StreamingParserHandler> NewCBOREncoder( std::vector<uint8_t>* out, Status* status) { - return std::unique_ptr<StreamingParserHandler>(new CBOREncoder(out, status)); + return std::unique_ptr<StreamingParserHandler>( + new CBOREncoder<std::vector<uint8_t>>(out, status)); +} +std::unique_ptr<StreamingParserHandler> NewCBOREncoder(std::string* out, + Status* status) { + return std::unique_ptr<StreamingParserHandler>( + new CBOREncoder<std::string>(out, status)); } // ============================================================================= @@ -861,10 +941,11 @@ namespace { // Prints |value| to |out| with 4 hex digits, most significant chunk first. -void PrintHex(uint16_t value, std::string* out) { +template <class C> +void PrintHex(uint16_t value, C* out) { for (int ii = 3; ii >= 0; --ii) { int four_bits = 0xf & (value >> (4 * ii)); - out->append(1, four_bits + ((four_bits <= 9) ? '0' : ('a' - 10))); + out->push_back(four_bits + ((four_bits <= 9) ? '0' : ('a' - 10))); } } @@ -882,6 +963,9 @@ class State { public: explicit State(Container container) : container_(container) {} + void StartElement(std::vector<uint8_t>* out) { + // FIXME!!! + } void StartElement(std::string* out) { assert(container_ != Container::NONE || size_ == 0); if (size_ != 0) { @@ -901,7 +985,8 @@ "ABCDEFGHIJKLMNOPQRSTUVWXYZ" "abcdefghijklmnopqrstuvwxyz0123456789+/"; -void Base64Encode(const span<uint8_t>& in, std::string* out) { +template <class C> +void Base64Encode(const span<uint8_t>& in, C* out) { // The following three cases are based on the tables in the example // section in https://en.wikipedia.org/wiki/Base64. We process three // input bytes at a time, emitting 4 output bytes at a time. @@ -933,9 +1018,10 @@ } // Implements a handler for JSON parser events to emit a JSON string. +template <class C> class JSONEncoder : public StreamingParserHandler { public: - JSONEncoder(const Platform* platform, std::string* out, Status* status) + JSONEncoder(const Platform* platform, C* out, Status* status) : platform_(platform), out_(out), status_(status) { *status_ = Status(); state_.emplace(Container::NONE); @@ -947,7 +1033,7 @@ assert(!state_.empty()); state_.top().StartElement(out_); state_.emplace(Container::MAP); - out_->append("{"); + Emit('{'); } void HandleMapEnd() override { @@ -955,7 +1041,7 @@ return; assert(state_.size() >= 2 && state_.top().container() == Container::MAP); state_.pop(); - out_->append("}"); + Emit('}'); } void HandleArrayBegin() override { @@ -963,7 +1049,7 @@ return; state_.top().StartElement(out_); state_.emplace(Container::ARRAY); - out_->append("["); + Emit('['); } void HandleArrayEnd() override { @@ -971,64 +1057,64 @@ return; assert(state_.size() >= 2 && state_.top().container() == Container::ARRAY); state_.pop(); - out_->append("]"); + Emit(']'); } void HandleString16(span<uint16_t> chars) override { if (!status_->ok()) return; state_.top().StartElement(out_); - out_->append("\""); + Emit('"'); for (const uint16_t ch : chars) { if (ch == '"') { - out_->append("\\\""); + Emit("\\\""); } else if (ch == '\\') { - out_->append("\\\\"); + Emit("\\\\"); } else if (ch == '\b') { - out_->append("\\b"); + Emit("\\b"); } else if (ch == '\f') { - out_->append("\\f"); + Emit("\\f"); } else if (ch == '\n') { - out_->append("\\n"); + Emit("\\n"); } else if (ch == '\r') { - out_->append("\\r"); + Emit("\\r"); } else if (ch == '\t') { - out_->append("\\t"); + Emit("\\t"); } else if (ch >= 32 && ch <= 126) { - out_->append(1, ch); + Emit(ch); } else { - out_->append("\\u"); + Emit("\\u"); PrintHex(ch, out_); } } - out_->append("\""); + Emit('"'); } void HandleString8(span<uint8_t> chars) override { if (!status_->ok()) return; state_.top().StartElement(out_); - out_->append("\""); + Emit('"'); for (std::ptrdiff_t ii = 0; ii < chars.size(); ++ii) { uint8_t c = chars[ii]; if (c == '"') { - out_->append("\\\""); + Emit("\\\""); } else if (c == '\\') { - out_->append("\\\\"); + Emit("\\\\"); } else if (c == '\b') { - out_->append("\\b"); + Emit("\\b"); } else if (c == '\f') { - out_->append("\\f"); + Emit("\\f"); } else if (c == '\n') { - out_->append("\\n"); + Emit("\\n"); } else if (c == '\r') { - out_->append("\\r"); + Emit("\\r"); } else if (c == '\t') { - out_->append("\\t"); + Emit("\\t"); } else if (c >= 32 && c <= 126) { - out_->append(1, c); + Emit(c); } else if (c < 32) { - out_->append("\\u"); + Emit("\\u"); PrintHex(static_cast<uint16_t>(c), out_); } else { // Inspect the leading byte to figure out how long the utf8 @@ -1079,29 +1165,29 @@ // using the math described at https://en.wikipedia.org/wiki/UTF-16, // for either one or two 16 bit characters. if (codepoint < 0xffff) { - out_->append("\\u"); + Emit("\\u"); PrintHex(static_cast<uint16_t>(codepoint), out_); continue; } codepoint -= 0x10000; // high surrogate - out_->append("\\u"); + Emit("\\u"); PrintHex(static_cast<uint16_t>((codepoint >> 10) + 0xd800), out_); // low surrogate - out_->append("\\u"); + Emit("\\u"); PrintHex(static_cast<uint16_t>((codepoint & 0x3ff) + 0xdc00), out_); } } - out_->append("\""); + Emit('"'); } void HandleBinary(span<uint8_t> bytes) override { if (!status_->ok()) return; state_.top().StartElement(out_); - out_->append("\""); + Emit('"'); Base64Encode(bytes, out_); - out_->append("\""); + Emit('"'); } void HandleDouble(double value) override { @@ -1111,7 +1197,7 @@ // JSON cannot represent NaN or Infinity. So, for compatibility, // we behave like the JSON object in web browsers: emit 'null'. if (!std::isfinite(value)) { - out_->append("null"); + Emit("null"); return; } std::unique_ptr<char[]> str_value = platform_->DToStr(value); @@ -1123,33 +1209,33 @@ // we probe for this and emit the leading 0 anyway if necessary. const char* chars = str_value.get(); if (chars[0] == '.') { - out_->append("0"); + Emit('0'); } else if (chars[0] == '-' && chars[1] == '.') { - out_->append("-0"); + Emit("-0"); ++chars; } - out_->append(chars); + Emit(chars); } void HandleInt32(int32_t value) override { if (!status_->ok()) return; state_.top().StartElement(out_); - out_->append(std::to_string(value)); + Emit(std::to_string(value)); } void HandleBool(bool value) override { if (!status_->ok()) return; state_.top().StartElement(out_); - out_->append(value ? "true" : "false"); + Emit(value ? "true" : "false"); } void HandleNull() override { if (!status_->ok()) return; state_.top().StartElement(out_); - out_->append("null"); + Emit("null"); } void HandleError(Status error) override { @@ -1159,18 +1245,33 @@ } private: + void Emit(char c) { out_->push_back(c); } + void Emit(const char* str) { + out_->insert(out_->end(), str, str + strlen(str)); + } + void Emit(const std::string& str) { + out_->insert(out_->end(), str.begin(), str.end()); + } + const Platform* platform_; - std::string* out_; + C* out_; Status* status_; std::stack<State> state_; }; } // namespace +std::unique_ptr<StreamingParserHandler> NewJSONEncoder( + const Platform* platform, + std::vector<uint8_t>* out, + Status* status) { + return std::unique_ptr<StreamingParserHandler>( + new JSONEncoder<std::vector<uint8_t>>(platform, out, status)); +} std::unique_ptr<StreamingParserHandler> NewJSONEncoder(const Platform* platform, std::string* out, Status* status) { return std::unique_ptr<StreamingParserHandler>( - new JSONEncoder(platform, out, status)); + new JSONEncoder<std::string>(platform, out, status)); } // ============================================================================= @@ -1786,18 +1887,64 @@ }; } // namespace -void ParseJSON(const Platform* platform, +void ParseJSON(const Platform& platform, span<uint8_t> chars, StreamingParserHandler* handler) { - JsonParser<uint8_t> parser(platform, handler); + JsonParser<uint8_t> parser(&platform, handler); parser.Parse(chars.data(), chars.size()); } -void ParseJSON(const Platform* platform, +void ParseJSON(const Platform& platform, span<uint16_t> chars, StreamingParserHandler* handler) { - JsonParser<uint16_t> parser(platform, handler); + JsonParser<uint16_t> parser(&platform, handler); parser.Parse(chars.data(), chars.size()); } + +// ============================================================================= +// json::ConvertCBORToJSON, json::ConvertJSONToCBOR - for transcoding +// ============================================================================= +template <class C> +Status ConvertCBORToJSONTmpl(const Platform& platform, + span<uint8_t> cbor, + C* json) { + Status status; + std::unique_ptr<StreamingParserHandler> json_writer = + NewJSONEncoder(&platform, json, &status); + cbor::ParseCBOR(cbor, json_writer.get()); + return status; +} + +Status ConvertCBORToJSON(const Platform& platform, + span<uint8_t> cbor, + std::vector<uint8_t>* json) { + return ConvertCBORToJSONTmpl(platform, cbor, json); +} +Status ConvertCBORToJSON(const Platform& platform, + span<uint8_t> cbor, + std::string* json) { + return ConvertCBORToJSONTmpl(platform, cbor, json); +} + +template <class C> +Status ConvertJSONToCBORTmpl(const Platform& platform, + span<uint8_t> json, + C* cbor) { + Status status; + std::unique_ptr<StreamingParserHandler> encoder = + cbor::NewCBOREncoder(cbor, &status); + ParseJSON(platform, json, encoder.get()); + return status; +} +Status ConvertJSONToCBOR(const Platform& platform, + span<uint8_t> json, + std::string* cbor) { + return ConvertJSONToCBORTmpl(platform, json, cbor); +} +Status ConvertJSONToCBOR(const Platform& platform, + span<uint8_t> json, + std::vector<uint8_t>* cbor) { + return ConvertJSONToCBORTmpl(platform, json, cbor); +} } // namespace json } // namespace inspector_protocol_encoding
diff --git a/third_party/inspector_protocol/encoding/encoding.h b/third_party/inspector_protocol/encoding/encoding.h index b958ff4..dc6dbfd 100644 --- a/third_party/inspector_protocol/encoding/encoding.h +++ b/third_party/inspector_protocol/encoding/encoding.h
@@ -189,32 +189,39 @@ // Encodes |value| as |UNSIGNED| (major type 0) iff >= 0, or |NEGATIVE| // (major type 1) iff < 0. void EncodeInt32(int32_t value, std::vector<uint8_t>* out); +void EncodeInt32(int32_t value, std::string* out); // Encodes a UTF16 string as a BYTE_STRING (major type 2). Each utf16 // character in |in| is emitted with most significant byte first, // appending to |out|. void EncodeString16(span<uint16_t> in, std::vector<uint8_t>* out); +void EncodeString16(span<uint16_t> in, std::string* out); // Encodes a UTF8 string |in| as STRING (major type 3). void EncodeString8(span<uint8_t> in, std::vector<uint8_t>* out); +void EncodeString8(span<uint8_t> in, std::string* out); // Encodes the given |latin1| string as STRING8. // If any non-ASCII character is present, it will be represented // as a 2 byte UTF8 sequence. void EncodeFromLatin1(span<uint8_t> latin1, std::vector<uint8_t>* out); +void EncodeFromLatin1(span<uint8_t> latin1, std::string* out); // Encodes the given |utf16| string as STRING8 if it's entirely US-ASCII. // Otherwise, encodes as STRING16. void EncodeFromUTF16(span<uint16_t> utf16, std::vector<uint8_t>* out); +void EncodeFromUTF16(span<uint16_t> utf16, std::string* out); // Encodes arbitrary binary data in |in| as a BYTE_STRING (major type 2) with // definitive length, prefixed with tag 22 indicating expected conversion to // base64 (see RFC 7049, Table 3 and Section 2.4.4.2). void EncodeBinary(span<uint8_t> in, std::vector<uint8_t>* out); +void EncodeBinary(span<uint8_t> in, std::string* out); // Encodes / decodes a double as Major type 7 (SIMPLE_VALUE), // with additional info = 27, followed by 8 bytes in big endian. void EncodeDouble(double value, std::vector<uint8_t>* out); +void EncodeDouble(double value, std::string* out); // ============================================================================= // cbor::EnvelopeEncoder - for wrapping submessages @@ -233,9 +240,11 @@ // byte size in |byte_size_pos_|. Also emits empty bytes for the // byte sisze so that encoding can continue. void EncodeStart(std::vector<uint8_t>* out); + void EncodeStart(std::string* out); // This records the current size in |out| at position byte_size_pos_. // Returns true iff successful. bool EncodeStop(std::vector<uint8_t>* out); + bool EncodeStop(std::string* out); private: std::size_t byte_size_pos_ = 0; @@ -252,6 +261,8 @@ std::unique_ptr<StreamingParserHandler> NewCBOREncoder( std::vector<uint8_t>* out, Status* status); +std::unique_ptr<StreamingParserHandler> NewCBOREncoder(std::string* out, + Status* status); // ============================================================================= // cbor::CBORTokenizer - for parsing individual CBOR items @@ -389,6 +400,9 @@ void WriteTokenStart(cbor::MajorType type, uint64_t value, std::vector<uint8_t>* encoded); +void WriteTokenStart(cbor::MajorType type, + uint64_t value, + std::string* encoded); } // namespace internals } // namespace cbor @@ -416,6 +430,10 @@ // Except for calling the HandleError routine at any time, the client // code must call the Handle* methods in an order in which they'd occur // in valid JSON; otherwise we may crash (the code uses assert). +std::unique_ptr<StreamingParserHandler> NewJSONEncoder( + const Platform* platform, + std::vector<uint8_t>* out, + Status* status); std::unique_ptr<StreamingParserHandler> NewJSONEncoder(const Platform* platform, std::string* out, Status* status); @@ -424,12 +442,28 @@ // json::ParseJSON - for receiving streaming parser events for JSON // ============================================================================= -void ParseJSON(const Platform* platform, +void ParseJSON(const Platform& platform, span<uint8_t> chars, StreamingParserHandler* handler); -void ParseJSON(const Platform* platform, +void ParseJSON(const Platform& platform, span<uint16_t> chars, StreamingParserHandler* handler); + +// ============================================================================= +// json::ConvertCBORToJSON, json::ConvertJSONToCBOR - for transcoding +// ============================================================================= +Status ConvertCBORToJSON(const Platform& platform, + span<uint8_t> cbor, + std::string* json); +Status ConvertCBORToJSON(const Platform& platform, + span<uint8_t> cbor, + std::vector<uint8_t>* json); +Status ConvertJSONToCBOR(const Platform& platform, + span<uint8_t> json, + std::vector<uint8_t>* cbor); +Status ConvertJSONToCBOR(const Platform& platform, + span<uint8_t> json, + std::string* cbor); } // namespace json } // namespace inspector_protocol_encoding
diff --git a/third_party/inspector_protocol/encoding/encoding_test.cc b/third_party/inspector_protocol/encoding/encoding_test.cc index e50ab58..baf7868 100644 --- a/third_party/inspector_protocol/encoding/encoding_test.cc +++ b/third_party/inspector_protocol/encoding/encoding_test.cc
@@ -68,9 +68,9 @@ } }; -json::Platform* GetTestPlatform() { +const json::Platform& GetTestPlatform() { static TestPlatform* platform = new TestPlatform; - return platform; + return *platform; } // ============================================================================= @@ -670,7 +670,7 @@ // And now we roundtrip, decoding the message we just encoded. std::string decoded; std::unique_ptr<StreamingParserHandler> json_encoder = - NewJSONEncoder(GetTestPlatform(), &decoded, &status); + NewJSONEncoder(&GetTestPlatform(), &decoded, &status); ParseCBOR(span<uint8_t>(encoded.data(), encoded.size()), json_encoder.get()); EXPECT_EQ(Error::OK, status.error); EXPECT_EQ(json, decoded); @@ -691,7 +691,7 @@ ParseJSON(GetTestPlatform(), ascii_in, encoder.get()); std::string decoded; std::unique_ptr<StreamingParserHandler> json_writer = - NewJSONEncoder(GetTestPlatform(), &decoded, &status); + NewJSONEncoder(&GetTestPlatform(), &decoded, &status); ParseCBOR(span<uint8_t>(encoded.data(), encoded.size()), json_writer.get()); EXPECT_EQ(Error::OK, status.error); EXPECT_EQ(json, decoded); @@ -724,7 +724,7 @@ // Now drive the json writer via the CBOR decoder. std::string decoded; std::unique_ptr<StreamingParserHandler> json_writer = - NewJSONEncoder(GetTestPlatform(), &decoded, &status); + NewJSONEncoder(&GetTestPlatform(), &decoded, &status); ParseCBOR(SpanFromVector(encoded), json_writer.get()); EXPECT_EQ(Error::OK, status.error); EXPECT_EQ(Status::npos(), status.pos); @@ -744,7 +744,7 @@ std::string out; Status status; std::unique_ptr<StreamingParserHandler> json_writer = - NewJSONEncoder(GetTestPlatform(), &out, &status); + NewJSONEncoder(&GetTestPlatform(), &out, &status); ParseCBOR(span<uint8_t>(in.data(), in.size()), json_writer.get()); EXPECT_EQ(Error::OK, status.error); EXPECT_EQ("{}", out); @@ -768,7 +768,7 @@ std::string out; Status status; std::unique_ptr<StreamingParserHandler> json_writer = - NewJSONEncoder(GetTestPlatform(), &out, &status); + NewJSONEncoder(&GetTestPlatform(), &out, &status); ParseCBOR(span<uint8_t>(bytes.data(), bytes.size()), json_writer.get()); EXPECT_EQ(Error::OK, status.error); EXPECT_EQ("{\"msg\":\"Hello, \\ud83c\\udf0e.\"}", out); @@ -793,7 +793,7 @@ std::string out; Status status; std::unique_ptr<StreamingParserHandler> json_writer = - NewJSONEncoder(GetTestPlatform(), &out, &status); + NewJSONEncoder(&GetTestPlatform(), &out, &status); ParseCBOR(span<uint8_t>(bytes.data(), bytes.size()), json_writer.get()); EXPECT_EQ(Error::OK, status.error); EXPECT_EQ("{\"\\ud83c\\udf0e\":\"\\u263e\"}", out); @@ -804,7 +804,7 @@ std::string out; Status status; std::unique_ptr<StreamingParserHandler> json_writer = - NewJSONEncoder(GetTestPlatform(), &out, &status); + NewJSONEncoder(&GetTestPlatform(), &out, &status); ParseCBOR(span<uint8_t>(in.data(), in.size()), json_writer.get()); EXPECT_EQ(Error::CBOR_NO_INPUT, status.error); EXPECT_EQ("", out); @@ -818,7 +818,7 @@ std::string out; Status status; std::unique_ptr<StreamingParserHandler> json_writer = - NewJSONEncoder(GetTestPlatform(), &out, &status); + NewJSONEncoder(&GetTestPlatform(), &out, &status); ParseCBOR(SpanFromStdString(json), json_writer.get()); EXPECT_EQ(Error::CBOR_INVALID_START_BYTE, status.error); EXPECT_EQ("", out); @@ -834,7 +834,7 @@ std::string out; Status status; std::unique_ptr<StreamingParserHandler> json_writer = - NewJSONEncoder(GetTestPlatform(), &out, &status); + NewJSONEncoder(&GetTestPlatform(), &out, &status); ParseCBOR(span<uint8_t>(bytes.data(), bytes.size()), json_writer.get()); EXPECT_EQ(Error::CBOR_UNEXPECTED_EOF_EXPECTED_VALUE, status.error); EXPECT_EQ(static_cast<int64_t>(bytes.size()), status.pos); @@ -852,7 +852,7 @@ std::string out; Status status; std::unique_ptr<StreamingParserHandler> json_writer = - NewJSONEncoder(GetTestPlatform(), &out, &status); + NewJSONEncoder(&GetTestPlatform(), &out, &status); ParseCBOR(span<uint8_t>(bytes.data(), bytes.size()), json_writer.get()); EXPECT_EQ(Error::CBOR_UNEXPECTED_EOF_IN_ARRAY, status.error); EXPECT_EQ(static_cast<int64_t>(bytes.size()), status.pos); @@ -867,7 +867,7 @@ std::string out; Status status; std::unique_ptr<StreamingParserHandler> json_writer = - NewJSONEncoder(GetTestPlatform(), &out, &status); + NewJSONEncoder(&GetTestPlatform(), &out, &status); ParseCBOR(span<uint8_t>(bytes.data(), bytes.size()), json_writer.get()); EXPECT_EQ(Error::CBOR_UNEXPECTED_EOF_IN_MAP, status.error); EXPECT_EQ(7, status.pos); @@ -884,7 +884,7 @@ std::string out; Status status; std::unique_ptr<StreamingParserHandler> json_writer = - NewJSONEncoder(GetTestPlatform(), &out, &status); + NewJSONEncoder(&GetTestPlatform(), &out, &status); ParseCBOR(span<uint8_t>(bytes.data(), bytes.size()), json_writer.get()); EXPECT_EQ(Error::CBOR_INVALID_MAP_KEY, status.error); EXPECT_EQ(7, status.pos); @@ -915,7 +915,7 @@ std::string out; Status status; std::unique_ptr<StreamingParserHandler> json_writer = - NewJSONEncoder(GetTestPlatform(), &out, &status); + NewJSONEncoder(&GetTestPlatform(), &out, &status); ParseCBOR(span<uint8_t>(bytes.data(), bytes.size()), json_writer.get()); EXPECT_EQ(Error::OK, status.error); EXPECT_EQ(Status::npos(), status.pos); @@ -926,7 +926,7 @@ std::string out; Status status; std::unique_ptr<StreamingParserHandler> json_writer = - NewJSONEncoder(GetTestPlatform(), &out, &status); + NewJSONEncoder(&GetTestPlatform(), &out, &status); ParseCBOR(span<uint8_t>(bytes.data(), bytes.size()), json_writer.get()); EXPECT_EQ(Error::OK, status.error); EXPECT_EQ(Status::npos(), status.pos); @@ -946,7 +946,7 @@ std::string out; Status status; std::unique_ptr<StreamingParserHandler> json_writer = - NewJSONEncoder(GetTestPlatform(), &out, &status); + NewJSONEncoder(&GetTestPlatform(), &out, &status); ParseCBOR(span<uint8_t>(bytes.data(), bytes.size()), json_writer.get()); EXPECT_EQ(Error::CBOR_STACK_LIMIT_EXCEEDED, status.error); EXPECT_EQ(opening_segment_size * 301, status.pos); @@ -956,7 +956,7 @@ std::string out; Status status; std::unique_ptr<StreamingParserHandler> json_writer = - NewJSONEncoder(GetTestPlatform(), &out, &status); + NewJSONEncoder(&GetTestPlatform(), &out, &status); ParseCBOR(span<uint8_t>(bytes.data(), bytes.size()), json_writer.get()); EXPECT_EQ(Error::CBOR_STACK_LIMIT_EXCEEDED, status.error); EXPECT_EQ(opening_segment_size * 301, status.pos); @@ -975,7 +975,7 @@ std::string out; Status status; std::unique_ptr<StreamingParserHandler> json_writer = - NewJSONEncoder(GetTestPlatform(), &out, &status); + NewJSONEncoder(&GetTestPlatform(), &out, &status); ParseCBOR(span<uint8_t>(bytes.data(), bytes.size()), json_writer.get()); EXPECT_EQ(Error::CBOR_UNSUPPORTED_VALUE, status.error); EXPECT_EQ(error_pos, status.pos); @@ -998,7 +998,7 @@ std::string out; Status status; std::unique_ptr<StreamingParserHandler> json_writer = - NewJSONEncoder(GetTestPlatform(), &out, &status); + NewJSONEncoder(&GetTestPlatform(), &out, &status); ParseCBOR(span<uint8_t>(bytes.data(), bytes.size()), json_writer.get()); EXPECT_EQ(Error::CBOR_INVALID_STRING16, status.error); EXPECT_EQ(error_pos, status.pos); @@ -1018,7 +1018,7 @@ std::string out; Status status; std::unique_ptr<StreamingParserHandler> json_writer = - NewJSONEncoder(GetTestPlatform(), &out, &status); + NewJSONEncoder(&GetTestPlatform(), &out, &status); ParseCBOR(span<uint8_t>(bytes.data(), bytes.size()), json_writer.get()); EXPECT_EQ(Error::CBOR_INVALID_STRING8, status.error); EXPECT_EQ(error_pos, status.pos); @@ -1040,7 +1040,7 @@ std::string out; Status status; std::unique_ptr<StreamingParserHandler> json_writer = - NewJSONEncoder(GetTestPlatform(), &out, &status); + NewJSONEncoder(&GetTestPlatform(), &out, &status); ParseCBOR(span<uint8_t>(bytes.data(), bytes.size()), json_writer.get()); EXPECT_EQ(Error::CBOR_INVALID_BINARY, status.error); EXPECT_EQ(error_pos, status.pos); @@ -1061,7 +1061,7 @@ std::string out; Status status; std::unique_ptr<StreamingParserHandler> json_writer = - NewJSONEncoder(GetTestPlatform(), &out, &status); + NewJSONEncoder(&GetTestPlatform(), &out, &status); ParseCBOR(span<uint8_t>(bytes.data(), bytes.size()), json_writer.get()); EXPECT_EQ(Error::CBOR_INVALID_DOUBLE, status.error); EXPECT_EQ(error_pos, status.pos); @@ -1082,7 +1082,7 @@ std::string out; Status status; std::unique_ptr<StreamingParserHandler> json_writer = - NewJSONEncoder(GetTestPlatform(), &out, &status); + NewJSONEncoder(&GetTestPlatform(), &out, &status); ParseCBOR(span<uint8_t>(bytes.data(), bytes.size()), json_writer.get()); EXPECT_EQ(Error::CBOR_INVALID_INT32, status.error); EXPECT_EQ(error_pos, status.pos); @@ -1105,7 +1105,7 @@ std::string out; Status status; std::unique_ptr<StreamingParserHandler> json_writer = - NewJSONEncoder(GetTestPlatform(), &out, &status); + NewJSONEncoder(&GetTestPlatform(), &out, &status); ParseCBOR(span<uint8_t>(bytes.data(), bytes.size()), json_writer.get()); EXPECT_EQ(Error::CBOR_TRAILING_JUNK, status.error); EXPECT_EQ(error_pos, status.pos); @@ -1127,7 +1127,7 @@ std::string out; Status status; std::unique_ptr<StreamingParserHandler> writer = - NewJSONEncoder(GetTestPlatform(), &out, &status); + NewJSONEncoder(&GetTestPlatform(), &out, &status); writer->HandleMapBegin(); WriteUTF8AsUTF16(writer.get(), "msg1"); WriteUTF8AsUTF16(writer.get(), "Hello, 🌎."); @@ -1171,7 +1171,7 @@ std::string out; Status status; std::unique_ptr<StreamingParserHandler> writer = - NewJSONEncoder(GetTestPlatform(), &out, &status); + NewJSONEncoder(&GetTestPlatform(), &out, &status); writer->HandleMapBegin(); writer->HandleString8(SpanFromStdString("Infinity")); writer->HandleDouble(std::numeric_limits<double>::infinity()); @@ -1192,7 +1192,7 @@ std::string out; Status status; std::unique_ptr<StreamingParserHandler> writer = - NewJSONEncoder(GetTestPlatform(), &out, &status); + NewJSONEncoder(&GetTestPlatform(), &out, &status); writer->HandleBinary(SpanFromVector(std::vector<uint8_t>({'M', 'a', 'n'}))); EXPECT_TRUE(status.ok()); EXPECT_EQ("\"TWFu\"", out); @@ -1201,7 +1201,7 @@ std::string out; Status status; std::unique_ptr<StreamingParserHandler> writer = - NewJSONEncoder(GetTestPlatform(), &out, &status); + NewJSONEncoder(&GetTestPlatform(), &out, &status); writer->HandleBinary(SpanFromVector(std::vector<uint8_t>({'M', 'a'}))); EXPECT_TRUE(status.ok()); EXPECT_EQ("\"TWE=\"", out); @@ -1210,7 +1210,7 @@ std::string out; Status status; std::unique_ptr<StreamingParserHandler> writer = - NewJSONEncoder(GetTestPlatform(), &out, &status); + NewJSONEncoder(&GetTestPlatform(), &out, &status); writer->HandleBinary(SpanFromVector(std::vector<uint8_t>({'M'}))); EXPECT_TRUE(status.ok()); EXPECT_EQ("\"TQ==\"", out); @@ -1219,7 +1219,7 @@ std::string out; Status status; std::unique_ptr<StreamingParserHandler> writer = - NewJSONEncoder(GetTestPlatform(), &out, &status); + NewJSONEncoder(&GetTestPlatform(), &out, &status); writer->HandleBinary(SpanFromVector(std::vector<uint8_t>( {'H', 'e', 'l', 'l', 'o', ',', ' ', 'w', 'o', 'r', 'l', 'd', '.'}))); EXPECT_TRUE(status.ok()); @@ -1233,7 +1233,7 @@ std::string out; Status status; std::unique_ptr<StreamingParserHandler> writer = - NewJSONEncoder(GetTestPlatform(), &out, &status); + NewJSONEncoder(&GetTestPlatform(), &out, &status); writer->HandleMapBegin(); WriteUTF8AsUTF16(writer.get(), "msg1"); writer->HandleError(Status{Error::JSON_PARSER_VALUE_EXPECTED, 42}); @@ -1610,5 +1610,24 @@ EXPECT_EQ(0, log_.status().pos); EXPECT_EQ("", log_.str()); } + +TEST(ConvertJSONToCBOR, RoundTripValidJson) { + std::string json = "{\"msg\":\"Hello, world.\"}"; + std::string cbor; + { + Status status = + ConvertJSONToCBOR(GetTestPlatform(), SpanFromStdString(json), &cbor); + EXPECT_EQ(Error::OK, status.error); + EXPECT_EQ(Status::npos(), status.pos); + } + std::string roundtrip_json; + { + Status status = ConvertCBORToJSON(GetTestPlatform(), + SpanFromStdString(cbor), &roundtrip_json); + EXPECT_EQ(Error::OK, status.error); + EXPECT_EQ(Status::npos(), status.pos); + } + EXPECT_EQ(json, roundtrip_json); +} } // namespace json } // namespace inspector_protocol_encoding
diff --git a/third_party/inspector_protocol/lib/encoding_cpp.template b/third_party/inspector_protocol/lib/encoding_cpp.template index 84251d9..7127409 100644 --- a/third_party/inspector_protocol/lib/encoding_cpp.template +++ b/third_party/inspector_protocol/lib/encoding_cpp.template
@@ -7,6 +7,7 @@ #include <cassert> +#include <cmath> #include <cstring> #include <limits> #include <stack> @@ -86,8 +87,8 @@ // Writes the bytes for |v| to |out|, starting with the most significant byte. // See also: https://commandcenter.blogspot.com/2012/04/byte-order-fallacy.html -template <typename T> -void WriteBytesMostSignificantByteFirst(T v, std::vector<uint8_t>* out) { +template <typename T, class C> +void WriteBytesMostSignificantByteFirst(T v, C* out) { for (int shift_bytes = sizeof(T) - 1; shift_bytes >= 0; --shift_bytes) out->push_back(0xff & (v >> (shift_bytes * 8))); } @@ -157,9 +158,8 @@ // Writes the start of a token with |type|. The |value| may indicate the size, // or it may be the payload if the value is an unsigned integer. -void WriteTokenStart(MajorType type, - uint64_t value, - std::vector<uint8_t>* encoded) { +template <class C> +void WriteTokenStartTmpl(MajorType type, uint64_t value, C* encoded) { if (value < 24) { // Values 0-23 are encoded directly into the additional info of the // initial byte. @@ -189,6 +189,14 @@ encoded->push_back(EncodeInitialByte(type, kAdditionalInformation8Bytes)); WriteBytesMostSignificantByteFirst<uint64_t>(value, encoded); } +void WriteTokenStart(MajorType type, + uint64_t value, + std::vector<uint8_t>* encoded) { + WriteTokenStartTmpl(type, value, encoded); +} +void WriteTokenStart(MajorType type, uint64_t value, std::string* encoded) { + WriteTokenStartTmpl(type, value, encoded); +} } // namespace internals // ============================================================================= @@ -232,7 +240,8 @@ return kStopByte; } -void EncodeInt32(int32_t value, std::vector<uint8_t>* out) { +template <class C> +void EncodeInt32Tmpl(int32_t value, C* out) { if (value >= 0) { internals::WriteTokenStart(MajorType::UNSIGNED, value, out); } else { @@ -240,8 +249,15 @@ internals::WriteTokenStart(MajorType::NEGATIVE, representation, out); } } +void EncodeInt32(int32_t value, std::vector<uint8_t>* out) { + EncodeInt32Tmpl(value, out); +} +void EncodeInt32(int32_t value, std::string* out) { + EncodeInt32Tmpl(value, out); +} -void EncodeString16(span<uint16_t> in, std::vector<uint8_t>* out) { +template <class C> +void EncodeString16Tmpl(span<uint16_t> in, C* out) { uint64_t byte_length = static_cast<uint64_t>(in.size_bytes()); internals::WriteTokenStart(MajorType::BYTE_STRING, byte_length, out); // When emitting UTF16 characters, we always write the least significant byte @@ -258,14 +274,28 @@ out->push_back(two_bytes >> 8); } } +void EncodeString16(span<uint16_t> in, std::vector<uint8_t>* out) { + EncodeString16Tmpl(in, out); +} +void EncodeString16(span<uint16_t> in, std::string* out) { + EncodeString16Tmpl(in, out); +} -void EncodeString8(span<uint8_t> in, std::vector<uint8_t>* out) { +template <class C> +void EncodeString8Tmpl(span<uint8_t> in, C* out) { internals::WriteTokenStart(MajorType::STRING, static_cast<uint64_t>(in.size_bytes()), out); out->insert(out->end(), in.begin(), in.end()); } +void EncodeString8(span<uint8_t> in, std::vector<uint8_t>* out) { + EncodeString8Tmpl(in, out); +} +void EncodeString8(span<uint8_t> in, std::string* out) { + EncodeString8Tmpl(in, out); +} -void EncodeFromLatin1(span<uint8_t> latin1, std::vector<uint8_t>* out) { +template <class C> +void EncodeFromLatin1Tmpl(span<uint8_t> latin1, C* out) { for (std::ptrdiff_t ii = 0; ii < latin1.size(); ++ii) { if (latin1[ii] <= 127) continue; @@ -285,8 +315,15 @@ } EncodeString8(latin1, out); } +void EncodeFromLatin1(span<uint8_t> latin1, std::vector<uint8_t>* out) { + EncodeFromLatin1Tmpl(latin1, out); +} +void EncodeFromLatin1(span<uint8_t> latin1, std::string* out) { + EncodeFromLatin1Tmpl(latin1, out); +} -void EncodeFromUTF16(span<uint16_t> utf16, std::vector<uint8_t>* out) { +template <class C> +void EncodeFromUTF16Tmpl(span<uint16_t> utf16, C* out) { // If there's at least one non-ASCII char, encode as STRING16 (UTF16). for (uint16_t ch : utf16) { if (ch <= 127) @@ -299,13 +336,26 @@ static_cast<uint64_t>(utf16.size()), out); out->insert(out->end(), utf16.begin(), utf16.end()); } +void EncodeFromUTF16(span<uint16_t> utf16, std::vector<uint8_t>* out) { + EncodeFromUTF16Tmpl(utf16, out); +} +void EncodeFromUTF16(span<uint16_t> utf16, std::string* out) { + EncodeFromUTF16Tmpl(utf16, out); +} -void EncodeBinary(span<uint8_t> in, std::vector<uint8_t>* out) { +template <class C> +void EncodeBinaryTmpl(span<uint8_t> in, C* out) { out->push_back(kExpectedConversionToBase64Tag); uint64_t byte_length = static_cast<uint64_t>(in.size_bytes()); internals::WriteTokenStart(MajorType::BYTE_STRING, byte_length, out); out->insert(out->end(), in.begin(), in.end()); } +void EncodeBinary(span<uint8_t> in, std::vector<uint8_t>* out) { + EncodeBinaryTmpl(in, out); +} +void EncodeBinary(span<uint8_t> in, std::string* out) { + EncodeBinaryTmpl(in, out); +} // A double is encoded with a specific initial byte // (kInitialByteForDouble) plus the 64 bits of payload for its value. @@ -316,7 +366,8 @@ // bit wide length, plus a 32 bit length for that string. constexpr std::ptrdiff_t kEncodedEnvelopeHeaderSize = 1 + 1 + sizeof(uint32_t); -void EncodeDouble(double value, std::vector<uint8_t>* out) { +template <class C> +void EncodeDoubleTmpl(double value, C* out) { // The additional_info=27 indicates 64 bits for the double follow. // See RFC 7049 Section 2.3, Table 1. out->push_back(kInitialByteForDouble); @@ -327,44 +378,68 @@ reinterpret.from_double = value; WriteBytesMostSignificantByteFirst<uint64_t>(reinterpret.to_uint64, out); } +void EncodeDouble(double value, std::vector<uint8_t>* out) { + EncodeDoubleTmpl(value, out); +} +void EncodeDouble(double value, std::string* out) { + EncodeDoubleTmpl(value, out); +} // ============================================================================= // cbor::EnvelopeEncoder - for wrapping submessages // ============================================================================= -void EnvelopeEncoder::EncodeStart(std::vector<uint8_t>* out) { - assert(byte_size_pos_ == 0); +template <class C> +void EncodeStartTmpl(C* out, std::size_t& byte_size_pos) { + assert(byte_size_pos == 0); out->push_back(kInitialByteForEnvelope); out->push_back(kInitialByteFor32BitLengthByteString); - byte_size_pos_ = out->size(); + byte_size_pos = out->size(); out->resize(out->size() + sizeof(uint32_t)); } -bool EnvelopeEncoder::EncodeStop(std::vector<uint8_t>* out) { - assert(byte_size_pos_ != 0); +void EnvelopeEncoder::EncodeStart(std::vector<uint8_t>* out) { + EncodeStartTmpl<std::vector<uint8_t>>(out, byte_size_pos_); +} + +void EnvelopeEncoder::EncodeStart(std::string* out) { + EncodeStartTmpl<std::string>(out, byte_size_pos_); +} + +template <class C> +bool EncodeStopTmpl(C* out, std::size_t& byte_size_pos) { + assert(byte_size_pos != 0); // The byte size is the size of the payload, that is, all the // bytes that were written past the byte size position itself. - uint64_t byte_size = out->size() - (byte_size_pos_ + sizeof(uint32_t)); + uint64_t byte_size = out->size() - (byte_size_pos + sizeof(uint32_t)); // We store exactly 4 bytes, so at most INT32MAX, with most significant // byte first. if (byte_size > std::numeric_limits<uint32_t>::max()) return false; for (int shift_bytes = sizeof(uint32_t) - 1; shift_bytes >= 0; --shift_bytes) { - (*out)[byte_size_pos_++] = 0xff & (byte_size >> (shift_bytes * 8)); + (*out)[byte_size_pos++] = 0xff & (byte_size >> (shift_bytes * 8)); } return true; } +bool EnvelopeEncoder::EncodeStop(std::vector<uint8_t>* out) { + return EncodeStopTmpl(out, byte_size_pos_); +} + +bool EnvelopeEncoder::EncodeStop(std::string* out) { + return EncodeStopTmpl(out, byte_size_pos_); +} + // ============================================================================= // cbor::NewCBOREncoder - for encoding from a streaming parser // ============================================================================= namespace { +template <class C> class CBOREncoder : public StreamingParserHandler { public: - CBOREncoder(std::vector<uint8_t>* out, Status* status) - : out_(out), status_(status) { + CBOREncoder(C* out, Status* status) : out_(out), status_(status) { *status_ = Status(); } @@ -425,7 +500,7 @@ } private: - std::vector<uint8_t>* out_; + C* out_; std::vector<EnvelopeEncoder> envelopes_; Status* status_; }; @@ -434,7 +509,13 @@ std::unique_ptr<StreamingParserHandler> NewCBOREncoder( std::vector<uint8_t>* out, Status* status) { - return std::unique_ptr<StreamingParserHandler>(new CBOREncoder(out, status)); + return std::unique_ptr<StreamingParserHandler>( + new CBOREncoder<std::vector<uint8_t>>(out, status)); +} +std::unique_ptr<StreamingParserHandler> NewCBOREncoder(std::string* out, + Status* status) { + return std::unique_ptr<StreamingParserHandler>( + new CBOREncoder<std::string>(out, status)); } // ============================================================================= @@ -666,7 +747,7 @@ namespace { // When parsing CBOR, we limit recursion depth for objects and arrays // to this constant. -static constexpr int kStackLimit = 1000; +static constexpr int kStackLimit = 300; // Below are three parsing routines for CBOR, which cover enough // to roundtrip JSON messages. @@ -867,10 +948,11 @@ namespace { // Prints |value| to |out| with 4 hex digits, most significant chunk first. -void PrintHex(uint16_t value, std::string* out) { +template <class C> +void PrintHex(uint16_t value, C* out) { for (int ii = 3; ii >= 0; --ii) { int four_bits = 0xf & (value >> (4 * ii)); - out->append(1, four_bits + ((four_bits <= 9) ? '0' : ('a' - 10))); + out->push_back(four_bits + ((four_bits <= 9) ? '0' : ('a' - 10))); } } @@ -888,6 +970,9 @@ class State { public: explicit State(Container container) : container_(container) {} + void StartElement(std::vector<uint8_t>* out) { + // FIXME!!! + } void StartElement(std::string* out) { assert(container_ != Container::NONE || size_ == 0); if (size_ != 0) { @@ -907,7 +992,8 @@ "ABCDEFGHIJKLMNOPQRSTUVWXYZ" "abcdefghijklmnopqrstuvwxyz0123456789+/"; -void Base64Encode(const span<uint8_t>& in, std::string* out) { +template <class C> +void Base64Encode(const span<uint8_t>& in, C* out) { // The following three cases are based on the tables in the example // section in https://en.wikipedia.org/wiki/Base64. We process three // input bytes at a time, emitting 4 output bytes at a time. @@ -939,9 +1025,10 @@ } // Implements a handler for JSON parser events to emit a JSON string. +template <class C> class JSONEncoder : public StreamingParserHandler { public: - JSONEncoder(const Platform* platform, std::string* out, Status* status) + JSONEncoder(const Platform* platform, C* out, Status* status) : platform_(platform), out_(out), status_(status) { *status_ = Status(); state_.emplace(Container::NONE); @@ -953,7 +1040,7 @@ assert(!state_.empty()); state_.top().StartElement(out_); state_.emplace(Container::MAP); - out_->append("{"); + Emit('{'); } void HandleMapEnd() override { @@ -961,7 +1048,7 @@ return; assert(state_.size() >= 2 && state_.top().container() == Container::MAP); state_.pop(); - out_->append("}"); + Emit('}'); } void HandleArrayBegin() override { @@ -969,7 +1056,7 @@ return; state_.top().StartElement(out_); state_.emplace(Container::ARRAY); - out_->append("["); + Emit('['); } void HandleArrayEnd() override { @@ -977,64 +1064,64 @@ return; assert(state_.size() >= 2 && state_.top().container() == Container::ARRAY); state_.pop(); - out_->append("]"); + Emit(']'); } void HandleString16(span<uint16_t> chars) override { if (!status_->ok()) return; state_.top().StartElement(out_); - out_->append("\""); + Emit('"'); for (const uint16_t ch : chars) { if (ch == '"') { - out_->append("\\\""); + Emit("\\\""); } else if (ch == '\\') { - out_->append("\\\\"); + Emit("\\\\"); } else if (ch == '\b') { - out_->append("\\b"); + Emit("\\b"); } else if (ch == '\f') { - out_->append("\\f"); + Emit("\\f"); } else if (ch == '\n') { - out_->append("\\n"); + Emit("\\n"); } else if (ch == '\r') { - out_->append("\\r"); + Emit("\\r"); } else if (ch == '\t') { - out_->append("\\t"); + Emit("\\t"); } else if (ch >= 32 && ch <= 126) { - out_->append(1, ch); + Emit(ch); } else { - out_->append("\\u"); + Emit("\\u"); PrintHex(ch, out_); } } - out_->append("\""); + Emit('"'); } void HandleString8(span<uint8_t> chars) override { if (!status_->ok()) return; state_.top().StartElement(out_); - out_->append("\""); + Emit('"'); for (std::ptrdiff_t ii = 0; ii < chars.size(); ++ii) { uint8_t c = chars[ii]; if (c == '"') { - out_->append("\\\""); + Emit("\\\""); } else if (c == '\\') { - out_->append("\\\\"); + Emit("\\\\"); } else if (c == '\b') { - out_->append("\\b"); + Emit("\\b"); } else if (c == '\f') { - out_->append("\\f"); + Emit("\\f"); } else if (c == '\n') { - out_->append("\\n"); + Emit("\\n"); } else if (c == '\r') { - out_->append("\\r"); + Emit("\\r"); } else if (c == '\t') { - out_->append("\\t"); + Emit("\\t"); } else if (c >= 32 && c <= 126) { - out_->append(1, c); + Emit(c); } else if (c < 32) { - out_->append("\\u"); + Emit("\\u"); PrintHex(static_cast<uint16_t>(c), out_); } else { // Inspect the leading byte to figure out how long the utf8 @@ -1085,35 +1172,41 @@ // using the math described at https://en.wikipedia.org/wiki/UTF-16, // for either one or two 16 bit characters. if (codepoint < 0xffff) { - out_->append("\\u"); + Emit("\\u"); PrintHex(static_cast<uint16_t>(codepoint), out_); continue; } codepoint -= 0x10000; // high surrogate - out_->append("\\u"); + Emit("\\u"); PrintHex(static_cast<uint16_t>((codepoint >> 10) + 0xd800), out_); // low surrogate - out_->append("\\u"); + Emit("\\u"); PrintHex(static_cast<uint16_t>((codepoint & 0x3ff) + 0xdc00), out_); } } - out_->append("\""); + Emit('"'); } void HandleBinary(span<uint8_t> bytes) override { if (!status_->ok()) return; state_.top().StartElement(out_); - out_->append("\""); + Emit('"'); Base64Encode(bytes, out_); - out_->append("\""); + Emit('"'); } void HandleDouble(double value) override { if (!status_->ok()) return; state_.top().StartElement(out_); + // JSON cannot represent NaN or Infinity. So, for compatibility, + // we behave like the JSON object in web browsers: emit 'null'. + if (!std::isfinite(value)) { + Emit("null"); + return; + } std::unique_ptr<char[]> str_value = platform_->DToStr(value); // DToStr may fail to emit a 0 before the decimal dot. E.g. this is @@ -1123,33 +1216,33 @@ // we probe for this and emit the leading 0 anyway if necessary. const char* chars = str_value.get(); if (chars[0] == '.') { - out_->append("0"); + Emit('0'); } else if (chars[0] == '-' && chars[1] == '.') { - out_->append("-0"); + Emit("-0"); ++chars; } - out_->append(chars); + Emit(chars); } void HandleInt32(int32_t value) override { if (!status_->ok()) return; state_.top().StartElement(out_); - out_->append(std::to_string(value)); + Emit(std::to_string(value)); } void HandleBool(bool value) override { if (!status_->ok()) return; state_.top().StartElement(out_); - out_->append(value ? "true" : "false"); + Emit(value ? "true" : "false"); } void HandleNull() override { if (!status_->ok()) return; state_.top().StartElement(out_); - out_->append("null"); + Emit("null"); } void HandleError(Status error) override { @@ -1159,18 +1252,33 @@ } private: + void Emit(char c) { out_->push_back(c); } + void Emit(const char* str) { + out_->insert(out_->end(), str, str + strlen(str)); + } + void Emit(const std::string& str) { + out_->insert(out_->end(), str.begin(), str.end()); + } + const Platform* platform_; - std::string* out_; + C* out_; Status* status_; std::stack<State> state_; }; } // namespace +std::unique_ptr<StreamingParserHandler> NewJSONEncoder( + const Platform* platform, + std::vector<uint8_t>* out, + Status* status) { + return std::unique_ptr<StreamingParserHandler>( + new JSONEncoder<std::vector<uint8_t>>(platform, out, status)); +} std::unique_ptr<StreamingParserHandler> NewJSONEncoder(const Platform* platform, std::string* out, Status* status) { return std::unique_ptr<StreamingParserHandler>( - new JSONEncoder(platform, out, status)); + new JSONEncoder<std::string>(platform, out, status)); } // ============================================================================= @@ -1178,7 +1286,7 @@ // ============================================================================= namespace { -const int kStackLimit = 1000; +const int kStackLimit = 300; enum Token { ObjectBegin, @@ -1786,19 +1894,65 @@ }; } // namespace -void ParseJSON(const Platform* platform, +void ParseJSON(const Platform& platform, span<uint8_t> chars, StreamingParserHandler* handler) { - JsonParser<uint8_t> parser(platform, handler); + JsonParser<uint8_t> parser(&platform, handler); parser.Parse(chars.data(), chars.size()); } -void ParseJSON(const Platform* platform, +void ParseJSON(const Platform& platform, span<uint16_t> chars, StreamingParserHandler* handler) { - JsonParser<uint16_t> parser(platform, handler); + JsonParser<uint16_t> parser(&platform, handler); parser.Parse(chars.data(), chars.size()); } + +// ============================================================================= +// json::ConvertCBORToJSON, json::ConvertJSONToCBOR - for transcoding +// ============================================================================= +template <class C> +Status ConvertCBORToJSONTmpl(const Platform& platform, + span<uint8_t> cbor, + C* json) { + Status status; + std::unique_ptr<StreamingParserHandler> json_writer = + NewJSONEncoder(&platform, json, &status); + cbor::ParseCBOR(cbor, json_writer.get()); + return status; +} + +Status ConvertCBORToJSON(const Platform& platform, + span<uint8_t> cbor, + std::vector<uint8_t>* json) { + return ConvertCBORToJSONTmpl(platform, cbor, json); +} +Status ConvertCBORToJSON(const Platform& platform, + span<uint8_t> cbor, + std::string* json) { + return ConvertCBORToJSONTmpl(platform, cbor, json); +} + +template <class C> +Status ConvertJSONToCBORTmpl(const Platform& platform, + span<uint8_t> json, + C* cbor) { + Status status; + std::unique_ptr<StreamingParserHandler> encoder = + cbor::NewCBOREncoder(cbor, &status); + ParseJSON(platform, json, encoder.get()); + return status; +} +Status ConvertJSONToCBOR(const Platform& platform, + span<uint8_t> json, + std::string* cbor) { + return ConvertJSONToCBORTmpl(platform, json, cbor); +} +Status ConvertJSONToCBOR(const Platform& platform, + span<uint8_t> json, + std::vector<uint8_t>* cbor) { + return ConvertJSONToCBORTmpl(platform, json, cbor); +} } // namespace json {% for namespace in config.protocol.namespace %}
diff --git a/third_party/inspector_protocol/lib/encoding_h.template b/third_party/inspector_protocol/lib/encoding_h.template index aba28c8..5250a89 100644 --- a/third_party/inspector_protocol/lib/encoding_h.template +++ b/third_party/inspector_protocol/lib/encoding_h.template
@@ -197,32 +197,39 @@ // Encodes |value| as |UNSIGNED| (major type 0) iff >= 0, or |NEGATIVE| // (major type 1) iff < 0. void EncodeInt32(int32_t value, std::vector<uint8_t>* out); +void EncodeInt32(int32_t value, std::string* out); // Encodes a UTF16 string as a BYTE_STRING (major type 2). Each utf16 // character in |in| is emitted with most significant byte first, // appending to |out|. void EncodeString16(span<uint16_t> in, std::vector<uint8_t>* out); +void EncodeString16(span<uint16_t> in, std::string* out); // Encodes a UTF8 string |in| as STRING (major type 3). void EncodeString8(span<uint8_t> in, std::vector<uint8_t>* out); +void EncodeString8(span<uint8_t> in, std::string* out); // Encodes the given |latin1| string as STRING8. // If any non-ASCII character is present, it will be represented // as a 2 byte UTF8 sequence. void EncodeFromLatin1(span<uint8_t> latin1, std::vector<uint8_t>* out); +void EncodeFromLatin1(span<uint8_t> latin1, std::string* out); // Encodes the given |utf16| string as STRING8 if it's entirely US-ASCII. // Otherwise, encodes as STRING16. void EncodeFromUTF16(span<uint16_t> utf16, std::vector<uint8_t>* out); +void EncodeFromUTF16(span<uint16_t> utf16, std::string* out); // Encodes arbitrary binary data in |in| as a BYTE_STRING (major type 2) with // definitive length, prefixed with tag 22 indicating expected conversion to // base64 (see RFC 7049, Table 3 and Section 2.4.4.2). void EncodeBinary(span<uint8_t> in, std::vector<uint8_t>* out); +void EncodeBinary(span<uint8_t> in, std::string* out); // Encodes / decodes a double as Major type 7 (SIMPLE_VALUE), // with additional info = 27, followed by 8 bytes in big endian. void EncodeDouble(double value, std::vector<uint8_t>* out); +void EncodeDouble(double value, std::string* out); // ============================================================================= // cbor::EnvelopeEncoder - for wrapping submessages @@ -241,9 +248,11 @@ // byte size in |byte_size_pos_|. Also emits empty bytes for the // byte sisze so that encoding can continue. void EncodeStart(std::vector<uint8_t>* out); + void EncodeStart(std::string* out); // This records the current size in |out| at position byte_size_pos_. // Returns true iff successful. bool EncodeStop(std::vector<uint8_t>* out); + bool EncodeStop(std::string* out); private: std::size_t byte_size_pos_ = 0; @@ -260,6 +269,8 @@ std::unique_ptr<StreamingParserHandler> NewCBOREncoder( std::vector<uint8_t>* out, Status* status); +std::unique_ptr<StreamingParserHandler> NewCBOREncoder(std::string* out, + Status* status); // ============================================================================= // cbor::CBORTokenizer - for parsing individual CBOR items @@ -397,6 +408,9 @@ void WriteTokenStart(cbor::MajorType type, uint64_t value, std::vector<uint8_t>* encoded); +void WriteTokenStart(cbor::MajorType type, + uint64_t value, + std::string* encoded); } // namespace internals } // namespace cbor @@ -424,6 +438,10 @@ // Except for calling the HandleError routine at any time, the client // code must call the Handle* methods in an order in which they'd occur // in valid JSON; otherwise we may crash (the code uses assert). +std::unique_ptr<StreamingParserHandler> NewJSONEncoder( + const Platform* platform, + std::vector<uint8_t>* out, + Status* status); std::unique_ptr<StreamingParserHandler> NewJSONEncoder(const Platform* platform, std::string* out, Status* status); @@ -432,12 +450,28 @@ // json::ParseJSON - for receiving streaming parser events for JSON // ============================================================================= -void ParseJSON(const Platform* platform, +void ParseJSON(const Platform& platform, span<uint8_t> chars, StreamingParserHandler* handler); -void ParseJSON(const Platform* platform, +void ParseJSON(const Platform& platform, span<uint16_t> chars, StreamingParserHandler* handler); + +// ============================================================================= +// json::ConvertCBORToJSON, json::ConvertJSONToCBOR - for transcoding +// ============================================================================= +Status ConvertCBORToJSON(const Platform& platform, + span<uint8_t> cbor, + std::string* json); +Status ConvertCBORToJSON(const Platform& platform, + span<uint8_t> cbor, + std::vector<uint8_t>* json); +Status ConvertJSONToCBOR(const Platform& platform, + span<uint8_t> json, + std::vector<uint8_t>* cbor); +Status ConvertJSONToCBOR(const Platform& platform, + span<uint8_t> json, + std::string* cbor); } // namespace json {% for namespace in config.protocol.namespace %}
diff --git a/third_party/r8/README.chromium b/third_party/r8/README.chromium index 209d12b..1a7e0df 100644 --- a/third_party/r8/README.chromium +++ b/third_party/r8/README.chromium
@@ -1,7 +1,7 @@ Name: R8 URL: https://r8.googlesource.com/r8 Revision: 03ab76ff37d7995f76b639dec680cd49ff6fbc41 -Version: 1.4.14-dev +Version: 1.5.18-dev License: BSD 3-Clause License File: NOT_SHIPPED Security Critical: no
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml index f727156..204bd83 100644 --- a/tools/metrics/histograms/enums.xml +++ b/tools/metrics/histograms/enums.xml
@@ -22805,6 +22805,8 @@ <int value="2855" label="InputTypeReset"/> <int value="2856" label="SelectElementSingle"/> <int value="2857" label="SelectElementMultiple"/> + <int value="2858" label="V8Animation_Effect_AttributeGetter"/> + <int value="2859" label="V8Animation_Effect_AttributeSetter"/> </enum> <enum name="FeaturePolicyFeature"> @@ -32396,6 +32398,7 @@ <int value="-1691281364" label="enable-notification-action-icons"/> <int value="-1687406612" label="UseSkiaRenderer:disabled"/> <int value="-1686782572" label="ChromeHomeInactivitySheetExpansion:disabled"/> + <int value="-1684773837" label="TabEngagementReportingAndroid:disabled"/> <int value="-1684123448" label="disable-best-effort-tasks"/> <int value="-1682843294" label="DataReductionProxyDecidesTransform:enabled"/> <int value="-1677715989" label="UnifiedConsent:disabled"/> @@ -33021,6 +33024,7 @@ <int value="-747463111" label="ContentSuggestionsNotifications:disabled"/> <int value="-746328467" label="ExpensiveBackgroundTimerThrottling:disabled"/> <int value="-744159181" label="disable-spdy-proxy-dev-auth-origin"/> + <int value="-743590125" label="TabEngagementReportingAndroid:enabled"/> <int value="-743103250" label="enable-linkable-ephemeral-apps"/> <int value="-742469530" label="DriveFS:disabled"/> <int value="-741806604" label="DownloadsUi:disabled"/> @@ -33059,6 +33063,7 @@ label="AutofillSaveCardDialogUnlabeledExpirationDate:disabled"/> <int value="-697751423" label="disable-quickoffice-component-app"/> <int value="-696693295" label="Canvas2DImageChromium:disabled"/> + <int value="-694622753" label="VizHitTest:disabled"/> <int value="-694187898" label="MashOopViz:disabled"/> <int value="-684900739" label="disable-merge-key-char-events"/> <int value="-684223908" label="enable-android-wallpapers-app"/> @@ -33164,6 +33169,7 @@ <int value="-508250572" label="ServiceWorkerLongRunningMessage:disabled"/> <int value="-508143738" label="disable-accelerated-fixed-root-background"/> <int value="-506706655" label="respect-autocomplete-off-autofill"/> + <int value="-506366023" label="VizHitTest:enabled"/> <int value="-505679399" label="FontCacheScaling:enabled"/> <int value="-503430431" label="XRSandbox:enabled"/> - @@ -34158,7 +34164,6 @@ <int value="1108663108" label="disable-device-discovery-notifications"/> <int value="1111871757" label="ForceUnifiedConsentBump:enabled"/> <int value="1112051724" label="DetectingHeavyPages:enabled"/> - <int value="1112885300" label="VizHitTestDrawQuad:disabled"/> <int value="1113196543" label="ShowManagedUi:disabled"/> <int value="1113365156" label="tab-management-experiment-type-chive"/> <int value="1114629582" label="enable-floating-virtual-keyboard"/> @@ -34383,7 +34388,6 @@ <int value="1441897340" label="AndroidSpellCheckerNonLowEnd:enabled"/> <int value="1442798825" label="enable-quic"/> <int value="1442830837" label="MemoryAblation:disabled"/> - <int value="1444320426" label="VizHitTestDrawQuad:enabled"/> <int value="1447295459" label="SyncPseudoUSSApps:enabled"/> <int value="1448684258" label="TabHoverCardImages:enabled"/> <int value="1452546183" label="PwaPersistentNotification:enabled"/> @@ -45730,6 +45734,8 @@ <int value="2" label="SchemaMetadataMissing"/> <int value="3" label="SchemaMetadataWrongVersion"/> <int value="4" label="ComponentMetadataMissing"/> + <int value="5" label="FetchedMetadataMissing"/> + <int value="6" label="ComponentAndFetchedMetadataMissing"/> </enum> <enum name="PreviewsHintCacheLevelDBStoreStatus">
diff --git a/tools/metrics/histograms/histograms.xml b/tools/metrics/histograms/histograms.xml index 42eedab..92e271a 100644 --- a/tools/metrics/histograms/histograms.xml +++ b/tools/metrics/histograms/histograms.xml
@@ -127120,6 +127120,26 @@ </summary> </histogram> +<histogram name="Tabs.TimeSinceLastView.OnTabClose" units="seconds" + expires_after="M82"> + <owner>mattsimmons@chromium.org</owner> + <owner>memex-team@google.com</owner> + <summary> + When a tab is closed, this reports the amount of time in seconds that has + passed since the last user engagement with the tab. Currently Android-only. + </summary> +</histogram> + +<histogram name="Tabs.TimeSinceLastView.OnTabView" units="seconds" + expires_after="M82"> + <owner>mattsimmons@chromium.org</owner> + <owner>memex-team@google.com</owner> + <summary> + When a tab is revisited, this reports the amount of time in seconds that has + passed since the last user engagement with the tab. Currently Android-only. + </summary> +</histogram> + <histogram base="true" name="Tabs.UnusedAndClosedInInterval.Count" units="tabs"> <owner>sebmarchand@chromium.org</owner> <summary>
diff --git a/tools/win/DebugVisualizers/libc++.natvis b/tools/win/DebugVisualizers/libc++.natvis index ae5101dd3..bed6f25 100644 --- a/tools/win/DebugVisualizers/libc++.natvis +++ b/tools/win/DebugVisualizers/libc++.natvis
@@ -2,6 +2,28 @@ <AutoVisualizer xmlns="http://schemas.microsoft.com/vstudio/debugger/natvis/2010"> + <!-- libc++'s __compressed_pair is an internal type used pervasively for + doing the empty base class optimization. + + __compressed_pair<U,V> derives from __compressed_pair_elem<U,0> and + __compressed_pair_elem<V,1>. __compressed_pair_elem<T> is specialized on + a 3rd template parameter: + * if T is empty and non-final the 3rd param is 1 and it derives from T + * else it has a member variable __value_ of type T + --> + <Type Name="std::__1::__compressed_pair_elem<*,*,0>"> + <DisplayString>{__value_}</DisplayString> + <Expand> + <ExpandedItem>__value_</ExpandedItem> + </Expand> + </Type> + <Type Name="std::__1::__compressed_pair_elem<*,*,1>"> + <DisplayString>{*($T1*)this}</DisplayString> + <Expand> + <ExpandedItem>*($T1*)this</ExpandedItem> + </Expand> + </Type> + <!--libc++'s short string optimization: A basic_string is 3 size_t words long. In the "alternate string layout" that we use, they are: pointer to data, size, capacity. @@ -79,6 +101,17 @@ </Expand> </Type> + <Type Name="std::__1::unique_ptr<*>"> + <Intrinsic Name="value" Expression="*($T1**)&__ptr_" /> + <SmartPointer Usage="Minimal">value()</SmartPointer> + <DisplayString Condition="value() == 0">empty</DisplayString> + <DisplayString Condition="value() != 0"> + unique_ptr {value()}</DisplayString> + <Expand> + <Item Condition="value() != 0" Name="[ptr]">value()</Item> + </Expand> + </Type> + <Type Name="std::__1::vector<*>"> <Intrinsic Name="size" Expression="__end_ - __begin_" /> <Expand>
diff --git a/ui/accessibility/ax_enum_util.cc b/ui/accessibility/ax_enum_util.cc index f0764e5..012e3e6 100644 --- a/ui/accessibility/ax_enum_util.cc +++ b/ui/accessibility/ax_enum_util.cc
@@ -1503,6 +1503,12 @@ return "textPosition"; case ax::mojom::IntAttribute::kTextStyle: return "textStyle"; + case ax::mojom::IntAttribute::kTextOverlineStyle: + return "textOverlineStyle"; + case ax::mojom::IntAttribute::kTextStrikethroughStyle: + return "textStrikethroughStyle"; + case ax::mojom::IntAttribute::kTextUnderlineStyle: + return "textUnderlineStyle"; case ax::mojom::IntAttribute::kPreviousFocusId: return "previousFocusId"; case ax::mojom::IntAttribute::kNextFocusId: @@ -1613,6 +1619,12 @@ return ax::mojom::IntAttribute::kTextPosition; if (0 == strcmp(int_attribute, "textStyle")) return ax::mojom::IntAttribute::kTextStyle; + if (0 == strcmp(int_attribute, "textOverlineStyle")) + return ax::mojom::IntAttribute::kTextOverlineStyle; + if (0 == strcmp(int_attribute, "textStrikethroughStyle")) + return ax::mojom::IntAttribute::kTextStrikethroughStyle; + if (0 == strcmp(int_attribute, "textUnderlineStyle")) + return ax::mojom::IntAttribute::kTextUnderlineStyle; if (0 == strcmp(int_attribute, "previousFocusId")) return ax::mojom::IntAttribute::kPreviousFocusId; if (0 == strcmp(int_attribute, "nextFocusId")) @@ -1962,6 +1974,42 @@ return ax::mojom::MarkerType::kNone; } +const char* ToString(ax::mojom::TextDecorationStyle text_decoration_style) { + switch (text_decoration_style) { + case ax::mojom::TextDecorationStyle::kNone: + return "none"; + case ax::mojom::TextDecorationStyle::kSolid: + return "solid"; + case ax::mojom::TextDecorationStyle::kDashed: + return "dashed"; + case ax::mojom::TextDecorationStyle::kDotted: + return "dotted"; + case ax::mojom::TextDecorationStyle::kDouble: + return "double"; + case ax::mojom::TextDecorationStyle::kWavy: + return "wavy"; + } + + return ""; +} + +ax::mojom::TextDecorationStyle ParseTextDecorationStyle( + const char* text_decoration_style) { + if (0 == strcmp(text_decoration_style, "none")) + return ax::mojom::TextDecorationStyle::kNone; + if (0 == strcmp(text_decoration_style, "solid")) + return ax::mojom::TextDecorationStyle::kSolid; + if (0 == strcmp(text_decoration_style, "dashed")) + return ax::mojom::TextDecorationStyle::kDashed; + if (0 == strcmp(text_decoration_style, "dotted")) + return ax::mojom::TextDecorationStyle::kDotted; + if (0 == strcmp(text_decoration_style, "double")) + return ax::mojom::TextDecorationStyle::kDouble; + if (0 == strcmp(text_decoration_style, "wavy")) + return ax::mojom::TextDecorationStyle::kWavy; + return ax::mojom::TextDecorationStyle::kNone; +} + const char* ToString(ax::mojom::TextDirection text_direction) { switch (text_direction) { case ax::mojom::TextDirection::kNone: @@ -2026,6 +2074,8 @@ return "underline"; case ax::mojom::TextStyle::kLineThrough: return "lineThrough"; + case ax::mojom::TextStyle::kOverline: + return "overline"; case ax::mojom::TextStyle::kNone: return "none"; } @@ -2042,6 +2092,8 @@ return ax::mojom::TextStyle::kUnderline; if (0 == strcmp(text_style, "lineThrough")) return ax::mojom::TextStyle::kLineThrough; + if (0 == strcmp(text_style, "overline")) + return ax::mojom::TextStyle::kOverline; return ax::mojom::TextStyle::kNone; }
diff --git a/ui/accessibility/ax_enum_util.h b/ui/accessibility/ax_enum_util.h index d558163a8..3b65385 100644 --- a/ui/accessibility/ax_enum_util.h +++ b/ui/accessibility/ax_enum_util.h
@@ -74,6 +74,12 @@ AX_EXPORT const char* ToString(ax::mojom::MarkerType marker_type); AX_EXPORT ax::mojom::MarkerType ParseMarkerType(const char* marker_type); +// ax:mojom::TextDecorationStyle +AX_EXPORT const char* ToString( + ax::mojom::TextDecorationStyle text_decoration_style); +AX_EXPORT ax::mojom::TextDecorationStyle ParseTextDecorationStyle( + const char* text_decoration_style); + // ax::mojom::TextDirection AX_EXPORT const char* ToString(ax::mojom::TextDirection text_direction); AX_EXPORT ax::mojom::TextDirection ParseTextDirection(
diff --git a/ui/accessibility/ax_enum_util_unittest.cc b/ui/accessibility/ax_enum_util_unittest.cc index 4b248fc2..05b4d1c 100644 --- a/ui/accessibility/ax_enum_util_unittest.cc +++ b/ui/accessibility/ax_enum_util_unittest.cc
@@ -135,6 +135,11 @@ TestEnumStringConversion<ax::mojom::MarkerType>(ParseMarkerType); } +TEST(AXEnumUtilTest, Text_Decoration_Style) { + TestEnumStringConversion<ax::mojom::TextDecorationStyle>( + ParseTextDecorationStyle); +} + TEST(AXEnumUtilTest, TextDirection) { TestEnumStringConversion<ax::mojom::TextDirection>(ParseTextDirection); }
diff --git a/ui/accessibility/ax_enums.mojom b/ui/accessibility/ax_enums.mojom index b69cd18..6eeb9e8 100644 --- a/ui/accessibility/ax_enums.mojom +++ b/ui/accessibility/ax_enums.mojom
@@ -587,6 +587,15 @@ // Bold, italic, underline, etc. kTextStyle, + // The overline text decoration style. + kTextOverlineStyle, + + // The strikethrough text decoration style. + kTextStrikethroughStyle, + + // The underline text decoration style. + kTextUnderlineStyle, + // Focus traversal in views and Android. kPreviousFocusId, kNextFocusId, @@ -775,9 +784,19 @@ kItalic, kUnderline, kLineThrough, + kOverline, kNone, }; +enum TextDecorationStyle { + kNone, + kDotted, + kDashed, + kSolid, + kDouble, + kWavy, +}; + enum AriaCurrentState { kNone, kFalse,
diff --git a/ui/accessibility/ax_node_data.cc b/ui/accessibility/ax_node_data.cc index bb50439..17e07bb 100644 --- a/ui/accessibility/ax_node_data.cc +++ b/ui/accessibility/ax_node_data.cc
@@ -147,6 +147,9 @@ case ax::mojom::IntAttribute::kTextDirection: case ax::mojom::IntAttribute::kTextPosition: case ax::mojom::IntAttribute::kTextStyle: + case ax::mojom::IntAttribute::kTextOverlineStyle: + case ax::mojom::IntAttribute::kTextStrikethroughStyle: + case ax::mojom::IntAttribute::kTextUnderlineStyle: case ax::mojom::IntAttribute::kAriaColumnCount: case ax::mojom::IntAttribute::kAriaCellColumnIndex: case ax::mojom::IntAttribute::kAriaRowCount: @@ -1058,9 +1061,26 @@ text_style_value += "underline,"; if (HasTextStyle(ax::mojom::TextStyle::kLineThrough)) text_style_value += "line-through,"; + if (HasTextStyle(ax::mojom::TextStyle::kOverline)) + text_style_value += "overline,"; result += text_style_value.substr(0, text_style_value.size() - 1); break; } + case ax::mojom::IntAttribute::kTextOverlineStyle: + result += std::string(" text_overline_style=") + + ui::ToString(static_cast<ax::mojom::TextDecorationStyle>( + int_attribute.second)); + break; + case ax::mojom::IntAttribute::kTextStrikethroughStyle: + result += std::string(" text_strikethrough_style=") + + ui::ToString(static_cast<ax::mojom::TextDecorationStyle>( + int_attribute.second)); + break; + case ax::mojom::IntAttribute::kTextUnderlineStyle: + result += std::string(" text_underline_style=") + + ui::ToString(static_cast<ax::mojom::TextDecorationStyle>( + int_attribute.second)); + break; case ax::mojom::IntAttribute::kSetSize: result += " setsize=" + value; break;
diff --git a/ui/accessibility/platform/ax_platform_node_textrangeprovider_win_unittest.cc b/ui/accessibility/platform/ax_platform_node_textrangeprovider_win_unittest.cc index 9d047706..32d24aaa 100644 --- a/ui/accessibility/platform/ax_platform_node_textrangeprovider_win_unittest.cc +++ b/ui/accessibility/platform/ax_platform_node_textrangeprovider_win_unittest.cc
@@ -1482,6 +1482,10 @@ text_data.role = ax::mojom::Role::kStaticText; text_data.AddStringAttribute(ax::mojom::StringAttribute::kFontFamily, "sans"); text_data.AddFloatAttribute(ax::mojom::FloatAttribute::kFontWeight, 300); + text_data.AddIntAttribute(ax::mojom::IntAttribute::kTextOverlineStyle, 1); + text_data.AddIntAttribute(ax::mojom::IntAttribute::kTextStrikethroughStyle, + 2); + text_data.AddIntAttribute(ax::mojom::IntAttribute::kTextUnderlineStyle, 3); text_data.SetName("some text"); ui::AXNodeData more_text_data; @@ -1563,6 +1567,21 @@ expected_mixed_variant); expected_variant.Reset(); + expected_variant.Set(TextDecorationLineStyle::TextDecorationLineStyle_Dot); + EXPECT_UIA_TEXTATTRIBUTE_EQ(text_range_provider, UIA_OverlineStyleAttributeId, + expected_variant); + expected_variant.Reset(); + + expected_variant.Set(TextDecorationLineStyle::TextDecorationLineStyle_Dash); + EXPECT_UIA_TEXTATTRIBUTE_EQ( + text_range_provider, UIA_StrikethroughStyleAttributeId, expected_variant); + expected_variant.Reset(); + + expected_variant.Set(TextDecorationLineStyle::TextDecorationLineStyle_Single); + EXPECT_UIA_TEXTATTRIBUTE_EQ(text_range_provider, + UIA_UnderlineStyleAttributeId, expected_variant); + expected_variant.Reset(); + base::string16 style_name = base::UTF8ToUTF16(""); expected_variant.Set(SysAllocString(style_name.c_str())); EXPECT_UIA_TEXTATTRIBUTE_EQ(text_range_provider, UIA_StyleNameAttributeId,
diff --git a/ui/accessibility/platform/ax_platform_node_win.cc b/ui/accessibility/platform/ax_platform_node_win.cc index cdf0a9058..ffe54e4 100644 --- a/ui/accessibility/platform/ax_platform_node_win.cc +++ b/ui/accessibility/platform/ax_platform_node_win.cc
@@ -4040,10 +4040,25 @@ V_VT(result) = VT_BOOL; V_BOOL(result) = IsInvisibleOrIgnored() ? VARIANT_TRUE : VARIANT_FALSE; break; + case UIA_OverlineStyleAttributeId: + V_VT(result) = VT_I4; + V_I4(result) = GetUIATextDecorationStyle( + ax::mojom::IntAttribute::kTextOverlineStyle); + break; + case UIA_StrikethroughStyleAttributeId: + V_VT(result) = VT_I4; + V_I4(result) = GetUIATextDecorationStyle( + ax::mojom::IntAttribute::kTextStrikethroughStyle); + break; case UIA_StyleNameAttributeId: V_VT(result) = VT_BSTR; V_BSTR(result) = GetStyleNameAttributeAsBSTR(); break; + case UIA_UnderlineStyleAttributeId: + V_VT(result) = VT_I4; + V_I4(result) = GetUIATextDecorationStyle( + ax::mojom::IntAttribute::kTextUnderlineStyle); + break; default: V_VT(result) = VT_UNKNOWN; return ::UiaGetReservedNotSupportedValue(&V_UNKNOWN(result)); @@ -6577,6 +6592,28 @@ return SysAllocString(style_name.c_str()); } +TextDecorationLineStyle AXPlatformNodeWin::GetUIATextDecorationStyle( + const ax::mojom::IntAttribute int_attribute) const { + const ax::mojom::TextDecorationStyle text_decoration_style = + static_cast<ax::mojom::TextDecorationStyle>( + GetIntAttribute(int_attribute)); + + switch (text_decoration_style) { + case ax::mojom::TextDecorationStyle::kNone: + return TextDecorationLineStyle::TextDecorationLineStyle_None; + case ax::mojom::TextDecorationStyle::kDotted: + return TextDecorationLineStyle::TextDecorationLineStyle_Dot; + case ax::mojom::TextDecorationStyle::kDashed: + return TextDecorationLineStyle::TextDecorationLineStyle_Dash; + case ax::mojom::TextDecorationStyle::kSolid: + return TextDecorationLineStyle::TextDecorationLineStyle_Single; + case ax::mojom::TextDecorationStyle::kDouble: + return TextDecorationLineStyle::TextDecorationLineStyle_Double; + case ax::mojom::TextDecorationStyle::kWavy: + return TextDecorationLineStyle::TextDecorationLineStyle_Wavy; + } +} + // IRawElementProviderSimple support methods. AXPlatformNodeWin::PatternProviderFactoryMethod
diff --git a/ui/accessibility/platform/ax_platform_node_win.h b/ui/accessibility/platform/ax_platform_node_win.h index ee9e2a9..7efa9ec 100644 --- a/ui/accessibility/platform/ax_platform_node_win.h +++ b/ui/accessibility/platform/ax_platform_node_win.h
@@ -1211,6 +1211,10 @@ // Helper to get the UIA StyleName for this node as a BSTR. BSTR GetStyleNameAttributeAsBSTR() const; + // Gets the TextDecorationLineStyle based on the provided int attribute. + TextDecorationLineStyle GetUIATextDecorationStyle( + const ax::mojom::IntAttribute int_attribute) const; + // IRawElementProviderSimple support methods. using PatternProviderFactoryMethod = HRESULT (*)(AXPlatformNodeWin*,
diff --git a/ui/aura/mus/in_flight_change.cc b/ui/aura/mus/in_flight_change.cc index a24ff32..946ff5ce 100644 --- a/ui/aura/mus/in_flight_change.cc +++ b/ui/aura/mus/in_flight_change.cc
@@ -85,12 +85,14 @@ WindowTreeClient* window_tree_client, WindowMus* window, const gfx::Rect& revert_bounds, + ui::WindowShowState revert_show_state, bool from_server, const base::Optional<viz::LocalSurfaceIdAllocation>& revert_local_surface_id_allocation) : InFlightChange(window, ChangeType::BOUNDS), window_tree_client_(window_tree_client), revert_bounds_(revert_bounds), + revert_show_state_(revert_show_state), from_server_(from_server), revert_local_surface_id_allocation_(revert_local_surface_id_allocation) { // Should always be created with a window. @@ -100,17 +102,17 @@ InFlightBoundsChange::~InFlightBoundsChange() {} void InFlightBoundsChange::SetRevertValueFrom(const InFlightChange& change) { - from_server_ = static_cast<const InFlightBoundsChange&>(change).from_server_; - revert_bounds_ = - static_cast<const InFlightBoundsChange&>(change).revert_bounds_; + const auto& from = static_cast<const InFlightBoundsChange&>(change); + from_server_ = from.from_server_; + revert_bounds_ = from.revert_bounds_; + revert_show_state_ = from.revert_show_state_; revert_local_surface_id_allocation_ = - static_cast<const InFlightBoundsChange&>(change) - .revert_local_surface_id_allocation_; + from.revert_local_surface_id_allocation_; } void InFlightBoundsChange::Revert() { window_tree_client_->SetWindowBoundsFromServer( - window(), revert_bounds_, from_server_, + window(), revert_bounds_, revert_show_state_, from_server_, revert_local_surface_id_allocation_); }
diff --git a/ui/aura/mus/in_flight_change.h b/ui/aura/mus/in_flight_change.h index 9fd7f4a7..97ad8de0 100644 --- a/ui/aura/mus/in_flight_change.h +++ b/ui/aura/mus/in_flight_change.h
@@ -155,6 +155,7 @@ InFlightBoundsChange(WindowTreeClient* window_tree_client, WindowMus* window, const gfx::Rect& revert_bounds, + ui::WindowShowState revert_show_state, bool from_server, const base::Optional<viz::LocalSurfaceIdAllocation>& local_surface_id_allocation); @@ -168,6 +169,7 @@ private: WindowTreeClient* window_tree_client_; gfx::Rect revert_bounds_; + ui::WindowShowState revert_show_state_; // If true the change originated from the server. If false, the change was // initiated by this client. bool from_server_;
diff --git a/ui/aura/mus/window_port_mus.cc b/ui/aura/mus/window_port_mus.cc index 579ecdf..404043f 100644 --- a/ui/aura/mus/window_port_mus.cc +++ b/ui/aura/mus/window_port_mus.cc
@@ -718,6 +718,14 @@ if (key == client::kDragDropDelegateKey) { SetCanAcceptDrops(window_->GetProperty(client::kDragDropDelegateKey) != nullptr); + } else if (key == client::kShowStateKey && + WindowTreeHostMus::ForWindow(window_) && + WindowTreeHostMus::ForWindow(window_) + ->is_server_setting_bounds()) { + // When the server is setting the bounds, it also provides a show state. + // When that's being applied, don't report the show state back to the + // server. + return; } ServerChangeData change_data;
diff --git a/ui/aura/mus/window_tree_client.cc b/ui/aura/mus/window_tree_client.cc index 5e93250..db7aff9 100644 --- a/ui/aura/mus/window_tree_client.cc +++ b/ui/aura/mus/window_tree_client.cc
@@ -510,7 +510,8 @@ window_data.visible); WindowMus* window = WindowMus::Get(window_tree_host->window()); - SetWindowBoundsFromServer(window, window_data.bounds, /* from_server */ true, + SetWindowBoundsFromServer(window, window_data.bounds, window_data.state, + /* from_server */ true, local_surface_id_allocation); return window_tree_host; } @@ -634,6 +635,7 @@ void WindowTreeClient::SetWindowBoundsFromServer( WindowMus* window, const gfx::Rect& revert_bounds, + ui::WindowShowState state, bool from_server, const base::Optional<viz::LocalSurfaceIdAllocation>& local_surface_id_allocation) { @@ -661,7 +663,7 @@ window_tree_host->TakePendingLocalSurfaceIdFromServer(); window->UpdateLocalSurfaceIdFromParent(*local_surface_id_allocation); - window_tree_host->SetBoundsFromServer(revert_bounds, + window_tree_host->SetBoundsFromServer(revert_bounds, state, window->GetLocalSurfaceIdAllocation()); window->DidSetWindowTreeHostBoundsFromServer(); @@ -681,7 +683,7 @@ const viz::LocalSurfaceIdAllocation lsia = window->GetWindow()->GetLocalSurfaceIdAllocation(); window_tree_host->SetBoundsFromServer(window_tree_host->bounds_in_dip(), - lsia); + ui::SHOW_STATE_DEFAULT, lsia); // Send the newly generated id to the server. This does *not* use // WindowTreeHost:SetBounds() (which notifies the server of a bounds and id) // as WindowTreeHost::SetBounds() leads to race conditions. In particular, @@ -691,7 +693,7 @@ tree_->UpdateLocalSurfaceIdFromChild(window->server_id(), lsia); } else { window_tree_host->SetBoundsFromServer( - window_tree_host->bounds_in_dip(), + window_tree_host->bounds_in_dip(), ui::SHOW_STATE_DEFAULT, window->GetLocalSurfaceIdAllocation()); } DCHECK(!window_tree_host->has_pending_local_surface_id_from_server()); @@ -741,8 +743,8 @@ } const uint32_t change_id = ScheduleInFlightChange(std::make_unique<InFlightBoundsChange>( - this, window, old_bounds_in_dip, /* from_server */ false, - local_surface_id_allocation)); + this, window, old_bounds_in_dip, ui::SHOW_STATE_DEFAULT, + /* from_server */ false, local_surface_id_allocation)); tree_->SetWindowBounds(change_id, window->server_id(), new_bounds_in_dip, local_surface_id_allocation); } @@ -1173,7 +1175,7 @@ const gfx::Rect bounds(data->bounds); { - InFlightBoundsChange bounds_change(this, window, bounds, + InFlightBoundsChange bounds_change(this, window, bounds, data->state, /* from_server */ true, local_surface_id_allocation); InFlightChange* current_change = @@ -1186,12 +1188,14 @@ local_surface_id_allocation); } else if (window->GetWindow()->GetBoundsInScreen() != bounds) { window->UpdateLocalSurfaceIdFromParent(local_surface_id_allocation); - SetWindowBoundsFromServer(window, bounds, /* from_server */ true, + SetWindowBoundsFromServer(window, bounds, data->state, + /* from_server */ true, window->GetLocalSurfaceIdAllocation()); } else { // No pending changes and the bounds match that of the server. Call // SetWindowBoundsFromServer() to apply |local_surface_id_allocation|. - SetWindowBoundsFromServer(window, bounds, /* from_server */ true, + SetWindowBoundsFromServer(window, bounds, data->state, + /* from_server */ true, local_surface_id_allocation); } } @@ -1219,6 +1223,7 @@ void WindowTreeClient::OnWindowBoundsChanged( ws::Id window_id, const gfx::Rect& new_bounds, + ui::WindowShowState state, const base::Optional<viz::LocalSurfaceIdAllocation>& local_surface_id_allocation) { WindowMus* window = GetWindowByServerId(window_id); @@ -1233,7 +1238,7 @@ } TRACE_EVENT0("ui", "WindowTreeClient::OnWindowBoundsChanged"); - InFlightBoundsChange new_change(this, window, new_bounds, + InFlightBoundsChange new_change(this, window, new_bounds, state, /* from_server */ true, local_surface_id_allocation); @@ -1263,7 +1268,7 @@ return; } - SetWindowBoundsFromServer(window, new_bounds, /* from_server */ true, + SetWindowBoundsFromServer(window, new_bounds, state, /* from_server */ true, local_surface_id_allocation); }
diff --git a/ui/aura/mus/window_tree_client.h b/ui/aura/mus/window_tree_client.h index 04a0a3d..815fdaf3 100644 --- a/ui/aura/mus/window_tree_client.h +++ b/ui/aura/mus/window_tree_client.h
@@ -337,6 +337,7 @@ void SetWindowBoundsFromServer( WindowMus* window, const gfx::Rect& revert_bounds, + ui::WindowShowState state, bool from_server, const base::Optional<viz::LocalSurfaceIdAllocation>& local_surface_id_allocation); @@ -415,6 +416,7 @@ void OnWindowBoundsChanged( ws::Id window_id, const gfx::Rect& new_bounds, + ui::WindowShowState state, const base::Optional<viz::LocalSurfaceIdAllocation>& local_surface_id_allocation) override; void OnWindowTransformChanged(ws::Id window_id,
diff --git a/ui/aura/mus/window_tree_client_unittest.cc b/ui/aura/mus/window_tree_client_unittest.cc index 330962f..1e434143 100644 --- a/ui/aura/mus/window_tree_client_unittest.cc +++ b/ui/aura/mus/window_tree_client_unittest.cc
@@ -158,6 +158,49 @@ DISALLOW_COPY_AND_ASSIGN(TestEventObserver); }; +// Helps verify that updates to window bounds and window state are kept in sync +// from the perspective of the WindowObserver. +class WindowBoundsChangeVerifier : public WindowObserver { + public: + WindowBoundsChangeVerifier(aura::Window* window, + ui::WindowShowState expected_state, + const gfx::Rect& expected_bounds) + : window_(window), + expected_state_(expected_state), + expected_bounds_(expected_bounds) { + window->AddObserver(this); + } + + ~WindowBoundsChangeVerifier() override { window_->RemoveObserver(this); } + + // WindowObserver: + void OnWindowPropertyChanged(Window* window, + const void* key, + intptr_t old) override { + if (key == client::kShowStateKey) + Verify(); + } + + void OnWindowBoundsChanged(Window* window, + const gfx::Rect& old_bounds, + const gfx::Rect& new_bounds, + ui::PropertyChangeReason reason) override { + Verify(); + } + + private: + void Verify() { + EXPECT_EQ(expected_state_, window_->GetProperty(client::kShowStateKey)); + EXPECT_EQ(expected_bounds_, window_->bounds()); + } + + Window* window_; + const ui::WindowShowState expected_state_; + const gfx::Rect expected_bounds_; + + DISALLOW_COPY_AND_ASSIGN(WindowBoundsChangeVerifier); +}; + } // namespace class WindowTreeClientTest : public test::AuraMusClientTestBase { @@ -526,7 +569,7 @@ viz::LocalSurfaceId(1, base::UnguessableToken::Create()), base::TimeTicks::Now()); window_tree_client()->OnWindowBoundsChanged( - server_id(&root_window), server_changed_bounds, + server_id(&root_window), server_changed_bounds, ui::SHOW_STATE_DEFAULT, server_changed_local_surface_id_allocation); WindowMus* root_window_mus = WindowMus::Get(&root_window); @@ -546,8 +589,9 @@ root_window_mus->GetLocalSurfaceIdAllocation()); // Simulate server changing back to original bounds. Should take immediately. - window_tree_client()->OnWindowBoundsChanged(server_id(&root_window), - original_bounds, base::nullopt); + window_tree_client()->OnWindowBoundsChanged( + server_id(&root_window), original_bounds, ui::SHOW_STATE_DEFAULT, + base::nullopt); EXPECT_EQ(original_bounds, root_window.bounds()); } @@ -2497,7 +2541,7 @@ const viz::LocalSurfaceIdAllocation lsia2 = GenerateLocalSurfaceIdForNewTopLevel(); window_tree_client()->OnWindowBoundsChanged(server_id(top_level), bounds, - lsia2); + ui::SHOW_STATE_DEFAULT, lsia2); EXPECT_EQ(0u, window_tree()->GetChangeCountForType(WindowTreeChangeType::BOUNDS)); // The local surface id is updated from lsia2, so it won't match with either. @@ -2505,19 +2549,52 @@ EXPECT_EQ(lsia2, top_level->GetLocalSurfaceIdAllocation()); } +// Regression test for https://crbug.com/943509 +TEST_F(WindowTreeClientTest, SetBoundsAlsoChangesShowState) { + const gfx::Rect new_bounds(gfx::Rect(0, 0, 100, 100)); + ASSERT_NE(new_bounds, root_window()->bounds()); + root_window()->SetBounds(new_bounds); + EXPECT_EQ(new_bounds, root_window()->bounds()); + + root_window()->SetProperty(aura::client::kShowStateKey, + ui::SHOW_STATE_NORMAL); + + gfx::Rect maximized_bounds(0, 0, 500, 500); + WindowBoundsChangeVerifier verifier(root_window(), ui::SHOW_STATE_MAXIMIZED, + maximized_bounds); + window_tree_client()->OnWindowBoundsChanged( + server_id(root_window()), maximized_bounds, ui::SHOW_STATE_MAXIMIZED, + GenerateLocalSurfaceIdForNewTopLevel()); +} + TEST_F(WindowTreeClientTestHighDPI, SetBounds) { const gfx::Rect new_bounds(gfx::Rect(0, 0, 100, 100)); ASSERT_NE(new_bounds, root_window()->bounds()); root_window()->SetBounds(new_bounds); EXPECT_EQ(new_bounds, root_window()->bounds()); + root_window()->SetProperty(aura::client::kShowStateKey, + ui::SHOW_STATE_NORMAL); + // Simulate the server responding with a bounds change. Server operates in // dips. const gfx::Rect server_changed_bounds(gfx::Rect(0, 0, 200, 200)); window_tree_client()->OnWindowBoundsChanged( - server_id(root_window()), server_changed_bounds, + server_id(root_window()), server_changed_bounds, ui::SHOW_STATE_DEFAULT, GenerateLocalSurfaceIdForNewTopLevel()); EXPECT_EQ(server_changed_bounds, root_window()->bounds()); + EXPECT_EQ(ui::SHOW_STATE_NORMAL, + root_window()->GetProperty(aura::client::kShowStateKey)); + + // Simulate the server responding with a bounds change along with a state + // update. Both should take effect. + const gfx::Rect server_changed_bounds2(gfx::Rect(0, 0, 300, 200)); + window_tree_client()->OnWindowBoundsChanged( + server_id(root_window()), server_changed_bounds2, + ui::SHOW_STATE_MAXIMIZED, GenerateLocalSurfaceIdForNewTopLevel()); + EXPECT_EQ(server_changed_bounds2, root_window()->bounds()); + EXPECT_EQ(ui::SHOW_STATE_MAXIMIZED, + root_window()->GetProperty(aura::client::kShowStateKey)); } TEST_F(WindowTreeClientTestHighDPI, NewTopLevelWindowBounds) { @@ -3078,7 +3155,7 @@ parent_local_surface_id_allocator_.GenerateId(); const gfx::Rect server_changed_bounds(gfx::Rect(0, 0, 200, 200)); window_tree_client()->OnWindowBoundsChanged( - server_id(root), server_changed_bounds, + server_id(root), server_changed_bounds, ui::SHOW_STATE_DEFAULT, parent_local_surface_id_allocator_.GetCurrentLocalSurfaceIdAllocation()); const viz::LocalSurfaceId local_surface_id1 = root->GetLocalSurfaceIdAllocation().local_surface_id();
diff --git a/ui/aura/mus/window_tree_host_mus.cc b/ui/aura/mus/window_tree_host_mus.cc index 4bd6f08..babd2f46 100644 --- a/ui/aura/mus/window_tree_host_mus.cc +++ b/ui/aura/mus/window_tree_host_mus.cc
@@ -8,6 +8,7 @@ #include <utility> #include "base/bind.h" +#include "ui/aura/client/aura_constants.h" #include "ui/aura/env.h" #include "ui/aura/mus/input_method_mus.h" #include "ui/aura/mus/mus_types.h" @@ -165,7 +166,11 @@ std::make_unique<Window>(nullptr, std::move(init_params.window_port))), display_id_(init_params.display_id), - delegate_(init_params.window_tree_client) { + delegate_(init_params.window_tree_client), + show_state_observer_( + window(), + base::BindRepeating(&WindowTreeHostMus::OnWindowShowStateDidChange, + base::Unretained(this))) { gfx::Rect bounds_in_pixels; window()->SetProperty(kWindowTreeHostMusKey, this); // TODO(sky): find a cleaner way to set this! Revisit this now that @@ -294,8 +299,23 @@ void WindowTreeHostMus::SetBoundsFromServer( const gfx::Rect& bounds, + ui::WindowShowState state, const viz::LocalSurfaceIdAllocation& local_surface_id_allocation) { base::AutoReset<bool> resetter(&is_server_setting_bounds_, true); + // When there's a non-default |state|, we want to set that property and then + // the bounds, so that by the time client code observes a bounds change the + // show state is already updated, and by the time client code observes a state + // change the bounds are already updated as well. To do this, we set the state + // here, and as the first WindowObserver on |window()|, update the bounds. + if (state != ui::SHOW_STATE_DEFAULT && + window()->GetProperty(aura::client::kShowStateKey) != state) { + server_bounds_ = &bounds; + server_lsia_ = &local_surface_id_allocation; + window()->SetProperty(aura::client::kShowStateKey, state); + DCHECK(!server_bounds_); + DCHECK(!server_lsia_); + return; + } SetBounds(bounds, local_surface_id_allocation); } @@ -429,14 +449,52 @@ return true; } +//////////////////////////////////////////////////////////////////////////////// +// WindowTreeHostMus, protected: + void WindowTreeHostMus::SetBoundsInPixels( const gfx::Rect& bounds, const viz::LocalSurfaceIdAllocation& local_surface_id_allocation) { // As UI code operates in DIPs (as does the window-service APIs), this - // function is very seldomly uses, and so converts to DIPs. + // function is very seldomly used, and so converts to DIPs. SetBounds( gfx::ConvertRectToDIP(ui::GetScaleFactorForNativeView(window()), bounds), local_surface_id_allocation); } +//////////////////////////////////////////////////////////////////////////////// +// WindowTreeHostMus, private: + +void WindowTreeHostMus::OnWindowShowStateDidChange() { + if (!server_bounds_) + return; + + DCHECK(is_server_setting_bounds_); + DCHECK(server_lsia_); + SetBounds(*server_bounds_, *server_lsia_); + server_bounds_ = nullptr; + server_lsia_ = nullptr; +} + +//////////////////////////////////////////////////////////////////////////////// +// WindowTreeHostMus::WindowShowStateChangeObserver, public: + +WindowTreeHostMus::WindowShowStateChangeObserver::WindowShowStateChangeObserver( + aura::Window* window, + base::RepeatingClosure show_state_changed_callback) + : show_state_changed_callback_(show_state_changed_callback) { + window->AddObserver(this); +} + +WindowTreeHostMus::WindowShowStateChangeObserver:: + ~WindowShowStateChangeObserver() = default; + +void WindowTreeHostMus::WindowShowStateChangeObserver::OnWindowPropertyChanged( + aura::Window* window, + const void* key, + intptr_t old) { + if (key == client::kShowStateKey) + show_state_changed_callback_.Run(); +} + } // namespace aura
diff --git a/ui/aura/mus/window_tree_host_mus.h b/ui/aura/mus/window_tree_host_mus.h index 25037ba81..bc1a37b 100644 --- a/ui/aura/mus/window_tree_host_mus.h +++ b/ui/aura/mus/window_tree_host_mus.h
@@ -15,6 +15,7 @@ #include "services/ws/public/mojom/window_tree_constants.mojom.h" #include "ui/aura/aura_export.h" #include "ui/aura/mus/input_method_mus_delegate.h" +#include "ui/aura/window_observer.h" #include "ui/aura/window_tree_host_platform.h" #include "ui/base/ime/mojo/ime.mojom.h" #include "ui/base/mojo/ui_base_types.mojom.h" @@ -46,6 +47,7 @@ const viz::LocalSurfaceIdAllocation& local_surface_id_allocation); void SetBoundsFromServer( const gfx::Rect& bounds, + ui::WindowShowState state, const viz::LocalSurfaceIdAllocation& local_surface_id_allocation); const gfx::Rect& bounds_in_dip() const { return bounds_in_dip_; } @@ -124,6 +126,8 @@ bool ConnectToImeEngine(ime::mojom::ImeEngineRequest engine_request, ime::mojom::ImeEngineClientPtr client) override; + bool is_server_setting_bounds() const { return is_server_setting_bounds_; } + protected: // This is in the protected section as SetBounds() is preferred. // aura::WindowTreeHostPlatform: @@ -132,9 +136,29 @@ const viz::LocalSurfaceIdAllocation& local_surface_id_allocation = viz::LocalSurfaceIdAllocation()) override; - bool is_server_setting_bounds() const { return is_server_setting_bounds_; } - private: + // This class observes |window()| and runs a callback every time the show + // state has changed. + class WindowShowStateChangeObserver : public WindowObserver { + public: + WindowShowStateChangeObserver( + aura::Window* window, + base::RepeatingClosure show_state_changed_callback); + ~WindowShowStateChangeObserver() override; + + // WindowObserver: + void OnWindowPropertyChanged(aura::Window* window, + const void* key, + intptr_t old) override; + + private: + base::RepeatingClosure show_state_changed_callback_; + + DISALLOW_COPY_AND_ASSIGN(WindowShowStateChangeObserver); + }; + + void OnWindowShowStateDidChange(); + int64_t display_id_; WindowTreeHostMusDelegate* delegate_; @@ -142,6 +166,13 @@ // If true, the server initiated the bounds change. bool is_server_setting_bounds_ = false; + // These two properties point to the arguments passed into + // SetBoundsFromServer, or null if that method isn't currently on the call + // stack. They're only set when the window's show state needs to change in + // addition to the bounds. + const gfx::Rect* server_bounds_ = nullptr; + const viz::LocalSurfaceIdAllocation* server_lsia_ = nullptr; + std::unique_ptr<InputMethodMus> input_method_mus_; base::Optional<viz::LocalSurfaceIdAllocation> @@ -149,6 +180,10 @@ gfx::Rect bounds_in_dip_; + // Start observing the window early (at construction time), because |this| + // needs to receive updates to the show state before other observers. + WindowShowStateChangeObserver show_state_observer_; + DISALLOW_COPY_AND_ASSIGN(WindowTreeHostMus); };
diff --git a/ui/base/mojo/ui_base_types.mojom b/ui/base/mojo/ui_base_types.mojom index ab2d4e5..00b6d98 100644 --- a/ui/base/mojo/ui_base_types.mojom +++ b/ui/base/mojo/ui_base_types.mojom
@@ -4,6 +4,16 @@ module ui.mojom; +// Window "show" state. +enum WindowShowState { + kDefault, + kNormal, + kMinimized, + kMaximized, + kInactive, + kFullscreen, +}; + // Dialog button identifiers used to specify which buttons to show the user. enum DialogButton { NONE,
diff --git a/ui/base/mojo/ui_base_types.typemap b/ui/base/mojo/ui_base_types.typemap index 9bad08ab..b60fb698 100644 --- a/ui/base/mojo/ui_base_types.typemap +++ b/ui/base/mojo/ui_base_types.typemap
@@ -15,4 +15,5 @@ # Note that HitTestCompat can't be used because it's not defined in Windows. "ui.mojom.HitTest=int", + "ui.mojom.WindowShowState=ui::WindowShowState", ]
diff --git a/ui/base/mojo/ui_base_types_struct_traits.h b/ui/base/mojo/ui_base_types_struct_traits.h index c91371d..3b61034ca 100644 --- a/ui/base/mojo/ui_base_types_struct_traits.h +++ b/ui/base/mojo/ui_base_types_struct_traits.h
@@ -5,9 +5,14 @@ #ifndef UI_BASE_MOJO_UI_BASE_TYPES_STRUCT_TRAITS_H_ #define UI_BASE_MOJO_UI_BASE_TYPES_STRUCT_TRAITS_H_ +#if defined(OS_WIN) +#include <windows.h> +#else +#include "ui/base/hit_test.h" +#endif + #include "build/build_config.h" #include "mojo/public/cpp/bindings/enum_traits.h" -#include "ui/base/hit_test.h" #include "ui/base/mojo/ui_base_types.mojom.h" #include "ui/base/ui_base_types.h" @@ -246,6 +251,57 @@ } }; +template <> +struct EnumTraits<ui::mojom::WindowShowState, ui::WindowShowState> { + static ui::mojom::WindowShowState ToMojom(ui::WindowShowState modal_type) { + switch (modal_type) { + case ui::SHOW_STATE_DEFAULT: + return ui::mojom::WindowShowState::kDefault; + case ui::SHOW_STATE_NORMAL: + return ui::mojom::WindowShowState::kNormal; + case ui::SHOW_STATE_MINIMIZED: + return ui::mojom::WindowShowState::kMinimized; + case ui::SHOW_STATE_MAXIMIZED: + return ui::mojom::WindowShowState::kMaximized; + case ui::SHOW_STATE_INACTIVE: + return ui::mojom::WindowShowState::kInactive; + case ui::SHOW_STATE_FULLSCREEN: + return ui::mojom::WindowShowState::kFullscreen; + case ui::SHOW_STATE_END: + NOTREACHED(); + break; + } + NOTREACHED(); + return ui::mojom::WindowShowState::kDefault; + } + + static bool FromMojom(ui::mojom::WindowShowState modal_type, + ui::WindowShowState* out) { + switch (modal_type) { + case ui::mojom::WindowShowState::kDefault: + *out = ui::SHOW_STATE_DEFAULT; + return true; + case ui::mojom::WindowShowState::kNormal: + *out = ui::SHOW_STATE_NORMAL; + return true; + case ui::mojom::WindowShowState::kMinimized: + *out = ui::SHOW_STATE_MINIMIZED; + return true; + case ui::mojom::WindowShowState::kMaximized: + *out = ui::SHOW_STATE_MAXIMIZED; + return true; + case ui::mojom::WindowShowState::kInactive: + *out = ui::SHOW_STATE_INACTIVE; + return true; + case ui::mojom::WindowShowState::kFullscreen: + *out = ui::SHOW_STATE_FULLSCREEN; + return true; + } + NOTREACHED(); + return false; + } +}; + } // namespace mojo #endif // UI_BASE_MOJO_WINDOW_OPEN_DISPOSITION_STRUCT_TRAITS_H_
diff --git a/ui/compositor/layer_unittest.cc b/ui/compositor/layer_unittest.cc index 79fcc7d3..a8ba54425 100644 --- a/ui/compositor/layer_unittest.cc +++ b/ui/compositor/layer_unittest.cc
@@ -61,6 +61,10 @@ #include "ui/gfx/interpolated_transform.h" #include "ui/gfx/skia_util.h" +#if defined(OS_WIN) +#include "base/win/windows_version.h" +#endif + using cc::MatchesPNGFile; using cc::WritePNGFile; @@ -134,7 +138,7 @@ LayerWithRealCompositorTest() : scoped_task_environment_( base::test::ScopedTaskEnvironment::MainThreadType::UI) { - gfx::FontList::SetDefaultFontDescription("Arial, Times New Roman, 15px"); + gfx::FontList::SetDefaultFontDescription("Segoe UI, 15px"); } ~LayerWithRealCompositorTest() override {} @@ -1890,7 +1894,12 @@ ReadPixels(&bitmap); ASSERT_FALSE(bitmap.empty()); - base::FilePath ref_img = test_data_dir().AppendASCII("string_faded.png"); + std::string filename; + if (base::win::GetVersion() < base::win::VERSION_WIN10) + filename = "string_faded_win7.png"; + else + filename = "string_faded_win10.png"; + base::FilePath ref_img = test_data_dir().AppendASCII(filename); // WritePNGFile(bitmap, ref_img, true); float percentage_pixels_large_error = 8.0f; // 200px / (50*50)
diff --git a/ui/gfx/platform_font_win.cc b/ui/gfx/platform_font_win.cc index 25b00ba..59fe1faa 100644 --- a/ui/gfx/platform_font_win.cc +++ b/ui/gfx/platform_font_win.cc
@@ -37,6 +37,7 @@ #include "ui/gfx/font.h" #include "ui/gfx/font_render_params.h" #include "ui/gfx/system_fonts_win.h" +#include "ui/gfx/win/direct_write.h" #include "ui/gfx/win/scoped_set_map_mode.h" namespace { @@ -238,8 +239,6 @@ // static PlatformFontWin::HFontRef* PlatformFontWin::base_font_ref_; -IDWriteFactory* PlatformFontWin::direct_write_factory_ = nullptr; - // TODO(ananta) // Remove the CHECKs in this function once this stabilizes on the field. HRESULT GetFamilyNameFromDirectWriteFont(IDWriteFont* dwrite_font, @@ -277,19 +276,6 @@ InitWithFontNameAndSize(font_name, font_size); } -// static -void PlatformFontWin::SetDirectWriteFactory(IDWriteFactory* factory) { - // We grab a reference on the DirectWrite factory. This reference is - // leaked, which is ok because skia leaks it as well. - factory->AddRef(); - direct_write_factory_ = factory; -} - -// static -bool PlatformFontWin::IsDirectWriteEnabled() { - return direct_write_factory_ != nullptr; -} - //////////////////////////////////////////////////////////////////////////////// // PlatformFontWin, PlatformFont implementation: @@ -425,10 +411,7 @@ GetTextMetricsForFont(screen_dc, font, &font_metrics); } - if (IsDirectWriteEnabled()) - return CreateHFontRefFromSkia(font, font_metrics); - - return CreateHFontRefFromGDI(font, font_metrics); + return CreateHFontRefFromSkia(font, font_metrics); } PlatformFontWin::HFontRef* PlatformFontWin::CreateHFontRefFromGDI( @@ -489,7 +472,7 @@ // DirectWrite to calculate the cap height. Microsoft::WRL::ComPtr<IDWriteFont> dwrite_font; HRESULT hr = GetMatchingDirectWriteFont( - &font_info, italic, direct_write_factory_, dwrite_font.GetAddressOf()); + &font_info, italic, win::GetDirectWriteFactory(), &dwrite_font); if (FAILED(hr)) { CHECK(false); return nullptr;
diff --git a/ui/gfx/platform_font_win.h b/ui/gfx/platform_font_win.h index 97ce5073..b4e538e 100644 --- a/ui/gfx/platform_font_win.h +++ b/ui/gfx/platform_font_win.h
@@ -60,8 +60,6 @@ // from skia and DirectWrite. static void SetDirectWriteFactory(IDWriteFactory* factory); - static bool IsDirectWriteEnabled(); - private: FRIEND_TEST_ALL_PREFIXES(PlatformFontWinTest, Metrics_SkiaVersusGDI); FRIEND_TEST_ALL_PREFIXES(PlatformFontWinTest, DirectWriteFontSubstitution); @@ -191,9 +189,6 @@ // Indirect reference to the HFontRef, which references the underlying HFONT. scoped_refptr<HFontRef> font_ref_; - // Pointer to the global IDWriteFactory interface. - static IDWriteFactory* direct_write_factory_; - DISALLOW_COPY_AND_ASSIGN(PlatformFontWin); };
diff --git a/ui/gfx/test/data/compositor/string_faded.png b/ui/gfx/test/data/compositor/string_faded.png deleted file mode 100644 index ec991520..0000000 --- a/ui/gfx/test/data/compositor/string_faded.png +++ /dev/null Binary files differ
diff --git a/ui/gfx/test/data/compositor/string_faded_win10.png b/ui/gfx/test/data/compositor/string_faded_win10.png new file mode 100644 index 0000000..3b91e7a3 --- /dev/null +++ b/ui/gfx/test/data/compositor/string_faded_win10.png Binary files differ
diff --git a/ui/gfx/test/data/compositor/string_faded_win7.png b/ui/gfx/test/data/compositor/string_faded_win7.png new file mode 100644 index 0000000..521a3675 --- /dev/null +++ b/ui/gfx/test/data/compositor/string_faded_win7.png Binary files differ
diff --git a/ui/gfx/win/direct_write.cc b/ui/gfx/win/direct_write.cc index 7bfa86d5b..d500b703 100644 --- a/ui/gfx/win/direct_write.cc +++ b/ui/gfx/win/direct_write.cc
@@ -17,6 +17,21 @@ namespace gfx { namespace win { +namespace { + +// Pointer to the global IDWriteFactory interface. +IDWriteFactory* g_direct_write_factory = nullptr; + +void SetDirectWriteFactory(IDWriteFactory* factory) { + DCHECK(!g_direct_write_factory); + // We grab a reference on the DirectWrite factory. This reference is + // leaked, which is ok because skia leaks it as well. + factory->AddRef(); + g_direct_write_factory = factory; +} + +} // anonymous namespace + void CreateDWriteFactory(IDWriteFactory** factory) { Microsoft::WRL::ComPtr<IUnknown> factory_unknown; HRESULT hr = @@ -40,6 +55,7 @@ Microsoft::WRL::ComPtr<IDWriteFactory> factory; CreateDWriteFactory(factory.GetAddressOf()); CHECK(!!factory); + SetDirectWriteFactory(factory.Get()); // The skia call to create a new DirectWrite font manager instance can fail // if we are unable to get the system font collection from the DirectWrite @@ -50,8 +66,19 @@ SkFontMgr_New_DirectWrite(factory.Get()); CHECK(!!direct_write_font_mgr); + // Override the default skia font manager. This must be called before any + // use of the skia font manager is done (e.g. before any call to + // SkFontMgr::RefDefault()). skia::OverrideDefaultSkFontMgr(std::move(direct_write_font_mgr)); - gfx::PlatformFontWin::SetDirectWriteFactory(factory.Get()); +} + +IDWriteFactory* GetDirectWriteFactory() { + // Some unittests may access this accessor without any previous call to + // |InitializeDirectWrite|. A call to |InitializeDirectWrite| after this + // function being called is still invalid. + if (!g_direct_write_factory) + InitializeDirectWrite(); + return g_direct_write_factory; } } // namespace win
diff --git a/ui/gfx/win/direct_write.h b/ui/gfx/win/direct_write.h index 63f490bf..b2e33e4 100644 --- a/ui/gfx/win/direct_write.h +++ b/ui/gfx/win/direct_write.h
@@ -17,6 +17,9 @@ // Creates a DirectWrite factory. GFX_EXPORT void CreateDWriteFactory(IDWriteFactory** factory); +// Returns the global DirectWrite factory. +GFX_EXPORT IDWriteFactory* GetDirectWriteFactory(); + } // namespace win } // namespace gfx
diff --git a/ui/login/display_manager.js b/ui/login/display_manager.js index a07839da..f2cf183 100644 --- a/ui/login/display_manager.js +++ b/ui/login/display_manager.js
@@ -35,8 +35,6 @@ /** @const */ var SCREEN_WRONG_HWID = 'wrong-hwid'; /** @const */ var SCREEN_DEVICE_DISABLED = 'device-disabled'; /** @const */ var SCREEN_UPDATE_REQUIRED = 'update-required'; -/** @const */ var SCREEN_UNRECOVERABLE_CRYPTOHOME_ERROR = - 'unrecoverable-cryptohome-error'; /** @const */ var SCREEN_ACTIVE_DIRECTORY_PASSWORD_CHANGE = 'ad-password-change'; /** @const */ var SCREEN_SYNC_CONSENT = 'sync-consent';
diff --git a/ui/message_center/views/notification_header_view.cc b/ui/message_center/views/notification_header_view.cc index 8ef9bbf..c7876d4e 100644 --- a/ui/message_center/views/notification_header_view.cc +++ b/ui/message_center/views/notification_header_view.cc
@@ -266,11 +266,13 @@ void NotificationHeaderView::SetAppIcon(const gfx::ImageSkia& img) { app_icon_view_->SetImage(img); + using_default_app_icon_ = false; } void NotificationHeaderView::ClearAppIcon() { app_icon_view_->SetImage( gfx::CreateVectorIcon(kProductIcon, kSmallImageSizeMD, accent_color_)); + using_default_app_icon_ = true; } void NotificationHeaderView::SetAppName(const base::string16& name) { @@ -360,6 +362,19 @@ summary_text_view_->SetEnabledColor(accent_color_); summary_text_divider_->SetEnabledColor(accent_color_); SetExpanded(is_expanded_); + + // If we are using the default app icon we should clear it so we refresh it + // with the new accent color. + if (using_default_app_icon_) + ClearAppIcon(); +} + +void NotificationHeaderView::SetBackgroundColor(SkColor color) { + app_name_view_->SetBackgroundColor(color); + summary_text_divider_->SetBackgroundColor(color); + summary_text_view_->SetBackgroundColor(color); + timestamp_divider_->SetBackgroundColor(color); + timestamp_view_->SetBackgroundColor(color); } bool NotificationHeaderView::IsExpandButtonEnabled() {
diff --git a/ui/message_center/views/notification_header_view.h b/ui/message_center/views/notification_header_view.h index adc254a..4793c326 100644 --- a/ui/message_center/views/notification_header_view.h +++ b/ui/message_center/views/notification_header_view.h
@@ -41,9 +41,15 @@ void SetSettingsButtonEnabled(bool enabled); void SetCloseButtonEnabled(bool enabled); void SetControlButtonsVisible(bool visible); + // Set the unified theme color used among the app icon, app name, and expand // button. void SetAccentColor(SkColor color); + + // Sets the background color of the notification. This is used to ensure that + // the accent color has enough contrast against the background. + void SetBackgroundColor(SkColor color); + void ClearAppIcon(); void ClearProgress(); void ClearTimestamp(); @@ -86,6 +92,7 @@ bool has_progress_ = false; bool has_timestamp_ = false; bool is_expanded_ = false; + bool using_default_app_icon_ = false; DISALLOW_COPY_AND_ASSIGN(NotificationHeaderView); };
diff --git a/ui/views/controls/editable_combobox/editable_combobox.cc b/ui/views/controls/editable_combobox/editable_combobox.cc index f911ed4b..6bd81c53 100644 --- a/ui/views/controls/editable_combobox/editable_combobox.cc +++ b/ui/views/controls/editable_combobox/editable_combobox.cc
@@ -31,6 +31,7 @@ #include "ui/gfx/geometry/rect.h" #include "ui/gfx/geometry/size.h" #include "ui/gfx/image/image.h" +#include "ui/gfx/range/range.h" #include "ui/gfx/render_text.h" #include "ui/gfx/scoped_canvas.h" #include "ui/native_theme/native_theme.h" @@ -299,6 +300,10 @@ return style::GetFont(text_context_, text_style_); } +void EditableCombobox::SelectRange(const gfx::Range& range) { + textfield_->SelectRange(range); +} + void EditableCombobox::SetAccessibleName(const base::string16& name) { textfield_->SetAccessibleName(name); } @@ -356,11 +361,13 @@ } void EditableCombobox::OnViewBlurred(View* observed_view) { - menu_runner_.reset(); + CloseMenu(); } void EditableCombobox::OnViewFocused(View* observed_view) { - ShowDropDownMenu(); + if (show_menu_on_next_focus_) + ShowDropDownMenu(); + show_menu_on_next_focus_ = true; } //////////////////////////////////////////////////////////////////////////////// @@ -368,7 +375,7 @@ void EditableCombobox::ButtonPressed(Button* sender, const ui::Event& event) { if (menu_runner_ && menu_runner_->IsRunning()) { - menu_runner_.reset(); + CloseMenu(); return; } textfield_->RequestFocus(); @@ -383,6 +390,10 @@ //////////////////////////////////////////////////////////////////////////////// // EditableCombobox, Private methods: +void EditableCombobox::CloseMenu() { + menu_runner_.reset(); +} + void EditableCombobox::OnItemSelected(int index) { // We set |showing_password_text_| to true before calling GetLabelAt on the // selected item so that even if it was false we still get the actual @@ -417,17 +428,13 @@ menu_model_->UpdateItemsShown(); } -void EditableCombobox::OnMenuClosed() { - menu_runner_.reset(); -} - void EditableCombobox::ShowDropDownMenu(ui::MenuSourceType source_type) { constexpr int kMenuBorderWidthLeft = 1; constexpr int kMenuBorderWidthTop = 1; constexpr int kMenuBorderWidthRight = 1; if (!menu_model_->GetItemCount()) { - menu_runner_.reset(); + CloseMenu(); return; } if (!textfield_->HasFocus() || (menu_runner_ && menu_runner_->IsRunning())) @@ -446,7 +453,7 @@ menu_runner_ = std::make_unique<MenuRunner>( menu_model_.get(), MenuRunner::EDITABLE_COMBOBOX, - base::BindRepeating(&EditableCombobox::OnMenuClosed, + base::BindRepeating(&EditableCombobox::CloseMenu, base::Unretained(this))); menu_runner_->RunMenuAt(GetWidget(), nullptr, bounds, MenuAnchorPosition::kTopLeft, source_type);
diff --git a/ui/views/controls/editable_combobox/editable_combobox.h b/ui/views/controls/editable_combobox/editable_combobox.h index 2f94b1a..824c6613 100644 --- a/ui/views/controls/editable_combobox/editable_combobox.h +++ b/ui/views/controls/editable_combobox/editable_combobox.h
@@ -20,6 +20,7 @@ namespace gfx { class FontList; +class Range; } // namespace gfx namespace ui { @@ -80,6 +81,13 @@ listener_ = listener; } + void set_show_menu_on_next_focus(bool show_menu_on_next_focus) { + show_menu_on_next_focus_ = show_menu_on_next_focus; + } + + // Selects the specified logical text range for the textfield. + void SelectRange(const gfx::Range& range); + // Sets the accessible name. Use SetAssociatedLabel instead if there is a // label associated with this combobox. void SetAccessibleName(const base::string16& name); @@ -102,15 +110,14 @@ private: class EditableComboboxMenuModel; + void CloseMenu(); + // Called when an item is selected from the menu. void OnItemSelected(int index); // Notifies listener of new content and updates the menu items to show. void HandleNewContent(const base::string16& new_content); - // Cleans up after the menu is closed. - void OnMenuClosed(); - // Shows the drop-down menu. void ShowDropDownMenu(ui::MenuSourceType source_type = ui::MENU_SOURCE_NONE); @@ -149,6 +156,10 @@ const Type type_; + // If false, then the menu won't be shown the next time the View is focused. + // Set false on creation to avoid showing the menu on the first focus event. + bool show_menu_on_next_focus_ = true; + // Set while the drop-down is showing. std::unique_ptr<MenuRunner> menu_runner_;
diff --git a/ui/views/controls/editable_combobox/editable_combobox_unittest.cc b/ui/views/controls/editable_combobox/editable_combobox_unittest.cc index 4f03bcb9..b4e18a3 100644 --- a/ui/views/controls/editable_combobox/editable_combobox_unittest.cc +++ b/ui/views/controls/editable_combobox/editable_combobox_unittest.cc
@@ -636,5 +636,25 @@ EXPECT_FALSE(IsMenuOpen()); } +TEST_F(EditableComboboxTest, ShowMenuOnNextFocusBehavior) { + std::vector<base::string16> items = {ASCIIToUTF16("item0"), + ASCIIToUTF16("item1")}; + InitEditableCombobox(items, /*filter_on_edit=*/false, + /*show_on_empty=*/true); + + dummy_focusable_view_->RequestFocus(); + combobox_->set_show_menu_on_next_focus(true); + combobox_->GetTextfieldForTest()->RequestFocus(); + EXPECT_TRUE(IsMenuOpen()); + + dummy_focusable_view_->RequestFocus(); + combobox_->set_show_menu_on_next_focus(false); + combobox_->GetTextfieldForTest()->RequestFocus(); + EXPECT_FALSE(IsMenuOpen()); + dummy_focusable_view_->RequestFocus(); + combobox_->GetTextfieldForTest()->RequestFocus(); + EXPECT_TRUE(IsMenuOpen()); +} + } // namespace } // namespace views
diff --git a/ui/views/controls/tabbed_pane/tabbed_pane.cc b/ui/views/controls/tabbed_pane/tabbed_pane.cc index 3b5ade9..93e9c7ba 100644 --- a/ui/views/controls/tabbed_pane/tabbed_pane.cc +++ b/ui/views/controls/tabbed_pane/tabbed_pane.cc
@@ -130,7 +130,7 @@ Tab::Tab(TabbedPane* tabbed_pane, const base::string16& title, View* contents) : tabbed_pane_(tabbed_pane), title_(new Label(title, style::CONTEXT_LABEL, style::STYLE_TAB_ACTIVE)), - tab_state_(TAB_ACTIVE), + state_(State::kActive), contents_(contents) { // Calculate the size while the font list is bold. preferred_title_size_ = title_->GetPreferredSize(); @@ -154,7 +154,7 @@ gfx::Insets(kTabVerticalPadding, kTabHorizontalPadding))); } SetLayoutManager(std::make_unique<FillLayout>()); - SetState(TAB_INACTIVE); + SetState(State::kInactive); // Calculate the size while the font list is normal and set the max size. preferred_title_size_.SetToMax(title_->GetPreferredSize()); AddChildView(title_); @@ -168,7 +168,7 @@ void Tab::SetSelected(bool selected) { contents_->SetVisible(selected); - SetState(selected ? TAB_ACTIVE : TAB_INACTIVE); + SetState(selected ? State::kActive : State::kInactive); #if defined(OS_MACOSX) SetFocusBehavior(selected ? FocusBehavior::ACCESSIBLE_ONLY : FocusBehavior::NEVER); @@ -183,8 +183,8 @@ tabbed_pane_->GetStyle() == TabbedPane::TabStripStyle::kHighlight; const int font_size_delta = is_highlight_mode ? kLabelFontSizeDeltaHighlight : ui::kLabelFontSizeDelta; - switch (tab_state_) { - case TAB_INACTIVE: + switch (state_) { + case State::kInactive: // Notify assistive tools to update this tab's selected status. // The way Chrome OS accessibility is implemented right now, firing almost // any event will work, we just need to trigger its state to be refreshed. @@ -197,13 +197,13 @@ is_highlight_mode ? kInactiveWeightHighlight : kInactiveWeightBorder)); break; - case TAB_ACTIVE: + case State::kActive: title_->SetEnabledColor(is_highlight_mode ? kTabTitleColor_ActiveHighlight : kTabTitleColor_ActiveBorder); title_->SetFontList(rb.GetFontListWithDelta( font_size_delta, gfx::Font::NORMAL, kActiveWeight)); break; - case TAB_HOVERED: + case State::kHovered: title_->SetEnabledColor(kTabTitleColor_Hovered); title_->SetFontList(rb.GetFontListWithDelta( font_size_delta, gfx::Font::NORMAL, @@ -219,11 +219,11 @@ } void Tab::OnMouseEntered(const ui::MouseEvent& event) { - SetState(selected() ? TAB_ACTIVE : TAB_HOVERED); + SetState(selected() ? State::kActive : State::kHovered); } void Tab::OnMouseExited(const ui::MouseEvent& event) { - SetState(selected() ? TAB_ACTIVE : TAB_INACTIVE); + SetState(selected() ? State::kActive : State::kInactive); } void Tab::OnGestureEvent(ui::GestureEvent* event) { @@ -235,7 +235,7 @@ tabbed_pane_->SelectTab(this); break; case ui::ET_GESTURE_TAP_CANCEL: - SetState(selected() ? TAB_ACTIVE : TAB_INACTIVE); + SetState(selected() ? State::kActive : State::kInactive); break; default: break; @@ -258,10 +258,10 @@ return kViewClassName; } -void Tab::SetState(TabState tab_state) { - if (tab_state == tab_state_) +void Tab::SetState(State state) { + if (state == state_) return; - tab_state_ = tab_state; + state_ = state; OnStateChanged(); SchedulePaint(); }
diff --git a/ui/views/controls/tabbed_pane/tabbed_pane.h b/ui/views/controls/tabbed_pane/tabbed_pane.h index f4a470a..b9101bb 100644 --- a/ui/views/controls/tabbed_pane/tabbed_pane.h +++ b/ui/views/controls/tabbed_pane/tabbed_pane.h
@@ -152,17 +152,17 @@ TabbedPane* tabbed_pane() { return tabbed_pane_; } - // Called whenever |tab_state_| changes. + // Called whenever |state_| changes. virtual void OnStateChanged(); private: - enum TabState { - TAB_INACTIVE, - TAB_ACTIVE, - TAB_HOVERED, + enum class State { + kInactive, + kActive, + kHovered, }; - void SetState(TabState tab_state); + void SetState(State state); // views::View: void OnPaint(gfx::Canvas* canvas) override; @@ -170,7 +170,7 @@ TabbedPane* tabbed_pane_; Label* title_; gfx::Size preferred_title_size_; - TabState tab_state_; + State state_; // The content view associated with this tab. View* contents_;
diff --git a/ui/views/mus/desktop_window_tree_host_mus_unittest.cc b/ui/views/mus/desktop_window_tree_host_mus_unittest.cc index b9bc1729..cac8ed0 100644 --- a/ui/views/mus/desktop_window_tree_host_mus_unittest.cc +++ b/ui/views/mus/desktop_window_tree_host_mus_unittest.cc
@@ -1175,7 +1175,8 @@ // Changes to the bounds from the server should not consider the min/max. const gfx::Rect server_bounds(1, 2, 250, 251); static_cast<aura::WindowTreeHostMus*>(widget->GetNativeWindow()->GetHost()) - ->SetBoundsFromServer(server_bounds, viz::LocalSurfaceIdAllocation()); + ->SetBoundsFromServer(server_bounds, ui::SHOW_STATE_DEFAULT, + viz::LocalSurfaceIdAllocation()); EXPECT_EQ(server_bounds, widget->GetWindowBoundsInScreen()); }
diff --git a/ui/views/mus/remote_view/remote_view_provider_unittest.cc b/ui/views/mus/remote_view/remote_view_provider_unittest.cc index 8499e90..3c08574 100644 --- a/ui/views/mus/remote_view/remote_view_provider_unittest.cc +++ b/ui/views/mus/remote_view/remote_view_provider_unittest.cc
@@ -208,6 +208,7 @@ parent_local_surface_id_allocator.GenerateId(); window_tree_client()->OnWindowBoundsChanged( aura::WindowMus::Get(root_window)->server_id(), root_bounds, + ui::SHOW_STATE_DEFAULT, parent_local_surface_id_allocator.GetCurrentLocalSurfaceIdAllocation()); EXPECT_EQ(root_bounds, root_window->GetHost()->GetBoundsInPixels()); EXPECT_EQ(root_bounds.origin(), root_window->GetBoundsInScreen().origin());
diff --git a/ui/webui/resources/cr_elements/chromeos/fingerprint/cr_fingerprint_progress_arc.js b/ui/webui/resources/cr_elements/chromeos/fingerprint/cr_fingerprint_progress_arc.js index bf7e1c8..c204783 100644 --- a/ui/webui/resources/cr_elements/chromeos/fingerprint/cr_fingerprint_progress_arc.js +++ b/ui/webui/resources/cr_elements/chromeos/fingerprint/cr_fingerprint_progress_arc.js
@@ -111,6 +111,13 @@ canvasCircleProgressColor_: CANVAS_CIRCLE_PROGRESS_COLOR, /** + * Animation ID for fingerprint scan progress bar. + * @type {number|undefined} + * @private + */ + progressAnimationIntervalId_: undefined, + + /** * Timer ID for fingerprint scan success update. * @type {number|undefined} * @private @@ -166,6 +173,7 @@ return; } this.isComplete_ = isComplete; + this.cancelAnimations_(); const slice = 2 * Math.PI / 100; const startAngle = prevPercentComplete * slice; @@ -176,41 +184,39 @@ // The value to update the angle by each tick. const step = (endAngle - startAngle) / (ANIMATE_DURATION_MS / ANIMATE_TICKS_MS); - const id = setInterval(doAnimate.bind(this), ANIMATE_TICKS_MS); - // Circles on html canvas have 0 radians on the positive x-axis and go in - // clockwise direction. We want to start at the top of the circle which is - // 3pi/2. - const start = 3 * Math.PI / 2; - // Function that is called every tick of the interval, draws the arc a bit // closer to the final destination each tick, until it reaches the final // destination. - function doAnimate() { + const doAnimate = () => { if (currentAngle >= endAngle) { - clearInterval(id); + if (this.progressAnimationIntervalId_) { + clearInterval(this.progressAnimationIntervalId_); + this.progressAnimationIntervalId_ = undefined; + } currentAngle = endAngle; } // Clears the canvas and draws the new progress circle. this.clearCanvas_(); - // Drawing two arcs to form a circle gives a nicer look than drawing an - // arc on top of a circle. If |currentAngle| is 0, draw from |start| + - // |currentAngle| to 7 * Math.PI / 2 (start is 3 * Math.PI / 2) otherwise - // the regular draw from |start| to |currentAngle| will draw nothing which - // will cause a flicker for one frame. + // Drawing two arcs to form a circle gives a nicer look than drawing + // an arc on top of a circle. If |currentAngle| is 0, draw from + // |start| + |currentAngle| to 7 * Math.PI / 2 (start is 3 * Math.PI / + // 2) otherwise the regular draw from |start| to |currentAngle| will + // draw nothing which will cause a flicker for one frame. this.drawArc( start, start + currentAngle, this.canvasCircleProgressColor_); this.drawArc( start + currentAngle, currentAngle <= 0 ? 7 * Math.PI / 2 : start, this.canvasCircleBackgroundColor_); currentAngle += step; - } + }; - // Clean up any pending animation update. - if (this.updateTimerId_ !== undefined) { - window.clearTimeout(this.updateTimerId_); - this.updateTimerId_ = undefined; - } + this.progressAnimationIntervalId_ = + setInterval(doAnimate, ANIMATE_TICKS_MS); + // Circles on html canvas have 0 radians on the positive x-axis and go in + // clockwise direction. We want to start at the top of the circle which is + // 3pi/2. + const start = 3 * Math.PI / 2; if (isComplete) { this.animateScanComplete_(); @@ -219,6 +225,21 @@ } }, + /* + * Cleans up any pending animation update created by setInterval(). + * @private + */ + cancelAnimations_: function() { + if (this.progressAnimationIntervalId_) { + clearInterval(this.progressAnimationIntervalId_); + this.progressAnimationIntervalId_ = undefined; + } + if (this.updateTimerId_) { + window.clearTimeout(this.updateTimerId_); + this.updateTimerId_ = undefined; + } + }, + /** * Show animation for enrollment completion. * @private @@ -259,6 +280,7 @@ * Reset the element to initial state, when the enrollment just starts. */ reset: function() { + this.cancelAnimations_(); this.clearCanvas_(); this.isComplete_ = false; this.drawBackgroundCircle();
diff --git a/ui/webui/resources/cr_elements/cr_container_shadow_behavior.html b/ui/webui/resources/cr_elements/cr_container_shadow_behavior.html index d62c681..da6baaf5 100644 --- a/ui/webui/resources/cr_elements/cr_container_shadow_behavior.html +++ b/ui/webui/resources/cr_elements/cr_container_shadow_behavior.html
@@ -1 +1,3 @@ +<link rel="import" href="chrome://resources/html/polymer.html"> + <script src="cr_container_shadow_behavior.js"></script>
diff --git a/ui/webui/resources/cr_elements/policy/cr_policy_indicator_behavior.html b/ui/webui/resources/cr_elements/policy/cr_policy_indicator_behavior.html index 518e6c1..934e036e 100644 --- a/ui/webui/resources/cr_elements/policy/cr_policy_indicator_behavior.html +++ b/ui/webui/resources/cr_elements/policy/cr_policy_indicator_behavior.html
@@ -1 +1,3 @@ +<link rel="import" href="chrome://resources/html/polymer.html"> + <script src="cr_policy_indicator_behavior.js"></script>
diff --git a/ui/webui/resources/html/cr/ui/focus_row_behavior.html b/ui/webui/resources/html/cr/ui/focus_row_behavior.html index 3b50f73f..021fcfb 100644 --- a/ui/webui/resources/html/cr/ui/focus_row_behavior.html +++ b/ui/webui/resources/html/cr/ui/focus_row_behavior.html
@@ -1,2 +1,5 @@ +<link rel="import" href="chrome://resources/html/polymer.html"> + <link rel="import" href="focus_row.html"> + <script src="../../../js/cr/ui/focus_row_behavior.js"></script>
diff --git a/ui/webui/resources/html/list_property_update_behavior.html b/ui/webui/resources/html/list_property_update_behavior.html index 3bdf5de..8fa5f3c 100644 --- a/ui/webui/resources/html/list_property_update_behavior.html +++ b/ui/webui/resources/html/list_property_update_behavior.html
@@ -1 +1,3 @@ +<link rel="import" href="chrome://resources/html/polymer.html"> + <script src="../js/list_property_update_behavior.js"></script>