diff --git a/DEPS b/DEPS index 4a602724..86d0c14 100644 --- a/DEPS +++ b/DEPS
@@ -40,7 +40,7 @@ # 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': '9b0b32fda4871776eb9afdf9553e523e5c28aa63', + 'skia_revision': '445b5573613179c10d5d9c28f82aa8ed94390aea', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling V8 # and whatever else without interference from each other. @@ -96,7 +96,7 @@ # Three lines of non-changing comments so that # the commit queue can handle CLs rolling catapult # and whatever else without interference from each other. - 'catapult_revision': 'ce94cbb62fa88fc712dde310185343f60f851b9f', + 'catapult_revision': '123b9d8ec2a233053e908750d1ce04c7c16fc3d8', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling libFuzzer # and whatever else without interference from each other.
diff --git a/android_webview/common/crash_reporter/crash_keys.cc b/android_webview/common/crash_reporter/crash_keys.cc index ed8297a9..06eb307 100644 --- a/android_webview/common/crash_reporter/crash_keys.cc +++ b/android_webview/common/crash_reporter/crash_keys.cc
@@ -112,10 +112,6 @@ // Temporary for https://crbug.com/685996. {"user-cloud-policy-manager-connect-trace", kMediumSize}, - - // TODO(bcwhite): Remove after fixing https://crbug.com/736675 - {"bad_histogram", kMediumSize}, - {"from_location", kMediumSize}, }; // This dynamic set of keys is used for sets of key value pairs when gathering
diff --git a/ash/accelerators/accelerator_controller.cc b/ash/accelerators/accelerator_controller.cc index 0c9812cc..6d82885 100644 --- a/ash/accelerators/accelerator_controller.cc +++ b/ash/accelerators/accelerator_controller.cc
@@ -83,6 +83,11 @@ const char kHighContrastToggleAccelNotificationId[] = "chrome://settings/accessibility/highcontrast"; +// Toast id and duration for voice interaction shortcuts +const char kSecondaryUserToastId[] = "voice_interaction_secondary_user"; +const char kUnsupportedLocaleToastId[] = "voice_interaction_locale_unsupported"; +const int kToastDurationMs = 2500; + // The notification delegate that will be used to open the keyboard shortcut // help page when the notification is clicked. class DeprecatedAcceleratorNotificationDelegate @@ -603,16 +608,29 @@ base::UserMetricsAction("VoiceInteraction.Started.Assistant")); } - // Show a toast if voice interaction is disabled due to unsupported locales. - if (!chromeos::switches::IsVoiceInteractionLocalesSupported()) { + // Show a toast if the active user is not primary. + if (Shell::Get()->session_controller()->GetPrimaryUserSession() != + Shell::Get()->session_controller()->GetUserSession(0)) { ash::ToastData toast( - "voice_interaction_locales_unsupported", + kSecondaryUserToastId, l10n_util::GetStringUTF16( - IDS_ASH_VOICE_INTERACTION_LOCALE_UNSUPPORTED_TOAST_MESSAGE), - 2500, base::Optional<base::string16>()); + IDS_ASH_VOICE_INTERACTION_SECONDARY_USER_TOAST_MESSAGE), + kToastDurationMs, base::Optional<base::string16>()); ash::Shell::Get()->toast_manager()->Show(toast); return; } + + // Show a toast if voice interaction is disabled due to unsupported locales. + if (!chromeos::switches::IsVoiceInteractionLocalesSupported()) { + ash::ToastData toast( + kUnsupportedLocaleToastId, + l10n_util::GetStringUTF16( + IDS_ASH_VOICE_INTERACTION_LOCALE_UNSUPPORTED_TOAST_MESSAGE), + kToastDurationMs, base::Optional<base::string16>()); + ash::Shell::Get()->toast_manager()->Show(toast); + return; + } + Shell::Get()->app_list()->ToggleVoiceInteractionSession(); }
diff --git a/ash/ash_strings.grd b/ash/ash_strings.grd index 2bb4fe9..941281a2 100644 --- a/ash/ash_strings.grd +++ b/ash/ash_strings.grd
@@ -450,6 +450,9 @@ <message name="IDS_ASH_VOICE_INTERACTION_LOCALE_UNSUPPORTED_TOAST_MESSAGE" desc="Message content on the toast that appears when the voice interaction shortcut is pressed but the locale is unsupported."> The Google Assistant doesn’t speak this language. </message> + <message name="IDS_ASH_VOICE_INTERACTION_SECONDARY_USER_TOAST_MESSAGE" desc="Message content on the toast that appears when the voice interaction shortcut is pressed but the locale is unsupported."> + Assistant is only available for primary profile. + </message> <message name="IDS_ASH_TOAST_DISMISS_BUTTON" desc="The text button shown in toasts to close the toast immediately without waiting timeout."> DISMISS
diff --git a/ash/shell.cc b/ash/shell.cc index e02b64d..224057b 100644 --- a/ash/shell.cc +++ b/ash/shell.cc
@@ -695,8 +695,6 @@ aura::client::GetFocusClient(GetPrimaryRootWindow())->FocusWindow(nullptr); // Please keep in reverse order as in Init() because it's easy to miss one. - split_view_controller_.reset(); - if (window_modality_controller_) window_modality_controller_.reset(); @@ -765,6 +763,10 @@ window_cycle_controller_.reset(); window_selector_controller_.reset(); + // |split_view_controller_| needs to be deleted after + // |window_selector_controller_|. + split_view_controller_.reset(); + CloseAllRootWindowChildWindows(); // MruWindowTracker must be destroyed after all windows have been deleted to
diff --git a/ash/shell/window_type_launcher.cc b/ash/shell/window_type_launcher.cc index bc8e9f4..0b46e90 100644 --- a/ash/shell/window_type_launcher.cc +++ b/ash/shell/window_type_launcher.cc
@@ -210,8 +210,7 @@ this, base::ASCIIToUTF16("Show a web/app notification"))), show_views_examples_callback_(show_views_examples_callback) { - views::GridLayout* layout = new views::GridLayout(this); - SetLayoutManager(layout); + views::GridLayout* layout = views::GridLayout::CreateAndInstall(this); SetBorder(views::CreateEmptyBorder(gfx::Insets(5))); views::ColumnSet* column_set = layout->AddColumnSet(0); column_set->AddColumn(views::GridLayout::LEADING, views::GridLayout::CENTER,
diff --git a/ash/system/date/date_view.cc b/ash/system/date/date_view.cc index 213b25e..434e34a 100644 --- a/ash/system/date/date_view.cc +++ b/ash/system/date/date_view.cc
@@ -262,8 +262,7 @@ AddChildView(horizontal_label_.get()); } else { RemoveChildView(horizontal_label_.get()); - views::GridLayout* layout = new views::GridLayout(this); - SetLayoutManager(layout); + views::GridLayout* layout = views::GridLayout::CreateAndInstall(this); const int kColumnId = 0; views::ColumnSet* columns = layout->AddColumnSet(kColumnId); columns->AddPaddingColumn(0, kVerticalClockLeftPadding);
diff --git a/ash/system/palette/palette_tray.cc b/ash/system/palette/palette_tray.cc index 552eca22..d20e84a3 100644 --- a/ash/system/palette/palette_tray.cc +++ b/ash/system/palette/palette_tray.cc
@@ -216,7 +216,7 @@ } bool PaletteTray::ContainsPointInScreen(const gfx::Point& point) { - if (icon_ && icon_->GetBoundsInScreen().Contains(point)) + if (GetBoundsInScreen().Contains(point)) return true; return bubble_ && bubble_->bubble_view()->GetBoundsInScreen().Contains(point);
diff --git a/ash/system/status_area_widget_delegate.cc b/ash/system/status_area_widget_delegate.cc index 3c44e5f..ab5e6056f 100644 --- a/ash/system/status_area_widget_delegate.cc +++ b/ash/system/status_area_widget_delegate.cc
@@ -139,8 +139,7 @@ void StatusAreaWidgetDelegate::UpdateLayout() { // Use a grid layout so that the trays can be centered in each cell, and // so that the widget gets laid out correctly when tray sizes change. - views::GridLayout* layout = new views::GridLayout(this); - SetLayoutManager(layout); + views::GridLayout* layout = views::GridLayout::CreateAndInstall(this); // Update tray border based on layout. bool is_child_on_edge = true;
diff --git a/ash/wm/overview/overview_window_drag_controller.cc b/ash/wm/overview/overview_window_drag_controller.cc index da677bb..a70ccdc 100644 --- a/ash/wm/overview/overview_window_drag_controller.cc +++ b/ash/wm/overview/overview_window_drag_controller.cc
@@ -90,6 +90,10 @@ } } +void OverviewWindowDragController::ResetWindowSelector() { + window_selector_ = nullptr; +} + void OverviewWindowDragController::UpdatePhantomWindowAndWindowGrid( const gfx::Point& location_in_screen) { SplitViewController::SnapPosition last_snap_position = snap_position_; @@ -174,12 +178,8 @@ // |item_| will be deleted after RemoveWindowSelectorItem(). aura::Window* window = item_->GetWindow(); window_selector_->RemoveWindowSelectorItem(item_); - item_ = nullptr; - - // Note: SplitViewController::SnapWindow() might end of overview mode if two - // windows are snapped to both side of the screen. In this case the deletion - // of WindowSelector will then delete OverviewWindowDragController. split_view_controller_->SnapWindow(window, snap_position); + item_ = nullptr; } } // namespace ash
diff --git a/ash/wm/overview/overview_window_drag_controller.h b/ash/wm/overview/overview_window_drag_controller.h index 89f8d94..61a9e06 100644 --- a/ash/wm/overview/overview_window_drag_controller.h +++ b/ash/wm/overview/overview_window_drag_controller.h
@@ -31,6 +31,12 @@ void Drag(const gfx::Point& location_in_screen); void CompleteDrag(); + // Resets |window_selector_| to nullptr. It's needed since we defer the + // deletion of OverviewWindowDragController in WindowSelector destructor and + // we need to reset |window_selector_| to nullptr to avoid null pointer + // dereference. + void ResetWindowSelector(); + WindowSelectorItem* item() { return item_; } private:
diff --git a/ash/wm/overview/window_selector.cc b/ash/wm/overview/window_selector.cc index 1a27d7d..3b98dc3 100644 --- a/ash/wm/overview/window_selector.cc +++ b/ash/wm/overview/window_selector.cc
@@ -30,6 +30,7 @@ #include "base/command_line.h" #include "base/metrics/histogram_macros.h" #include "base/metrics/user_metrics.h" +#include "base/threading/thread_task_runner_handle.h" #include "components/vector_icons/vector_icons.h" #include "third_party/skia/include/core/SkPath.h" #include "ui/base/resource/resource_bundle.h" @@ -252,7 +253,14 @@ } WindowSelector::~WindowSelector() { - RemoveAllObservers(); + DCHECK(observed_windows_.empty()); + // Don't delete |window_drag_controller_| yet since the stack might be still + // using it. + if (window_drag_controller_) { + window_drag_controller_->ResetWindowSelector(); + base::ThreadTaskRunnerHandle::Get()->DeleteSoon( + FROM_HERE, window_drag_controller_.release()); + } } // NOTE: The work done in Init() is not done in the constructor because it may @@ -400,6 +408,7 @@ void WindowSelector::RemoveAllObservers() { for (auto* window : observed_windows_) window->RemoveObserver(this); + observed_windows_.clear(); Shell::Get()->activation_client()->RemoveObserver(this); display::Screen::GetScreen()->RemoveObserver(this); @@ -498,6 +507,8 @@ for (std::unique_ptr<WindowGrid>& grid : grid_list_) { if (grid->Contains(item->GetWindow())) { grid->RemoveItem(item); + if (grid->empty()) + OnGridEmpty(grid.get()); break; } }
diff --git a/ash/wm/overview/window_selector_controller.cc b/ash/wm/overview/window_selector_controller.cc index a69f44f2..8ff60b1 100644 --- a/ash/wm/overview/window_selector_controller.cc +++ b/ash/wm/overview/window_selector_controller.cc
@@ -30,6 +30,11 @@ delayed_animations_) { animation_observer->Shutdown(); } + + if (window_selector_.get()) { + window_selector_->Shutdown(); + window_selector_.reset(); + } } // static @@ -139,7 +144,9 @@ // windows, so we can remove WindowSelectorDelegate. void WindowSelectorController::OnSelectionEnded() { window_selector_->Shutdown(); - window_selector_.reset(); + // Don't delete |window_selector_| yet since the stack is still using it. + base::ThreadTaskRunnerHandle::Get()->DeleteSoon(FROM_HERE, + window_selector_.release()); last_selection_time_ = base::Time::Now(); Shell::Get()->NotifyOverviewModeEnded(); }
diff --git a/ash/wm/overview/window_selector_unittest.cc b/ash/wm/overview/window_selector_unittest.cc index e25f7b5f..5d049e1 100644 --- a/ash/wm/overview/window_selector_unittest.cc +++ b/ash/wm/overview/window_selector_unittest.cc
@@ -921,6 +921,8 @@ // Exit overview. ToggleOverview(); + base::RunLoop().RunUntilIdle(); + EXPECT_TRUE(window1->IsVisible()); EXPECT_TRUE(window2->IsVisible()); } @@ -1238,15 +1240,15 @@ gfx::Rect bounds(0, 0, 400, 400); // These windows will be deleted when the test exits and the Shell instance // is shut down. - aura::Window* window1(CreateWindow(bounds)); - aura::Window* window2(CreateWindow(bounds)); - aura::Window* window3(CreatePanelWindow(bounds)); - aura::Window* window4(CreatePanelWindow(bounds)); + std::unique_ptr<aura::Window> window1(CreateWindow(bounds)); + std::unique_ptr<aura::Window> window2(CreateWindow(bounds)); + std::unique_ptr<aura::Window> window3(CreatePanelWindow(bounds)); + std::unique_ptr<aura::Window> window4(CreatePanelWindow(bounds)); - wm::ActivateWindow(window4); - wm::ActivateWindow(window3); - wm::ActivateWindow(window2); - wm::ActivateWindow(window1); + wm::ActivateWindow(window4.get()); + wm::ActivateWindow(window3.get()); + wm::ActivateWindow(window2.get()); + wm::ActivateWindow(window1.get()); ToggleOverview(); } @@ -2067,4 +2069,35 @@ ToggleOverview(); } +// Tests that if there is only one window in the MRU window list in the overview +// mode, snapping the window to one side of the screen will end the overview +// mode since there is no more window left in the overview window grid. +TEST_F(WindowSelectorTest, EmptyWindowsListExitOverview) { + base::CommandLine::ForCurrentProcess()->AppendSwitch( + switches::kAshEnableTabletSplitView); + Shell::Get()->tablet_mode_controller()->EnableTabletModeWindowManager(true); + const gfx::Rect bounds(0, 0, 400, 400); + std::unique_ptr<aura::Window> window1(CreateWindow(bounds)); + + ToggleOverview(); + EXPECT_TRUE(window_selector_controller()->IsSelecting()); + + // Drag |window1| selector item to snap to left. + const int grid_index = 0; + WindowSelectorItem* selector_item1 = + GetWindowItemForWindow(grid_index, window1.get()); + const gfx::Rect selector_item_bounds1 = selector_item1->target_bounds(); + // Start drag in the middle of the seletor item. + const gfx::Point start_location1(selector_item_bounds1.CenterPoint()); + window_selector()->InitiateDrag(selector_item1, start_location1); + const gfx::Point end_location1(0, 0); + window_selector()->Drag(selector_item1, end_location1); + window_selector()->CompleteDrag(selector_item1); + + EXPECT_EQ(split_view_controller()->IsSplitViewModeActive(), true); + EXPECT_EQ(split_view_controller()->state(), + SplitViewController::LEFT_SNAPPED); + EXPECT_FALSE(window_selector_controller()->IsSelecting()); +} + } // namespace ash
diff --git a/base/BUILD.gn b/base/BUILD.gn index 82f7a7fd..19c9512a 100644 --- a/base/BUILD.gn +++ b/base/BUILD.gn
@@ -239,6 +239,7 @@ "containers/linked_list.h", "containers/mru_cache.h", "containers/small_map.h", + "containers/span.h", "containers/stack_container.h", "containers/vector_buffer.h", "cpu.cc",
diff --git a/base/debug/task_annotator.cc b/base/debug/task_annotator.cc index 511155b..10c5636 100644 --- a/base/debug/task_annotator.cc +++ b/base/debug/task_annotator.cc
@@ -8,11 +8,9 @@ #include "base/debug/activity_tracker.h" #include "base/debug/alias.h" -#include "base/metrics/statistics_recorder.h" // crbug/744734 #include "base/pending_task.h" #include "base/trace_event/trace_event.h" #include "base/tracked_objects.h" -#include "build/build_config.h" // crbug/744734 namespace base { namespace debug { @@ -35,13 +33,6 @@ PendingTask* pending_task) { ScopedTaskRunActivity task_activity(*pending_task); -#if defined(OS_ANDROID) - // Try to find the source of heap object corruption by validating all - // histogram objects before and after each posted task. - // TODO(bcwhite): Remove this after successful canary build. - base::StatisticsRecorder::ValidateAllHistograms(nullptr); -#endif - tracked_objects::TaskStopwatch stopwatch; stopwatch.Start(); base::TimeDelta queue_duration = @@ -70,13 +61,6 @@ stopwatch.Stop(); tracked_objects::ThreadData::TallyRunOnNamedThreadIfTracking(*pending_task, stopwatch); - -#if defined(OS_ANDROID) - // Try to find the source of heap object corruption by validating all - // histogram objects before and after each posted task. - // TODO(bcwhite): Remove this after successful canary build. - base::StatisticsRecorder::ValidateAllHistograms(&pending_task->posted_from); -#endif } uint64_t TaskAnnotator::GetTaskTraceID(const PendingTask& task) const {
diff --git a/base/files/file_path.h b/base/files/file_path.h index 31d419c3..a4ced94 100644 --- a/base/files/file_path.h +++ b/base/files/file_path.h
@@ -451,13 +451,8 @@ StringType path_; }; -// This is required by googletest to print a readable output on test failures. -// This is declared here for use in gtest-based unit tests but is defined in -// the test_support_base target. Depend on that to use this in your unit test. -// This should not be used in production code - call ToString() instead. -void PrintTo(const FilePath& path, std::ostream* out); - -std::ostream& operator<<(std::ostream& out, const FilePath& file_path); +BASE_EXPORT std::ostream& operator<<(std::ostream& out, + const FilePath& file_path); } // namespace base
diff --git a/base/files/file_path_unittest.cc b/base/files/file_path_unittest.cc index dbc47d15f..eba9a96 100644 --- a/base/files/file_path_unittest.cc +++ b/base/files/file_path_unittest.cc
@@ -1287,12 +1287,11 @@ } #endif -// Test the PrintTo overload for FilePath (used when a test fails to compare two -// FilePaths). -TEST_F(FilePathTest, PrintTo) { +// Test the operator<<(ostream, FilePath). +TEST_F(FilePathTest, PrintToOstream) { std::stringstream ss; FilePath fp(FPL("foo")); - base::PrintTo(fp, &ss); + ss << fp; EXPECT_EQ("foo", ss.str()); }
diff --git a/base/metrics/histogram.cc b/base/metrics/histogram.cc index 84cd3a4..f3683f0 100644 --- a/base/metrics/histogram.cc +++ b/base/metrics/histogram.cc
@@ -559,10 +559,20 @@ }; uint32_t bad_fields = 0; - if (histogram_name().length() > 20 && histogram_name().at(20) == '\0') + if (!unlogged_samples_) + bad_fields |= 1 << kUnloggedSamplesField; + else if (!unlogged_samples_->bucket_ranges()) + bad_fields |= 1 << kUnloggedBucketRangesField; + if (!logged_samples_) + bad_fields |= 1 << kLoggedSamplesField; + else if (!logged_samples_->bucket_ranges()) + bad_fields |= 1 << kLoggedBucketRangesField; + else if (logged_samples_->id() == 0) + bad_fields |= 1 << kIdField; + else if (HashMetricName(histogram_name()) != logged_samples_->id()) bad_fields |= 1 << kHistogramNameField; - else if (histogram_name().length() > 40 && histogram_name().at(40) == '\0') - bad_fields |= 1 << kHistogramNameField; + if (flags() == 0) + bad_fields |= 1 << kFlagsField; if (dummy_ != kDummyValue) bad_fields |= 1 << kDummyField;
diff --git a/base/metrics/statistics_recorder.cc b/base/metrics/statistics_recorder.cc index c125ff9..fc044f7 100644 --- a/base/metrics/statistics_recorder.cc +++ b/base/metrics/statistics_recorder.cc
@@ -7,10 +7,8 @@ #include <memory> #include "base/at_exit.h" -#include "base/debug/crash_logging.h" // crbug/744734 #include "base/debug/leak_annotations.h" #include "base/json/string_escape.h" -#include "base/location.h" // crbug/744734 #include "base/logging.h" #include "base/memory/ptr_util.h" #include "base/metrics/histogram.h" @@ -20,7 +18,6 @@ #include "base/stl_util.h" #include "base/strings/stringprintf.h" #include "base/values.h" -#include "build/build_config.h" // crbug/744734 namespace { @@ -302,26 +299,22 @@ } // static -void StatisticsRecorder::ValidateAllHistograms( - tracked_objects::Location* location) { +void StatisticsRecorder::ValidateAllHistograms() { ImportGlobalPersistentHistograms(); auto known = GetKnownHistograms(/*include_persistent=*/true); + HistogramBase* last_invalid_histogram = nullptr; + int invalid_count = 0; for (HistogramBase* h : known) { - const bool is_valid = h->ValidateHistogramContents(!location, 0); + const bool is_valid = h->ValidateHistogramContents(false, 0); if (!is_valid) { -#if !defined(OS_NACL) - // CrashKey is scoped so can't be inside "if (location)" block so the - // "!location" parameter to ValidatehistogramContents causes it to - // crash there if location is passed as null. - const std::string debug_string = base::StringPrintf( - "%s:%d", location->file_name(), location->line_number()); - base::debug::ScopedCrashKey crash_key("from_location", debug_string); - h->ValidateHistogramContents(true, 0); -#endif + ++invalid_count; + last_invalid_histogram = h; } } + if (last_invalid_histogram) + last_invalid_histogram->ValidateHistogramContents(true, invalid_count); } // static @@ -455,8 +448,6 @@ bool include_persistent) { std::vector<HistogramBase*> known; base::AutoLock auto_lock(lock_.Get()); - if (!histograms_) - return known; known.reserve(histograms_->size()); for (const auto& h : *histograms_) {
diff --git a/base/metrics/statistics_recorder.h b/base/metrics/statistics_recorder.h index e7446bd..0a24b504 100644 --- a/base/metrics/statistics_recorder.h +++ b/base/metrics/statistics_recorder.h
@@ -28,10 +28,6 @@ #include "base/strings/string_piece.h" #include "base/synchronization/lock.h" -namespace tracked_objects { -class Location; -} - namespace base { class BucketRanges; @@ -141,8 +137,7 @@ HistogramSnapshotManager* snapshot_manager); // TODO(asvitkine): Remove this after crbug/736675. - static void ValidateAllHistograms( - tracked_objects::Location* location = nullptr); + static void ValidateAllHistograms(); // GetSnapshot copies some of the pointers to registered histograms into the // caller supplied vector (Histograms). Only histograms which have |query| as
diff --git a/base/process/launch_fuchsia.cc b/base/process/launch_fuchsia.cc index c3617157..6c3b9d8 100644 --- a/base/process/launch_fuchsia.cc +++ b/base/process/launch_fuchsia.cc
@@ -10,12 +10,14 @@ #include <unistd.h> #include "base/command_line.h" +#include "base/fuchsia/default_job.h" #include "base/logging.h" namespace base { namespace { +// TODO(758683): Replace this with a call to LaunchProcess(). bool GetAppOutputInternal(const std::vector<std::string>& argv, bool include_stderr, std::string* output, @@ -28,8 +30,8 @@ argv_cstr.push_back(arg.c_str()); argv_cstr.push_back(nullptr); - launchpad_t* lp; - launchpad_create(MX_HANDLE_INVALID, argv_cstr[0], &lp); + launchpad_t* lp = nullptr; + launchpad_create(GetDefaultJob(), argv_cstr[0], &lp); launchpad_load_from_file(lp, argv_cstr[0]); launchpad_set_args(lp, argv.size(), argv_cstr.data()); launchpad_clone(lp, LP_CLONE_MXIO_NAMESPACE | LP_CLONE_MXIO_CWD | @@ -38,7 +40,7 @@ int pipe_fd; mx_status_t status = launchpad_add_pipe(lp, &pipe_fd, STDOUT_FILENO); if (status != MX_OK) { - LOG(ERROR) << "launchpad_add_pipe failed: " << status; + LOG(ERROR) << "launchpad_add_pipe failed: " << mx_status_get_string(status); launchpad_destroy(lp); return false; } @@ -52,7 +54,8 @@ const char* errmsg; status = launchpad_go(lp, &proc, &errmsg); if (status != MX_OK) { - LOG(ERROR) << "launchpad_go failed: " << errmsg << ", status=" << status; + LOG(ERROR) << "launchpad_go failed: " << errmsg + << ", status=" << mx_status_get_string(status); return false; } @@ -89,8 +92,12 @@ // used in a "builder" style. From launchpad_create() to launchpad_go() the // status is tracked in the launchpad_t object, and launchpad_go() reports on // the final status, and cleans up |lp| (assuming it was even created). - launchpad_t* lp; - launchpad_create(options.job_handle, argv_cstr[0], &lp); + launchpad_t* lp = nullptr; + mx_handle_t job = options.job_handle != MX_HANDLE_INVALID ? options.job_handle + : GetDefaultJob(); + DCHECK_NE(MX_HANDLE_INVALID, job); + + launchpad_create(job, argv_cstr[0], &lp); launchpad_load_from_file(lp, argv_cstr[0]); launchpad_set_args(lp, argv.size(), argv_cstr.data()); @@ -109,6 +116,22 @@ to_clone |= LP_CLONE_MXIO_CWD; } + if (to_clone & LP_CLONE_DEFAULT_JOB) { + // Override Fuchsia's built in default job cloning behavior with our own + // logic which uses |job| instead of mx_job_default(). + // This logic is based on the launchpad implementation. + mx_handle_t job_duplicate = MX_HANDLE_INVALID; + mx_status_t status = + mx_handle_duplicate(job, MX_RIGHT_SAME_RIGHTS, &job_duplicate); + if (status != MX_OK) { + LOG(ERROR) << "mx_handle_duplicate(job): " + << mx_status_get_string(status); + return Process(); + } + launchpad_add_handle(lp, job_duplicate, PA_HND(PA_JOB_DEFAULT, 0)); + to_clone &= ~LP_CLONE_DEFAULT_JOB; + } + if (!environ_modifications.empty()) new_environ = AlterEnvironment(old_environ, environ_modifications); @@ -141,7 +164,8 @@ const char* errmsg; mx_status_t status = launchpad_go(lp, &proc, &errmsg); if (status != MX_OK) { - LOG(ERROR) << "launchpad_go failed: " << errmsg << ", status=" << status; + LOG(ERROR) << "launchpad_go failed: " << errmsg + << ", status=" << mx_status_get_string(status); return Process(); }
diff --git a/base/process/process_fuchsia.cc b/base/process/process_fuchsia.cc index 0c1b49b..43efbf0 100644 --- a/base/process/process_fuchsia.cc +++ b/base/process/process_fuchsia.cc
@@ -8,6 +8,7 @@ #include <magenta/syscalls.h> #include "base/debug/activity_tracker.h" +#include "base/fuchsia/default_job.h" #include "base/strings/stringprintf.h" namespace base { @@ -50,7 +51,7 @@ // mx_job_default() might not contain it, so this call can fail. ScopedMxHandle handle; mx_status_t status = mx_object_get_child( - mx_job_default(), pid, MX_RIGHT_SAME_RIGHTS, handle.receive()); + GetDefaultJob(), pid, MX_RIGHT_SAME_RIGHTS, handle.receive()); if (status != MX_OK) { DLOG(ERROR) << "mx_object_get_child failed: " << status; return Process();
diff --git a/base/test/android/javatests/src/org/chromium/base/test/BaseChromiumAndroidJUnitRunner.java b/base/test/android/javatests/src/org/chromium/base/test/BaseChromiumAndroidJUnitRunner.java index 8a23048..e721899 100644 --- a/base/test/android/javatests/src/org/chromium/base/test/BaseChromiumAndroidJUnitRunner.java +++ b/base/test/android/javatests/src/org/chromium/base/test/BaseChromiumAndroidJUnitRunner.java
@@ -8,6 +8,9 @@ import android.app.Application; import android.app.Instrumentation; import android.content.Context; +import android.content.pm.InstrumentationInfo; +import android.content.pm.PackageManager; +import android.content.pm.PackageManager.NameNotFoundException; import android.os.Bundle; import android.support.test.InstrumentationRegistry; import android.support.test.internal.runner.RunnerArgs; @@ -27,10 +30,22 @@ * This class is the equivalent of BaseChromiumInstrumentationTestRunner in JUnit3. Please * beware that is this not a class runner. It is declared in test apk AndroidManifest.xml * <instrumentation> + * + * TODO(yolandyan): remove this class after all tests are converted to JUnit4. Use class runner + * for test listing. */ public class BaseChromiumAndroidJUnitRunner extends AndroidJUnitRunner { private static final String LIST_ALL_TESTS_FLAG = "org.chromium.base.test.BaseChromiumAndroidJUnitRunner.TestList"; + private static final String LIST_TESTS_PACKAGE_FLAG = + "org.chromium.base.test.BaseChromiumAndroidJUnitRunner.TestListPackage"; + /** + * This flag is supported by AndroidJUnitRunner. + * + * See the following page for detail + * https://developer.android.com/reference/android/support/test/runner/AndroidJUnitRunner.html + */ + private static final String ARGUMENT_TEST_PACKAGE = "package"; /** * The following arguments are corresponding to AndroidJUnitRunner command line arguments. @@ -84,6 +99,23 @@ } } + // TODO(yolandyan): Move this to test harness side once this class gets removed + private void addTestListPackage(Bundle bundle) { + PackageManager pm = getContext().getPackageManager(); + InstrumentationInfo info; + try { + info = pm.getInstrumentationInfo(getComponentName(), PackageManager.GET_META_DATA); + } catch (NameNotFoundException e) { + Log.e(TAG, String.format("Could not find component %s", getComponentName())); + throw new RuntimeException(e); + } + Bundle metaDataBundle = info.metaData; + if (metaDataBundle != null && metaDataBundle.getString(LIST_TESTS_PACKAGE_FLAG) != null) { + bundle.putString( + ARGUMENT_TEST_PACKAGE, metaDataBundle.getString(LIST_TESTS_PACKAGE_FLAG)); + } + } + private void listTests() { Bundle results = new Bundle(); TestListInstrumentationRunListener listener = new TestListInstrumentationRunListener(); @@ -92,11 +124,13 @@ executorBuilder.addRunListener(listener); Bundle junit3Arguments = new Bundle(InstrumentationRegistry.getArguments()); junit3Arguments.putString(ARGUMENT_NOT_ANNOTATION, "org.junit.runner.RunWith"); + addTestListPackage(junit3Arguments); TestRequest listJUnit3TestRequest = createListTestRequest(junit3Arguments); results = executorBuilder.build().execute(listJUnit3TestRequest); Bundle junit4Arguments = new Bundle(InstrumentationRegistry.getArguments()); junit4Arguments.putString(ARGUMENT_ANNOTATION, "org.junit.runner.RunWith"); + addTestListPackage(junit4Arguments); // Do not use Log runner from android test support. //
diff --git a/base/test/test_file_util.cc b/base/test/test_file_util.cc index 40b25f01..8dafc58a 100644 --- a/base/test/test_file_util.cc +++ b/base/test/test_file_util.cc
@@ -20,9 +20,4 @@ return false; } -// Declared in base/files/file_path.h. -void PrintTo(const FilePath& path, std::ostream* out) { - *out << path.value(); -} - } // namespace base
diff --git a/build/android/apk_operations.py b/build/android/apk_operations.py old mode 100644 new mode 100755 index a116781..d861b5e --- a/build/android/apk_operations.py +++ b/build/android/apk_operations.py
@@ -1,3 +1,4 @@ +#!/usr/bin/env python # 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. @@ -37,25 +38,27 @@ return getattr(colorama.Fore, color) + text + colorama.Fore.RESET -def _InstallApk(apk, install_dict, devices_obj): +def _InstallApk(devices, apk, install_dict): def install(device): if install_dict: installer.Install(device, install_dict, apk=apk) else: device.Install(apk) - devices_obj.pMap(install) + + logging.info('Installing %sincremental apk.', '' if install_dict else 'non-') + device_utils.DeviceUtils.parallel(devices).pMap(install) -def _UninstallApk(install_dict, devices_obj, apk_package): +def _UninstallApk(devices, install_dict, package_name): def uninstall(device): if install_dict: - installer.Uninstall(device, apk_package) + installer.Uninstall(device, package_name) else: - device.Uninstall(apk_package) - devices_obj.pMap(uninstall) + device.Uninstall(package_name) + device_utils.DeviceUtils.parallel(devices).pMap(uninstall) -def _LaunchUrl(devices_obj, input_args, device_args_file, url, apk): +def _LaunchUrl(devices, input_args, device_args_file, url, apk): if input_args and device_args_file is None: raise Exception('This apk does not support any flags.') if url: @@ -81,17 +84,17 @@ activity=view_activity, data=url, package=apk.GetPackageName()) device.StartActivity(launch_intent) - devices_obj.pMap(launch) + device_utils.DeviceUtils.parallel(devices).pMap(launch) -def _ChangeFlags(devices, devices_obj, input_args, device_args_file): +def _ChangeFlags(devices, input_args, device_args_file): if input_args is None: - _DisplayArgs(devices, devices_obj, device_args_file) + _DisplayArgs(devices, device_args_file) else: flags = shlex.split(input_args) def update(device): flag_changer.FlagChanger(device, device_args_file).ReplaceFlags(flags) - devices_obj.pMap(update) + device_utils.DeviceUtils.parallel(devices).pMap(update) def _TargetCpuToTargetArch(target_cpu): @@ -102,13 +105,13 @@ return target_cpu -def _RunGdb(apk_name, apk_package, device, target_cpu, extra_args, verbose): +def _RunGdb(device, package_name, output_directory, target_cpu, extra_args, + verbose): gdb_script_path = os.path.dirname(__file__) + '/adb_gdb' cmd = [ gdb_script_path, - '--program-name=%s' % os.path.splitext(apk_name)[0], - '--package-name=%s' % apk_package, - '--output-directory=%s' % constants.GetOutDirectory(), + '--package-name=%s' % package_name, + '--output-directory=%s' % output_directory, '--adb=%s' % adb_wrapper.AdbWrapper.GetAdbPath(), '--device=%s' % device.serial, # Use one lib dir per device so that changing between devices does require @@ -136,16 +139,17 @@ yield result -def _RunMemUsage(devices, devices_obj, apk_package): +def _RunMemUsage(devices, package_name): def mem_usage_helper(d): ret = [] - proc_map = d.GetPids(apk_package) + proc_map = d.GetPids(package_name) for name, pids in proc_map.iteritems(): for pid in pids: ret.append((name, pid, d.GetMemoryUsageForPid(pid))) return ret - all_results = devices_obj.pMap(mem_usage_helper).pGet(None) + parallel_devices = device_utils.DeviceUtils.parallel(devices) + all_results = parallel_devices.pMap(mem_usage_helper).pGet(None) for result in _PrintPerDeviceOutput(devices, all_results): if not result: print 'No processes found.' @@ -201,7 +205,7 @@ logging.error('Failed to parse du output:\n%s', output) -def _RunDiskUsage(devices, devices_obj, apk_package, verbose): +def _RunDiskUsage(devices, package_name, verbose): # Measuring dex size is a bit complicated: # https://source.android.com/devices/tech/dalvik/jit-compiler # @@ -250,7 +254,7 @@ # cmd package compile -m speed-profile org.chromium.chrome # For others def disk_usage_helper(d): package_output = '\n'.join(d.RunShellCommand( - ['dumpsys', 'package', apk_package], check_return=True)) + ['dumpsys', 'package', package_name], check_return=True)) # Prints a message but does not return error when apk is not installed. if 'Unable to find package:' in package_output: return None @@ -274,7 +278,7 @@ compilation_filters.add(re.sub(r'\s+', '', m.group(1))) compilation_filter = ','.join(sorted(compilation_filters)) - data_dir_sizes = _DuHelper(d, '%s/{*,.*}' % data_dir, run_as=apk_package) + data_dir_sizes = _DuHelper(d, '%s/{*,.*}' % data_dir, run_as=package_name) # Measure code_cache separately since it can be large. code_cache_sizes = {} code_cache_dir = next( @@ -282,7 +286,7 @@ if code_cache_dir: data_dir_sizes.pop(code_cache_dir) code_cache_sizes = _DuHelper(d, '%s/{*,.*}' % code_cache_dir, - run_as=apk_package) + run_as=package_name) apk_path_spec = code_path if not apk_path_spec.endswith('.apk'): @@ -324,7 +328,8 @@ for path, size in sorted(sizes.iteritems()): print ' %s: %skb' % (path, size) - all_results = devices_obj.pMap(disk_usage_helper).pGet(None) + parallel_devices = device_utils.DeviceUtils.parallel(devices) + all_results = parallel_devices.pMap(disk_usage_helper).pGet(None) for result in _PrintPerDeviceOutput(devices, all_results): if not result: print 'APK is not installed.' @@ -347,8 +352,9 @@ print 'Total: %skb (%.1fmb)' % (total, total / 1024.0) -def _RunPs(devices, devices_obj, apk_package): - all_pids = devices_obj.GetPids(apk_package).pGet(None) +def _RunPs(devices, package_name): + parallel_devices = device_utils.DeviceUtils.parallel(devices) + all_pids = parallel_devices.GetPids(package_name).pGet(None) for proc_map in _PrintPerDeviceOutput(devices, all_pids): if not proc_map: print 'No processes found.' @@ -357,9 +363,11 @@ print name, ','.join(pids) -def _RunShell(devices, devices_obj, apk_package, cmd): +def _RunShell(devices, package_name, cmd): if cmd: - outputs = devices_obj.RunShellCommand(cmd, run_as=apk_package).pGet(None) + parallel_devices = device_utils.DeviceUtils.parallel(devices) + outputs = parallel_devices.RunShellCommand( + cmd, run_as=package_name).pGet(None) for output in _PrintPerDeviceOutput(devices, outputs): for line in output: print line @@ -368,25 +376,27 @@ cmd = [adb_path, '-s', devices[0].serial, 'shell'] # Pre-N devices do not support -t flag. if devices[0].build_version_sdk >= version_codes.NOUGAT: - cmd += ['-t', 'run-as', apk_package] + cmd += ['-t', 'run-as', package_name] else: print 'Upon entering the shell, run:' - print 'run-as', apk_package + print 'run-as', package_name print os.execv(adb_path, cmd) -def _RunCompileDex(devices, devices_obj, apk_package, compilation_filter): +def _RunCompileDex(devices, package_name, compilation_filter): cmd = ['cmd', 'package', 'compile', '-f', '-m', compilation_filter, - apk_package] - outputs = devices_obj.RunShellCommand(cmd).pGet(None) + package_name] + parallel_devices = device_utils.DeviceUtils.parallel(devices) + outputs = parallel_devices.RunShellCommand(cmd).pGet(None) for output in _PrintPerDeviceOutput(devices, outputs): for line in output: print line -# TODO(Yipengw):add "--all" in the MultipleDevicesError message and use it here. -def _GenerateMissingAllFlagMessage(devices, devices_obj): +# TODO(agrieve):add "--all" in the MultipleDevicesError message and use it here. +def _GenerateMissingAllFlagMessage(devices): + devices_obj = device_utils.DeviceUtils.parallel(devices) descriptions = devices_obj.pMap(lambda d: d.build_description).pGet(None) msg = ('More than one device available. Use --all to select all devices, ' 'or use --device to select a device by serial.\n\nAvailable ' @@ -396,102 +406,29 @@ return msg -def _DisplayArgs(devices, devices_obj, device_args_file): +def _DisplayArgs(devices, device_args_file): def flags_helper(d): changer = flag_changer.FlagChanger(d, device_args_file) return changer.GetCurrentFlags() - outputs = devices_obj.pMap(flags_helper).pGet(None) + parallel_devices = device_utils.DeviceUtils.parallel(devices) + outputs = parallel_devices.pMap(flags_helper).pGet(None) print 'Existing flags per-device (via /data/local/tmp/%s):' % device_args_file for flags in _PrintPerDeviceOutput(devices, outputs, single_line=True): quoted_flags = ' '.join(pipes.quote(f) for f in flags) print quoted_flags or 'No flags set.' -def _AddCommonOptions(parser): - parser.add_argument('--all', - action='store_true', - default=False, - help='Operate on all connected devices.',) - parser.add_argument('-d', - '--device', - action='append', - default=[], - dest='devices', - help='Target device for script to work on. Enter ' - 'multiple times for multiple devices.') - parser.add_argument('-v', - '--verbose', - action='count', - default=0, - dest='verbose_count', - help='Verbose level (multiple times for more)') - - -def _AddInstallOptions(parser): - parser = parser.add_argument_group('install arguments') - parser.add_argument('--incremental', - action='store_true', - default=False, - help='Always install an incremental apk.') - parser.add_argument('--non-incremental', - action='store_true', - default=False, - help='Always install a non-incremental apk.') - - -def _AddLaunchOptions(parser): - parser = parser.add_argument_group('launch arguments') - parser.add_argument('url', - nargs='?', - help='The URL this command launches.') - - -def _AddArgsOptions(parser): - parser = parser.add_argument_group('argv arguments') - parser.add_argument('--args', - help='The flags set by the user.') - - -def _DeviceCachePath(device): +def _DeviceCachePath(device, output_directory): file_name = 'device_cache_%s.json' % device.serial - return os.path.join(constants.GetOutDirectory(), file_name) + return os.path.join(output_directory, file_name) -def _SelectApk(apk_path, incremental_install_json_path, parser, args): - if apk_path and not os.path.exists(apk_path): - apk_path = None - if (incremental_install_json_path and - not os.path.exists(incremental_install_json_path)): - incremental_install_json_path = None - - if args.command in ('install', 'run'): - if args.incremental and args.non_incremental: - parser.error('--incremental and --non-incremental cannot both be used.') - elif args.non_incremental: - if not apk_path: - parser.error('Apk has not been built.') - incremental_install_json_path = None - elif args.incremental: - if not incremental_install_json_path: - parser.error('Incremental apk has not been built.') - apk_path = None - - if apk_path and incremental_install_json_path: - parser.error('Both incremental and non-incremental apks exist, please ' - 'use --incremental or --non-incremental to select one.') - elif apk_path: - logging.info('Using the non-incremental apk.') - elif incremental_install_json_path: - logging.info('Using the incremental apk.') - else: - parser.error('Neither incremental nor non-incremental apk is built.') - return apk_path, incremental_install_json_path - - -def _LoadDeviceCaches(devices): +def _LoadDeviceCaches(devices, output_directory): + if not output_directory: + return for d in devices: - cache_path = _DeviceCachePath(d) + cache_path = _DeviceCachePath(d, output_directory) if os.path.exists(cache_path): logging.debug('Using device cache: %s', cache_path) with open(cache_path) as f: @@ -502,187 +439,416 @@ logging.debug('No cache present for device: %s', d) -def _SaveDeviceCaches(devices): +def _SaveDeviceCaches(devices, output_directory): + if not output_directory: + return for d in devices: - cache_path = _DeviceCachePath(d) + cache_path = _DeviceCachePath(d, output_directory) with open(cache_path, 'w') as f: f.write(d.DumpCacheData()) logging.info('Wrote device cache: %s', cache_path) -# target_cpu=None so that old wrapper scripts continue to work without -# the need for a rebuild. -def Run(output_directory, apk_path, incremental_install_json_path, - command_line_flags_file, target_cpu=None): - colorama.init() - constants.SetOutputDirectory(output_directory) +class _Command(object): + name = None + description = None + needs_package_name = False + needs_output_directory = False + needs_apk_path = False + supports_incremental = False + accepts_command_line_flags = False + accepts_args = False + accepts_url = False + all_devices_by_default = False + calls_exec = False - parser = argparse.ArgumentParser() - command_parsers = parser.add_subparsers(title='Apk operations', - dest='command') - subp = command_parsers.add_parser('install', help='Install the apk.') - _AddCommonOptions(subp) - _AddInstallOptions(subp) + def __init__(self, from_wrapper_script): + self._parser = None + self._from_wrapper_script = from_wrapper_script + self.args = None + self.apk_helper = None + self.install_dict = None + self.devices = None + # Do not support incremental install outside the context of wrapper scripts. + if not from_wrapper_script: + self.supports_incremental = False - subp = command_parsers.add_parser('uninstall', help='Uninstall the apk.') - _AddCommonOptions(subp) + def _RegisterExtraArgs(self, subp): + pass - subp = command_parsers.add_parser('launch', - help='Launches the apk with the given ' - 'command-line flags, and optionally the ' - 'given URL') - _AddCommonOptions(subp) - _AddLaunchOptions(subp) - _AddArgsOptions(subp) + def RegisterArgs(self, parser): + subp = parser.add_parser(self.name, help=self.description) + self._parser = subp + subp.set_defaults(command=self) + subp.add_argument('--all', + action='store_true', + default=self.all_devices_by_default, + help='Operate on all connected devices.',) + subp.add_argument('-d', + '--device', + action='append', + default=[], + dest='devices', + help='Target device for script to work on. Enter ' + 'multiple times for multiple devices.') + subp.add_argument('-v', + '--verbose', + action='count', + default=0, + dest='verbose_count', + help='Verbose level (multiple times for more)') + group = subp.add_argument_group('%s arguments' % self.name) - subp = command_parsers.add_parser('run', help='Install and launch.') - _AddCommonOptions(subp) - _AddInstallOptions(subp) - _AddLaunchOptions(subp) - _AddArgsOptions(subp) + if self.needs_package_name: + # Always gleaned from apk when using wrapper scripts. + group.add_argument('--package-name', + help=argparse.SUPPRESS if self._from_wrapper_script else ( + "App's package name.")) - subp = command_parsers.add_parser('stop', help='Stop apks on all devices') - _AddCommonOptions(subp) + if self.needs_apk_path or self.needs_package_name: + # When passed by wrapper script, don't show in --help. + group.add_argument('--apk-path', + required=self.needs_apk_path and not self._from_wrapper_script, + help=argparse.SUPPRESS if self._from_wrapper_script else ( + 'Path to .apk')) - subp = command_parsers.add_parser('clear-data', - help='Clear states for the given package') - _AddCommonOptions(subp) + if self.supports_incremental: + group.add_argument('--incremental', + action='store_true', + default=False, + help='Always install an incremental apk.') + group.add_argument('--non-incremental', + action='store_true', + default=False, + help='Always install a non-incremental apk.') - subp = command_parsers.add_parser('argv', - help='Display and update flags on devices.') - _AddCommonOptions(subp) - _AddArgsOptions(subp) + # accepts_command_line_flags and accepts_args are mutually exclusive. + # argparse will throw if they are both set. + if self.accepts_command_line_flags: + group.add_argument('--args', help='Command-line flags.') - subp = command_parsers.add_parser('gdb', - help='Run build/android/adb_gdb script.') - _AddCommonOptions(subp) - _AddArgsOptions(subp) + if self.accepts_args: + group.add_argument('--args', help='Extra arguments.') - subp = command_parsers.add_parser('logcat', - help='Run the shell command "adb logcat".') - _AddCommonOptions(subp) + if self.accepts_url: + group.add_argument('url', nargs='?', help='A URL to launch with.') - subp = command_parsers.add_parser('disk-usage', - help='Display disk usage for the APK.') - _AddCommonOptions(subp) + if not self._from_wrapper_script and self.accepts_command_line_flags: + # Provided by wrapper scripts. + group.add_argument( + '--command-line-flags-file-name', + help='Name of the command-line flags file') - subp = command_parsers.add_parser('mem-usage', - help='Display memory usage of currently running APK processes.') - _AddCommonOptions(subp) + self._RegisterExtraArgs(group) - subp = command_parsers.add_parser('ps', - help='Shows PIDs of any APK processes currently running.') - _AddCommonOptions(subp) + def ProcessArgs(self, args): + devices = device_utils.DeviceUtils.HealthyDevices( + device_arg=args.devices, + enable_device_files_cache=bool(args.output_directory), + default_retries=0) + self.args = args + self.devices = devices + # TODO(agrieve): Device cache should not depend on output directory. + # Maybe put int /tmp? + _LoadDeviceCaches(devices, args.output_directory) - subp = command_parsers.add_parser('shell', - help='Same as "adb shell <command>", but runs as the apk\'s uid (via ' - 'run-as). Useful for inspecting the app\'s data directory.') - _AddCommonOptions(subp) - group = subp.add_argument_group('shell arguments') - group.add_argument('cmd', nargs=argparse.REMAINDER, help='Command to run.') + try: + if len(devices) > 1: + if self.calls_exec: + self._parser.error(device_errors.MultipleDevicesError(devices)) + if not args.all and not args.devices: + self._parser.error(_GenerateMissingAllFlagMessage(devices)) - subp = command_parsers.add_parser('compile-dex', - help='Applicable only for Android N+. Forces .odex files to be compiled ' - 'with the given compilation filter. To see existing filter, use ' - '"disk-usage" command.') - _AddCommonOptions(subp) - group = subp.add_argument_group('compile-dex arguments') - # Allow only the most useful subset of filters. - group.add_argument('compilation_filter', - choices=['verify', 'quicken', 'space-profile', 'space', - 'speed-profile', 'speed'], - help='For WebView/Monochrome, use "speed". ' - 'For other apks, use "speed-profile".') + if self.supports_incremental: + if args.incremental and args.non_incremental: + self._parser.error('Must use only one of --incremental and ' + '--non-incremental') + elif args.non_incremental: + if not args.apk_path: + self._parser.error('Apk has not been built.') + args.incremental_json = None + elif args.incremental: + if not args.incremental_json: + self._parser.error('Incremental apk has not been built.') + args.apk_path = None + + if args.apk_path and args.incremental_json: + self._parser.error('Both incremental and non-incremental apks exist. ' + 'Select using --incremental or --non-incremental') + elif not args.apk_path and not args.incremental_json: + self._parser.error( + 'Neither incremental nor non-incremental apk is built.') + + if self.needs_apk_path or args.apk_path: + if args.incremental_json: + with open(args.incremental_json) as f: + self.install_dict = json.load(f) + self.apk_helper = apk_helper.ToHelper( + os.path.join(args.output_directory, + self.install_dict['apk_path'])) + else: + self.apk_helper = apk_helper.ToHelper(args.apk_path) + + if self.needs_package_name and not args.package_name: + if self.apk_helper: + args.package_name = self.apk_helper.GetPackageName() + else: + self._parser.error('One of --package-name or --apk-path is required.') + + # Save cache now if command will not get a chance to afterwards. + if self.calls_exec: + _SaveDeviceCaches(devices, args.output_directory) + except: + _SaveDeviceCaches(devices, args.output_directory) + raise + + +class _InstallCommand(_Command): + name = 'install' + description = 'Installs the APK to one or more devices.' + needs_apk_path = True + supports_incremental = True + + def Run(self): + _InstallApk(self.devices, self.apk_helper, self.install_dict) + + +class _UninstallCommand(_Command): + name = 'uninstall' + description = 'Removes the APK to one or more devices.' + needs_package_name = True + + def Run(self): + _UninstallApk(self.devices, self.install_dict, self.args.package_name) + + +class _LaunchCommand(_Command): + name = 'launch' + description = ('Sends a launch intent for the APK after first writing the ' + 'command-line flags file.') + # TODO(agrieve): Launch could be changed to require only package name by + # parsing "dumpsys package" for launch & view activities. + needs_apk_path = True + accepts_command_line_flags = True + accepts_url = True + all_devices_by_default = True + + def Run(self): + _LaunchUrl(self.devices, self.args.args, self.args.command_line_flags_file, + self.args.url, self.apk_helper) + + +class _RunCommand(_Command): + name = 'run' + description = 'Install and then launch.' + needs_apk_path = True + supports_incremental = True + needs_package_name = True + accepts_command_line_flags = True + accepts_url = True + + def Run(self): + logging.warning('Installing...') + _InstallApk(self.devices, self.apk_helper, self.install_dict) + logging.warning('Sending launch intent...') + _LaunchUrl(self.devices, self.args.args, self.args.command_line_flags_file, + self.args.url, self.apk_helper) + + +class _StopCommand(_Command): + name = 'stop' + description = 'Force-stops the app.' + needs_package_name = True + all_devices_by_default = True + + def Run(self): + device_utils.DeviceUtils.parallel(self.devices).ForceStop( + self.args.package_name) + + +class _ClearDataCommand(_Command): + name = 'clear-data' + descriptions = 'Clears all app data.' + needs_package_name = True + all_devices_by_default = True + + def Run(self): + device_utils.DeviceUtils.parallel(self.devices).ClearApplicationState( + self.args.package_name) + + +class _ArgvCommand(_Command): + name = 'argv' + description = 'Display and optionally update command-line flags file.' + needs_package_name = True + accepts_command_line_flags = True + all_devices_by_default = True + + def Run(self): + _ChangeFlags(self.devices, self.args.args, + self.args.command_line_flags_file) + + +class _GdbCommand(_Command): + name = 'gdb' + description = 'Runs //build/android/adb_gdb with apk-specific args.' + needs_package_name = True + needs_output_directory = True + accepts_args = True + calls_exec = True + + def Run(self): + extra_args = shlex.split(self.args.args or '') + _RunGdb(self.devices[0], self.args.package_name, self.args.output_directory, + self.args.target_cpu, extra_args, bool(self.args.verbose_count)) + + +class _LogcatCommand(_Command): + name = 'logcat' + description = 'Runs "adb logcat"' + calls_exec = True + + def Run(self): + adb_path = adb_wrapper.AdbWrapper.GetAdbPath() + cmd = [adb_path, '-s', self.devices[0].serial, 'logcat'] + os.execv(adb_path, cmd) + + +class _PsCommand(_Command): + name = 'ps' + description = 'Show PIDs of any APK processes currently running.' + needs_package_name = True + all_devices_by_default = True + + def Run(self): + _RunPs(self.devices, self.args.package_name) + + +class _DiskUsageCommand(_Command): + name = 'disk-usage' + description = 'Show how much device storage is being consumed by the app.' + needs_package_name = True + all_devices_by_default = True + + def Run(self): + _RunDiskUsage(self.devices, self.args.package_name, + bool(self.args.verbose_count)) + + +class _MemUsageCommand(_Command): + name = 'mem-usage' + description = 'Show memory usage of currently running APK processes.' + needs_package_name = True + all_devices_by_default = True + + def Run(self): + _RunMemUsage(self.devices, self.args.package_name) + + +class _ShellCommand(_Command): + name = 'shell' + description = ('Same as "adb shell <command>", but runs as the apk\'s uid ' + '(via run-as). Useful for inspecting the app\'s data ' + 'directory.') + needs_package_name = True + + @property + def calls_exec(self): + return not self.args.cmd + + def _RegisterExtraArgs(self, group): + group.add_argument( + 'cmd', nargs=argparse.REMAINDER, help='Command to run.') + + def Run(self): + _RunShell(self.devices, self.args.package_name, self.args.cmd) + + +class _CompileDexCommand(_Command): + name = 'compile-dex' + description = ('Applicable only for Android N+. Forces .odex files to be ' + 'compiled with the given compilation filter. To see existing ' + 'filter, use "disk-usage" command.') + needs_package_name = True + all_devices_by_default = True + + def _RegisterExtraArgs(self, group): + group.add_argument( + 'compilation_filter', + choices=['verify', 'quicken', 'space-profile', 'space', + 'speed-profile', 'speed'], + help='For WebView/Monochrome, use "speed". For other apks, use ' + '"speed-profile".') + + def Run(self): + _RunCompileDex(self.devices, self.args.package_name, + self.args.compilation_filter) + + +_COMMANDS = [ + _InstallCommand, + _UninstallCommand, + _LaunchCommand, + _StopCommand, + _ClearDataCommand, + _ArgvCommand, + _GdbCommand, + _LogcatCommand, + _PsCommand, + _DiskUsageCommand, + _MemUsageCommand, + _ShellCommand, + _CompileDexCommand, +] + + +def _ParseArgs(parser, from_wrapper_script): + subparsers = parser.add_subparsers() + commands = [clazz(from_wrapper_script) for clazz in _COMMANDS] + for command in commands: + if from_wrapper_script or not command.needs_output_directory: + command.RegisterArgs(subparsers) # Show extended help when no command is passed. argv = sys.argv[1:] if not argv: argv = ['--help'] - args = parser.parse_args(argv) + + return parser.parse_args(argv) + + +def _RunInternal(parser, output_directory=None): + colorama.init() + parser.set_defaults(output_directory=output_directory) + from_wrapper_script = bool(output_directory) + args = _ParseArgs(parser, from_wrapper_script) run_tests_helper.SetLogLevel(args.verbose_count) - command = args.command + args.command.ProcessArgs(args) + args.command.Run() + _SaveDeviceCaches(args.command.devices, output_directory) + +# TODO(agrieve): Remove =None from target_cpu on or after October 2017. +# It exists only so that stale wrapper scripts continue to work. +def Run(output_directory, apk_path, incremental_json, command_line_flags_file, + target_cpu=None): + """Entry point for generated wrapper scripts.""" + constants.SetOutputDirectory(output_directory) + devil_chromium.Initialize(output_directory=output_directory) + parser = argparse.ArgumentParser() + parser.set_defaults( + command_line_flags_file=command_line_flags_file, + target_cpu=target_cpu, + apk_path=apk_path if os.path.exists(apk_path) else None, + incremental_json=( + incremental_json if os.path.exists(incremental_json) else None)) + _RunInternal(parser, output_directory=output_directory) + + +def main(): devil_chromium.Initialize() + _RunInternal(argparse.ArgumentParser(), output_directory=None) - devices = device_utils.DeviceUtils.HealthyDevices( - device_arg=args.devices, - enable_device_files_cache=True, - default_retries=0) - devices_obj = device_utils.DeviceUtils.parallel(devices) - _LoadDeviceCaches(devices) - try: - if len(devices) > 1: - if command in ('gdb', 'logcat') or command == 'shell' and not args.cmd: - raise device_errors.MultipleDevicesError(devices) - default_all = command in ('argv', 'stop', 'clear-data', 'disk-usage', - 'mem-usage', 'ps', 'compile-dex') - if default_all or args.devices: - args.all = True - if len(devices) > 1 and not args.all: - raise Exception(_GenerateMissingAllFlagMessage(devices, devices_obj)) - except: - _SaveDeviceCaches(devices) - raise - - apk_name = os.path.basename(apk_path) - apk_path, incremental_install_json_path = _SelectApk( - apk_path, incremental_install_json_path, parser, args) - install_dict = None - - if incremental_install_json_path: - with open(incremental_install_json_path) as f: - install_dict = json.load(f) - apk = apk_helper.ToHelper( - os.path.join(output_directory, install_dict['apk_path'])) - else: - apk = apk_helper.ToHelper(apk_path) - - apk_package = apk.GetPackageName() - - # These commands use os.exec(), so we won't get a chance to update the cache - # afterwards. - if command in ('gdb', 'logcat', 'shell'): - _SaveDeviceCaches(devices) - - if command == 'install': - _InstallApk(apk, install_dict, devices_obj) - elif command == 'uninstall': - _UninstallApk(install_dict, devices_obj, apk_package) - elif command == 'launch': - _LaunchUrl(devices_obj, args.args, command_line_flags_file, - args.url, apk) - elif command == 'run': - logging.warning('Installing...') - _InstallApk(apk, install_dict, devices_obj) - logging.warning('Sending launch intent...') - _LaunchUrl(devices_obj, args.args, command_line_flags_file, - args.url, apk) - elif command == 'stop': - devices_obj.ForceStop(apk_package) - elif command == 'clear-data': - devices_obj.ClearApplicationState(apk_package) - elif command == 'argv': - _ChangeFlags(devices, devices_obj, args.args, - command_line_flags_file) - elif command == 'gdb': - extra_args = shlex.split(args.args or '') - _RunGdb(apk_name, apk_package, devices[0], target_cpu, extra_args, - args.verbose_count) - elif command == 'logcat': - adb_path = adb_wrapper.AdbWrapper.GetAdbPath() - cmd = [adb_path, '-s', devices[0].serial, 'logcat'] - os.execv(adb_path, cmd) - elif command == 'mem-usage': - _RunMemUsage(devices, devices_obj, apk_package) - elif command == 'disk-usage': - _RunDiskUsage(devices, devices_obj, apk_package, args.verbose_count) - elif command == 'ps': - _RunPs(devices, devices_obj, apk_package) - elif command == 'shell': - _RunShell(devices, devices_obj, apk_package, args.cmd) - elif command == 'compile-dex': - _RunCompileDex(devices, devices_obj, apk_package, args.compilation_filter) - - # Save back to the cache. - _SaveDeviceCaches(devices) +if __name__ == '__main__': + sys.exit(main())
diff --git a/build/android/pylib/local/device/local_device_instrumentation_test_run.py b/build/android/pylib/local/device/local_device_instrumentation_test_run.py index 231370e8f..4da50fdc7 100644 --- a/build/android/pylib/local/device/local_device_instrumentation_test_run.py +++ b/build/android/pylib/local/device/local_device_instrumentation_test_run.py
@@ -609,7 +609,6 @@ test_package = self._test_instance.test_package extras = {} extras['log'] = 'true' - extras['package'] = '.'.join(test_package.split('.')[:2]) extras[_EXTRA_TEST_LIST] = dev_test_list_json.name target = '%s/%s' % (test_package, junit4_runner_class) test_list_run_output = dev.StartInstrumentation(
diff --git a/build/fuchsia/runner_common.py b/build/fuchsia/runner_common.py index c6463623..af145cc 100755 --- a/build/fuchsia/runner_common.py +++ b/build/fuchsia/runner_common.py
@@ -9,14 +9,12 @@ to run, or starts the bootserver to allow running on a hardware device.""" import argparse -import multiprocessing import os import re import shutil import signal import subprocess import sys -import tempfile DIR_SOURCE_ROOT = os.path.abspath( @@ -123,7 +121,7 @@ def _StripBinary(dry_run, bin_path): """Creates a stripped copy of the executable at |bin_path| and returns the path to the stripped copy.""" - strip_path = tempfile.mktemp() + strip_path = bin_path + '.bootfs_stripped' _RunAndCheck(dry_run, ['/usr/bin/strip', bin_path, '-o', strip_path]) if not dry_run and not os.path.exists(strip_path): raise Exception('strip did not create output file') @@ -131,15 +129,18 @@ def _StripBinaries(dry_run, file_mapping): - """Strips all executables in |file_mapping|, and returns a new mapping - dictionary, suitable to pass to _WriteManifest()""" - new_mapping = file_mapping.copy() + """Updates the supplied manifest |file_mapping|, by stripping any executables + and updating their entries to point to the stripped location. Returns a + mapping from target executables to their un-stripped paths, for use during + symbolization.""" + symbols_mapping = {} for target, source in file_mapping.iteritems(): with open(source, 'rb') as f: file_tag = f.read(4) if file_tag == '\x7fELF': - new_mapping[target] = _StripBinary(dry_run, source) - return new_mapping + symbols_mapping[target] = source + file_mapping[target] = _StripBinary(dry_run, source) + return symbols_mapping def _WriteManifest(manifest_file, file_mapping): @@ -165,7 +166,7 @@ file_mapping = dict(runtime_deps) # Generate a script that runs the binaries and shuts down QEMU (if used). - autorun_file = tempfile.NamedTemporaryFile() + autorun_file = open(bin_name + '.bootfs_autorun', 'w') autorun_file.write('#!/bin/sh\n') if _IsRunningOnBot(): # We drop to -smp 1 to avoid counterintuitive observations on the realtime @@ -211,11 +212,11 @@ lambda x: _MakeTargetImageName(DIR_SOURCE_ROOT, output_directory, x)) # Strip any binaries in the file list, and generate a manifest mapping. - manifest_mapping = _StripBinaries(dry_run, file_mapping) + symbols_mapping = _StripBinaries(dry_run, file_mapping) # Write the target, source mappings to a file suitable for bootfs. - manifest_file = tempfile.NamedTemporaryFile() - _WriteManifest(manifest_file.file, manifest_mapping) + manifest_file = open(bin_name + '.bootfs_manifest', 'w') + _WriteManifest(manifest_file, file_mapping) manifest_file.flush() _DumpFile(dry_run, manifest_file.name, 'manifest') @@ -230,38 +231,45 @@ return None # Return both the name of the bootfs file, and the filename mapping. - return (bootfs_name, file_mapping) + return (bootfs_name, symbols_mapping) -def _SymbolizeEntry(entry): +def _SymbolizeEntries(entries): filename_re = re.compile(r'at ([-._a-zA-Z0-9/+]+):(\d+)') - raw, frame_id = entry['raw'], entry['frame_id'] - prefix = '#%s: ' % frame_id - if entry.has_key('debug_binary') and entry.has_key('pc_offset'): - # Invoke addr2line on the host-side binary to resolve the symbol. + # Use addr2line to symbolize all the |pc_offset|s in |entries| in one go. + # Entries with no |debug_binary| are also processed here, so that we get + # consistent output in that case, with the cannot-symbolize case. + addr2line_output = None + if entries[0].has_key('debug_binary'): addr2line_output = subprocess.check_output( - ['addr2line', '-Cipf', '--exe=' + entry['debug_binary'], - entry['pc_offset']]) + ['addr2line', '-Cipf', '-p', '--exe=' + entries[0]['debug_binary']] + + map(lambda entry: entry['pc_offset'], entries)).splitlines() + assert addr2line_output - # addr2line outputs a second line for inlining information, offset - # that to align it properly after the frame index. - addr2line_filtered = addr2line_output.strip().replace( - '(inlined', ' ' * len(prefix) + '(inlined') + # Collate a set of |(frame_id, result)| pairs from the output lines. + results = {} + for i in xrange(len(entries)): + entry = entries[i] + raw, frame_id = entry['raw'], entry['frame_id'] + prefix = '#%s: ' % frame_id - # Relativize path to DIR_SOURCE_ROOT if we see a filename. - def RelativizePath(m): - relpath = os.path.relpath(os.path.normpath(m.group(1)), DIR_SOURCE_ROOT) - return 'at ' + relpath + ':' + m.group(2) - addr2line_filtered = filename_re.sub(RelativizePath, addr2line_filtered) + if addr2line_output: + # Relativize path to DIR_SOURCE_ROOT if we see a filename. + def RelativizePath(m): + relpath = os.path.relpath(os.path.normpath(m.group(1)), DIR_SOURCE_ROOT) + return 'at ' + relpath + ':' + m.group(2) + filtered_line = filename_re.sub(RelativizePath, addr2line_output[i]) - # If symbolization fails just output the raw backtrace. - if '??' in addr2line_filtered: - addr2line_filtered = raw - else: - addr2line_filtered = raw + # If symbolization fails just output the raw backtrace. + if '??' in filtered_line: + filtered_line = raw + else: + filtered_line = raw - return '%s%s' % (prefix, addr2line_filtered) + results[entry['frame_id']] = prefix + filtered_line + + return results def _FindDebugBinary(entry, file_mapping): @@ -297,34 +305,24 @@ return None -def _ParallelSymbolizeBacktrace(backtrace, file_mapping): - # Disable handling of SIGINT during sub-process creation, to prevent - # sub-processes from consuming Ctrl-C signals, rather than the parent - # process doing so. - saved_sigint_handler = signal.signal(signal.SIGINT, signal.SIG_IGN) - p = multiprocessing.Pool(multiprocessing.cpu_count()) - # Restore the signal handler for the parent process. - signal.signal(signal.SIGINT, saved_sigint_handler) - - # Resolve the |binary| name in each entry to a host-accessible filename. +def _SymbolizeBacktrace(backtrace, file_mapping): + # Group |backtrace| entries according to the associated binary, and locate + # the path to the debug symbols for that binary, if any. + batches = {} for entry in backtrace: debug_binary = _FindDebugBinary(entry, file_mapping) if debug_binary: entry['debug_binary'] = debug_binary + batches.setdefault(debug_binary, []).append(entry) - symbolized = [] - try: - result = p.map_async(_SymbolizeEntry, backtrace) - symbolized = result.get(SYMBOLIZATION_TIMEOUT_SECS) - if not symbolized: - return [] - except multiprocessing.TimeoutError: - return ['(timeout error occurred during symbolization)'] - except KeyboardInterrupt: # SIGINT - p.terminate() + # Run _SymbolizeEntries on each batch and collate the results. + symbolized = {} + for batch in batches.itervalues(): + symbolized.update(_SymbolizeEntries(batch)) - return symbolized + # Map each backtrace to its symbolized form, by frame-id, and return the list. + return map(lambda entry: symbolized[entry['frame_id']], backtrace) def RunFuchsia(bootfs_and_manifest, use_device, dry_run): @@ -424,8 +422,8 @@ frame_id = matched.group('frame_id') if backtrace_line == 'end': if backtrace_entries: - for processed in _ParallelSymbolizeBacktrace(backtrace_entries, - bootfs_manifest): + for processed in _SymbolizeBacktrace(backtrace_entries, + bootfs_manifest): print processed backtrace_entries = [] continue
diff --git a/build/vs_toolchain.py b/build/vs_toolchain.py index 93a04ceb..738343b 100755 --- a/build/vs_toolchain.py +++ b/build/vs_toolchain.py
@@ -22,8 +22,8 @@ json_data_file = os.path.join(script_dir, 'win_toolchain.json') -# Use MSVS2015 as the default toolchain. -CURRENT_DEFAULT_TOOLCHAIN_VERSION = '2015' +# Use MSVS2017 as the default toolchain. +CURRENT_DEFAULT_TOOLCHAIN_VERSION = '2017' def SetEnvironmentAndGetRuntimeDllDirs():
diff --git a/cc/layers/layer.h b/cc/layers/layer.h index 482fc22..7834546 100644 --- a/cc/layers/layer.h +++ b/cc/layers/layer.h
@@ -362,7 +362,9 @@ void set_property_tree_sequence_number(int sequence_number) { property_tree_sequence_number_ = sequence_number; } - int property_tree_sequence_number() { return property_tree_sequence_number_; } + int property_tree_sequence_number() const { + return property_tree_sequence_number_; + } void SetTransformTreeIndex(int index); int transform_tree_index() const;
diff --git a/cc/raster/raster_source.cc b/cc/raster/raster_source.cc index d28efab..aedd659 100644 --- a/cc/raster/raster_source.cc +++ b/cc/raster/raster_source.cc
@@ -56,7 +56,8 @@ raster_canvas->clipRect(SkRect::MakeFromIRect(raster_bounds)); raster_canvas->translate(raster_transform.translation().x(), raster_transform.translation().y()); - raster_canvas->scale(raster_transform.scale(), raster_transform.scale()); + raster_canvas->scale(raster_transform.scale() / recording_scale_factor_, + raster_transform.scale() / recording_scale_factor_); PlaybackToCanvas(raster_canvas, target_color_space, settings); raster_canvas->restore(); }
diff --git a/cc/raster/raster_source_unittest.cc b/cc/raster/raster_source_unittest.cc index 0f33b51..1ee83af 100644 --- a/cc/raster/raster_source_unittest.cc +++ b/cc/raster/raster_source_unittest.cc
@@ -11,6 +11,7 @@ #include "cc/raster/playback_image_provider.h" #include "cc/test/fake_recording_source.h" #include "cc/test/skia_common.h" +#include "cc/test/test_skcanvas.h" #include "cc/tiles/software_image_decode_cache.h" #include "testing/gtest/include/gtest/gtest.h" #include "third_party/skia/include/core/SkPixelRef.h" @@ -20,6 +21,10 @@ #include "ui/gfx/geometry/rect.h" #include "ui/gfx/geometry/size_conversions.h" +using ::testing::_; +using ::testing::StrictMock; +using ::testing::Sequence; + namespace cc { namespace { @@ -602,5 +607,34 @@ EXPECT_EQ(SK_ColorRED, bitmap.getColor(24, y)); } +TEST(RasterSourceTest, RasterTransformWithoutRecordingScale) { + gfx::Size size(100, 100); + float recording_scale = 2.f; + std::unique_ptr<FakeRecordingSource> recording_source = + FakeRecordingSource::CreateFilledRecordingSource(size); + recording_source->Rerecord(); + recording_source->SetRecordingScaleFactor(recording_scale); + scoped_refptr<RasterSource> raster_source = + recording_source->CreateRasterSource(); + + StrictMock<MockCanvas> mock_canvas; + Sequence s; + RasterSource::PlaybackSettings settings; + settings.playback_to_shared_canvas = true; + + SkMatrix m; + m.setScale(1.f / recording_scale, 1.f / recording_scale); + + EXPECT_CALL(mock_canvas, willSave()).InSequence(s); + // The call to raster_canvas->scale() should have values with the recording + // scale removed. + EXPECT_CALL(mock_canvas, didConcat(m)).InSequence(s); + EXPECT_CALL(mock_canvas, willRestore()).InSequence(s); + + raster_source->PlaybackToCanvas(&mock_canvas, ColorSpaceForTesting(), + gfx::Rect(size), gfx::Rect(size), + gfx::AxisTransform2d(), settings); +} + } // namespace } // namespace cc
diff --git a/cc/trees/property_tree_builder.cc b/cc/trees/property_tree_builder.cc index 8d9799b..e094495 100644 --- a/cc/trees/property_tree_builder.cc +++ b/cc/trees/property_tree_builder.cc
@@ -245,12 +245,25 @@ return layer->test_properties()->sorting_context_id != 0; } +static inline bool HasLatestSequenceNumber(const Layer* layer, int number) { + return layer->property_tree_sequence_number() == number; +} + +static inline bool HasLatestSequenceNumber(const LayerImpl*, int) { + return true; +} + template <typename LayerType> void AddClipNodeIfNeeded(const DataForRecursion<LayerType>& data_from_ancestor, LayerType* layer, bool created_transform_node, DataForRecursion<LayerType>* data_for_children) { const bool inherits_clip = !ClipParent(layer); + // Sanity check the clip parent already built clip node before us. + DCHECK(inherits_clip || + HasLatestSequenceNumber( + ClipParent(layer), + data_from_ancestor.property_trees->sequence_number)); const int parent_id = inherits_clip ? data_from_ancestor.clip_tree_parent : ClipParent(layer)->clip_tree_index();
diff --git a/chrome/android/java/AndroidManifest.xml b/chrome/android/java/AndroidManifest.xml index 3f99a82..b9bc301 100644 --- a/chrome/android/java/AndroidManifest.xml +++ b/chrome/android/java/AndroidManifest.xml
@@ -756,6 +756,11 @@ </intent-filter> </receiver> + <!-- Download foreground service --> + <service android:name="org.chromium.chrome.browser.download.DownloadForegroundService" + android:exported="false"> + </service> + <!-- Download broadcast manager service --> <service android:name="org.chromium.chrome.browser.download.DownloadBroadcastManager" android:exported="false">
diff --git a/chrome/android/java/res/drawable-hdpi/contextual_search_bar_shadow.png b/chrome/android/java/res/drawable-hdpi/contextual_search_bar_shadow.png deleted file mode 100644 index 136cb8d..0000000 --- a/chrome/android/java/res/drawable-hdpi/contextual_search_bar_shadow.png +++ /dev/null Binary files differ
diff --git a/chrome/android/java/res/drawable-hdpi/modern_toolbar_shadow.png b/chrome/android/java/res/drawable-hdpi/modern_toolbar_shadow.png new file mode 100644 index 0000000..baf7d1c4b --- /dev/null +++ b/chrome/android/java/res/drawable-hdpi/modern_toolbar_shadow.png Binary files differ
diff --git a/chrome/android/java/res/drawable-mdpi/contextual_search_bar_shadow.png b/chrome/android/java/res/drawable-mdpi/contextual_search_bar_shadow.png deleted file mode 100644 index 09e2bdc7..0000000 --- a/chrome/android/java/res/drawable-mdpi/contextual_search_bar_shadow.png +++ /dev/null Binary files differ
diff --git a/chrome/android/java/res/drawable-mdpi/modern_toolbar_shadow.png b/chrome/android/java/res/drawable-mdpi/modern_toolbar_shadow.png new file mode 100644 index 0000000..25ac6592 --- /dev/null +++ b/chrome/android/java/res/drawable-mdpi/modern_toolbar_shadow.png Binary files differ
diff --git a/chrome/android/java/res/drawable-xhdpi/contextual_search_bar_shadow.png b/chrome/android/java/res/drawable-xhdpi/contextual_search_bar_shadow.png deleted file mode 100644 index 19f6936..0000000 --- a/chrome/android/java/res/drawable-xhdpi/contextual_search_bar_shadow.png +++ /dev/null Binary files differ
diff --git a/chrome/android/java/res/drawable-xhdpi/modern_toolbar_shadow.png b/chrome/android/java/res/drawable-xhdpi/modern_toolbar_shadow.png new file mode 100644 index 0000000..da84aae0 --- /dev/null +++ b/chrome/android/java/res/drawable-xhdpi/modern_toolbar_shadow.png Binary files differ
diff --git a/chrome/android/java/res/drawable-xxhdpi/contextual_search_bar_shadow.png b/chrome/android/java/res/drawable-xxhdpi/contextual_search_bar_shadow.png deleted file mode 100644 index 65d952d..0000000 --- a/chrome/android/java/res/drawable-xxhdpi/contextual_search_bar_shadow.png +++ /dev/null Binary files differ
diff --git a/chrome/android/java/res/drawable-xxhdpi/modern_toolbar_shadow.png b/chrome/android/java/res/drawable-xxhdpi/modern_toolbar_shadow.png new file mode 100644 index 0000000..2206ddd8 --- /dev/null +++ b/chrome/android/java/res/drawable-xxhdpi/modern_toolbar_shadow.png Binary files differ
diff --git a/chrome/android/java/res/drawable-xxxhdpi/contextual_search_bar_shadow.png b/chrome/android/java/res/drawable-xxxhdpi/contextual_search_bar_shadow.png deleted file mode 100644 index 135c09db..0000000 --- a/chrome/android/java/res/drawable-xxxhdpi/contextual_search_bar_shadow.png +++ /dev/null Binary files differ
diff --git a/chrome/android/java/res/layout/bottom_control_container.xml b/chrome/android/java/res/layout/bottom_control_container.xml index 278b660..3c692e7f 100644 --- a/chrome/android/java/res/layout/bottom_control_container.xml +++ b/chrome/android/java/res/layout/bottom_control_container.xml
@@ -33,7 +33,7 @@ android:id="@+id/bottom_toolbar_shadow" android:layout_width="match_parent" android:layout_height="@dimen/toolbar_shadow_height" - android:src="@drawable/toolbar_shadow" + android:src="@drawable/modern_toolbar_shadow" android:scaleType="fitXY" android:scaleY="-1" android:contentDescription="@null" />
diff --git a/chrome/android/java/res/layout/download_manager_ui_space_widget.xml b/chrome/android/java/res/layout/download_manager_ui_space_widget.xml index 22a11a3..c1fa0467 100644 --- a/chrome/android/java/res/layout/download_manager_ui_space_widget.xml +++ b/chrome/android/java/res/layout/download_manager_ui_space_widget.xml
@@ -25,36 +25,25 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:singleLine="true" - android:textAppearance="@style/RobotoMediumStyle" - android:textColor="@color/google_blue_500" - android:textSize="16sp" /> + android:textAppearance="@style/BlueLink2" /> <!-- The progress bar uses 20dp of space, vertically, including spacing. --> <org.chromium.chrome.browser.widget.MaterialProgressBar android:id="@+id/space_bar" android:layout_width="match_parent" - android:layout_height="4dp" + android:layout_height="2dp" android:layout_marginTop="8dp" android:layout_marginBottom="8dp" - chrome:colorBackground="@color/black_alpha_38" - chrome:colorProgress="@color/google_blue_500" - chrome:colorSecondaryProgress="@color/black_alpha_87" /> + chrome:colorBackground="@color/google_grey_500" + chrome:colorProgress="@color/google_grey_300" + chrome:colorSecondaryProgress="@color/google_blue_700" /> <TextView - android:id="@+id/size_other_apps" + android:id="@+id/size_free_and_other_apps" android:layout_width="match_parent" android:layout_height="wrap_content" android:singleLine="true" - android:textColor="@color/black_alpha_87" - android:textSize="14sp" /> - - <TextView - android:id="@+id/size_free" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:singleLine="true" - android:textColor="@color/black_alpha_54" - android:textSize="14sp" /> + android:textAppearance="@style/BlackDisabledText2" /> </LinearLayout>
diff --git a/chrome/android/java/res/layout/history_privacy_disclaimer_header.xml b/chrome/android/java/res/layout/history_privacy_disclaimer_header.xml index f19f8d1..99d5d1c2 100644 --- a/chrome/android/java/res/layout/history_privacy_disclaimer_header.xml +++ b/chrome/android/java/res/layout/history_privacy_disclaimer_header.xml
@@ -10,16 +10,14 @@ android:paddingBottom="16dp" > <org.chromium.ui.widget.TextViewWithClickableSpans - android:id="@+id/signed_in_not_synced" - style="@style/PrivacyDisclaimerText" /> - - <org.chromium.ui.widget.TextViewWithClickableSpans - android:id="@+id/signed_in_synced" - style="@style/PrivacyDisclaimerText" /> - - <org.chromium.ui.widget.TextViewWithClickableSpans - android:id="@+id/other_forms_of_browsing_history" - style="@style/PrivacyDisclaimerText" /> + android:id="@+id/privacy_disclaimer" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginTop="6dp" + android:layout_marginStart="@dimen/list_item_default_margin" + android:layout_marginEnd="@dimen/list_item_default_margin" + android:textAppearance="@style/BlackBody" + android:lineSpacingExtra="6sp"/> <LinearLayout android:id="@+id/privacy_disclaimer_bottom_space"
diff --git a/chrome/android/java/res/values-v17/styles.xml b/chrome/android/java/res/values-v17/styles.xml index 54efa22..c6fe783 100644 --- a/chrome/android/java/res/values-v17/styles.xml +++ b/chrome/android/java/res/values-v17/styles.xml
@@ -633,18 +633,6 @@ <item name="android:scaleType">center</item> </style> - <!-- History UI --> - <style name="PrivacyDisclaimerText"> - <item name="android:layout_width">match_parent</item> - <item name="android:layout_height">wrap_content</item> - <item name="android:layout_marginTop">6dp</item> - <item name="android:layout_marginStart">@dimen/list_item_default_margin</item> - <item name="android:layout_marginEnd">@dimen/list_item_default_margin</item> - <item name="android:lineSpacingExtra">6sp</item> - <item name="android:textSize">14sp</item> - <item name="android:visibility">gone</item> - </style> - <!-- Download Home --> <style name="DownloadHomeStatusText"> <item name="android:layout_width">wrap_content</item>
diff --git a/chrome/android/java/res/values/dimens.xml b/chrome/android/java/res/values/dimens.xml index 960694fe..a82fcd16 100644 --- a/chrome/android/java/res/values/dimens.xml +++ b/chrome/android/java/res/values/dimens.xml
@@ -240,7 +240,7 @@ <dimen name="toolbar_progress_bar_height">2dp</dimen> <dimen name="toolbar_button_width">48dp</dimen> <dimen name="toolbar_handle_height">3dp</dimen> - <dimen name="toolbar_handle_width">24dp</dimen> + <dimen name="toolbar_handle_width">20dp</dimen> <dimen name="toolbar_handle_corner_radius">1.5dp</dimen> <dimen name="toolbar_handle_margin_top">12dp</dimen>
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/bottombar/contextualsearch/ContextualSearchPanel.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/bottombar/contextualsearch/ContextualSearchPanel.java index 4554684d..8402736 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/bottombar/contextualsearch/ContextualSearchPanel.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/bottombar/contextualsearch/ContextualSearchPanel.java
@@ -88,10 +88,13 @@ mSceneLayer = createNewContextualSearchSceneLayer(); mPanelMetrics = new ContextualSearchPanelMetrics(); - mBarShadowHeightPx = ApiCompatibilityUtils.getDrawable(mContext.getResources(), - R.drawable.contextual_search_bar_shadow).getIntrinsicHeight(); - mEndButtonWidthDp = mPxToDp * (float) mContext.getResources().getDimensionPixelSize( - R.dimen.contextual_search_end_button_width); + mBarShadowHeightPx = + ApiCompatibilityUtils + .getDrawable(mContext.getResources(), R.drawable.modern_toolbar_shadow) + .getIntrinsicHeight(); + mEndButtonWidthDp = mPxToDp + * mContext.getResources().getDimensionPixelSize( + R.dimen.contextual_search_end_button_width); } @Override
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/scene_layer/ContextualSearchSceneLayer.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/scene_layer/ContextualSearchSceneLayer.java index cb26d3b..7cdc1d4 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/scene_layer/ContextualSearchSceneLayer.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/scene_layer/ContextualSearchSceneLayer.java
@@ -132,7 +132,7 @@ nativeUpdateContextualSearchLayer(mNativePtr, R.drawable.contextual_search_bar_background, searchContextViewId, searchTermViewId, searchCaptionViewId, - R.drawable.contextual_search_bar_shadow, R.drawable.googleg, quickActionIconResId, + R.drawable.modern_toolbar_shadow, R.drawable.googleg, quickActionIconResId, R.drawable.breadcrumb_arrow, ContextualSearchPanel.CLOSE_ICON_DRAWABLE_ID, R.drawable.progress_bar_background, R.drawable.progress_bar_foreground, searchPromoViewId, R.drawable.contextual_search_promo_ripple,
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadForegroundService.java b/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadForegroundService.java index 21698f9..da6389e 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadForegroundService.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadForegroundService.java
@@ -34,18 +34,14 @@ * @param notification The new notification to be pinned to. */ public void startOrUpdateForegroundService(int notificationId, Notification notification) { - // TODO(jming): Make sure there is not weird UI in switching the pinned notification. startForeground(notificationId, notification); } /** * Stop the foreground service that is running. - * @param isComplete If the download has been complete and, therefore, if its notification - * should be killed. */ - public void stopDownloadForegroundService(boolean isComplete) { - // TODO(jming): Check to make sure the notification does not get killed. - stopForeground(isComplete /* killNotification */); + public void stopDownloadForegroundService(boolean isCancelled) { + stopForeground(isCancelled /* kill notification if cancelled */); } @Override
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadForegroundServiceManager.java b/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadForegroundServiceManager.java index 68ca516..083ee31c 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadForegroundServiceManager.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadForegroundServiceManager.java
@@ -101,7 +101,7 @@ // Stop the foreground service. // In the pending case, this will stop the foreground immediately after it was started. if (!isActive(downloadUpdate.mDownloadStatus)) { - stopAndUnbindService(downloadUpdate.mDownloadStatus == DownloadStatus.COMPLETE); + stopAndUnbindService(downloadUpdate.mDownloadStatus == DownloadStatus.CANCEL); cleanDownloadUpdateQueue(); return; } @@ -159,9 +159,8 @@ @VisibleForTesting void startAndBindServiceInternal(Context context) { DownloadForegroundService.startDownloadForegroundService(context); - ContextUtils.getApplicationContext().bindService( - new Intent(ContextUtils.getApplicationContext(), DownloadForegroundService.class), - mConnection, Context.BIND_AUTO_CREATE); + context.bindService(new Intent(context, DownloadForegroundService.class), mConnection, + Context.BIND_AUTO_CREATE); } private final ServiceConnection mConnection = new ServiceConnection() { @@ -197,17 +196,17 @@ /** Helper code to stop and unbind service. */ @VisibleForTesting - void stopAndUnbindService(boolean isComplete) { + void stopAndUnbindService(boolean isCancelled) { mIsServiceBound = false; if (mBoundService != null) { - stopAndUnbindServiceInternal(isComplete); + stopAndUnbindServiceInternal(isCancelled); mBoundService = null; } } @VisibleForTesting - void stopAndUnbindServiceInternal(boolean isComplete) { - mBoundService.stopDownloadForegroundService(isComplete); + void stopAndUnbindServiceInternal(boolean isCancelled) { + mBoundService.stopDownloadForegroundService(isCancelled); ContextUtils.getApplicationContext().unbindService(mConnection); }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadNotificationFactory.java b/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadNotificationFactory.java index 652abcd1..7301014 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadNotificationFactory.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadNotificationFactory.java
@@ -17,10 +17,8 @@ import static org.chromium.chrome.browser.download.DownloadNotificationService.EXTRA_IS_OFF_THE_RECORD; import static org.chromium.chrome.browser.download.DownloadNotificationService.EXTRA_IS_SUPPORTED_MIME_TYPE; import static org.chromium.chrome.browser.download.DownloadNotificationService.EXTRA_NOTIFICATION_BUNDLE_ICON_ID; -import static org.chromium.chrome.browser.download.DownloadNotificationService.NOTIFICATION_NAMESPACE; import android.app.Notification; -import android.app.NotificationManager; import android.app.PendingIntent; import android.content.ComponentName; import android.content.Context; @@ -56,27 +54,6 @@ } /** - * Call from the DownloadNotificationStore when downloads are updated (added, changed, deleted) - * to trigger the creation, display, or removal of a corresponding notification. - * NOTE: This is currently not being used because DownloadNotificationStore does not yet exist. - * @param context of the application. - * @param downloadStatus (in progress, paused, successful, failed, deleted, or summary). - * @param downloadUpdate information about the download (ie. contentId, fileName, icon, etc). - */ - public static void updateDownloadStatus( - Context context, DownloadStatus downloadStatus, DownloadUpdate downloadUpdate) { - NotificationManager notificationManager = - (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE); - if (downloadStatus == DownloadStatus.DELETED) { - notificationManager.cancel(NOTIFICATION_NAMESPACE, downloadUpdate.getNotificationId()); - } else { - Notification notification = buildNotification(context, downloadStatus, downloadUpdate); - notificationManager.notify( - NOTIFICATION_NAMESPACE, downloadUpdate.getNotificationId(), notification); - } - } - - /** * Builds a downloads notification based on the status of the download and its information. * @param context of the download. * @param downloadStatus (in progress, paused, successful, failed, deleted, or summary).
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadNotificationService.java b/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadNotificationService.java index 4e9b213..f33a964 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadNotificationService.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/download/DownloadNotificationService.java
@@ -56,8 +56,6 @@ public static final String ACTION_DOWNLOAD_OPEN = "org.chromium.chrome.browser.download.DOWNLOAD_OPEN"; - public static final String NOTIFICATION_NAMESPACE = "DownloadNotificationService"; - public static final String EXTRA_NOTIFICATION_BUNDLE_ICON_ID = "Chrome.NotificationBundleIconIdExtra"; /** Notification Id starting value, to avoid conflicts from IDs used in prior versions. */ @@ -200,9 +198,8 @@ long timeRemainingInMillis, long startTime, boolean isOffTheRecord, boolean canDownloadWhileMetered, boolean isDownloadPending, boolean isTransient, Bitmap icon, boolean hasUserGesture) { - int notificationId = getNotificationId(id); Context context = ContextUtils.getApplicationContext(); - + int notificationId = getNotificationId(id); DownloadUpdate downloadUpdate = new DownloadUpdate.Builder() .setContentId(id) .setFileName(fileName) @@ -236,7 +233,8 @@ } public void cancelNotification(int notificationId) { - mNotificationManager.cancel(NOTIFICATION_NAMESPACE, notificationId); + // TODO(b/65052774): Add back NOTIFICATION_NAMESPACE when able to. + mNotificationManager.cancel(notificationId); } /** @@ -306,9 +304,9 @@ stopTrackingInProgressDownload(id); return; } - int notificationId = entry == null ? getNotificationId(id) : entry.notificationId; - Context context = ContextUtils.getApplicationContext(); + Context context = ContextUtils.getApplicationContext(); + int notificationId = entry == null ? getNotificationId(id) : entry.notificationId; DownloadUpdate downloadUpdate = new DownloadUpdate.Builder() .setContentId(id) .setFileName(fileName) @@ -317,10 +315,8 @@ .setIcon(icon) .setNotificationId(notificationId) .build(); - Notification notification = DownloadNotificationFactory.buildNotification( context, DownloadNotificationFactory.DownloadStatus.PAUSED, downloadUpdate); - updateNotification(notificationId, notification, id, new DownloadSharedPreferenceEntry(id, notificationId, isOffTheRecord, canDownloadWhileMetered, fileName, isAutoResumable, isTransient)); @@ -406,9 +402,8 @@ fileName = entry.fileName; } - int notificationId = getNotificationId(id); Context context = ContextUtils.getApplicationContext(); - + int notificationId = getNotificationId(id); DownloadUpdate downloadUpdate = new DownloadUpdate.Builder() .setContentId(id) .setFileName(fileName) @@ -452,7 +447,8 @@ @VisibleForTesting void updateNotification(int id, Notification notification) { - mNotificationManager.notify(NOTIFICATION_NAMESPACE, id, notification); + // TODO(b/65052774): Add back NOTIFICATION_NAMESPACE when able to. + mNotificationManager.notify(id, notification); } private void updateNotification(int notificationId, Notification notification, ContentId id, @@ -491,11 +487,10 @@ * already in progress, do nothing. */ void resumeAllPendingDownloads() { - Context context = ContextUtils.getApplicationContext(); List<DownloadSharedPreferenceEntry> entries = mDownloadSharedPreferenceHelper.getEntries(); for (int i = 0; i < entries.size(); ++i) { DownloadSharedPreferenceEntry entry = entries.get(i); - if (!canResumeDownload(context, entry)) continue; + if (!canResumeDownload(ContextUtils.getApplicationContext(), entry)) continue; if (mDownloadsInProgress.contains(entry.id)) continue; Intent intent = new Intent();
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/ui/SpaceDisplay.java b/chrome/android/java/src/org/chromium/chrome/browser/download/ui/SpaceDisplay.java index 92557ca..f8f43692 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/download/ui/SpaceDisplay.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/download/ui/SpaceDisplay.java
@@ -9,6 +9,9 @@ import android.os.Environment; import android.os.StatFs; import android.support.v7.widget.RecyclerView; +import android.text.Spannable; +import android.text.SpannableString; +import android.text.style.ForegroundColorSpan; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; @@ -109,8 +112,7 @@ private View mView; private View mViewContainer; private TextView mSpaceUsedByDownloadsTextView; - private TextView mSpaceUsedByOtherAppsTextView; - private TextView mSpaceFreeTextView; + private TextView mSpaceFreeAndOtherAppsTextView; private MaterialProgressBar mSpaceBar; private long mFreeBytes; @@ -120,8 +122,8 @@ .inflate(R.layout.download_manager_ui_space_widget, parent, false); mView = mViewContainer.findViewById(R.id.space_widget_content); mSpaceUsedByDownloadsTextView = (TextView) mView.findViewById(R.id.size_downloaded); - mSpaceUsedByOtherAppsTextView = (TextView) mView.findViewById(R.id.size_other_apps); - mSpaceFreeTextView = (TextView) mView.findViewById(R.id.size_free); + mSpaceFreeAndOtherAppsTextView = + (TextView) mView.findViewById(R.id.size_free_and_other_apps); mSpaceBar = (MaterialProgressBar) mView.findViewById(R.id.space_bar); mFileSystemBytesTask = new StorageSizeTask(true).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); @@ -190,22 +192,28 @@ Context context = mSpaceUsedByDownloadsTextView.getContext(); mSpaceUsedByDownloadsTextView.setText( DownloadUtils.getStringForBytes(context, USED_STRINGS, bytesUsedByDownloads)); - mSpaceUsedByOtherAppsTextView.setText( - DownloadUtils.getStringForBytes(context, OTHER_STRINGS, bytesUsedByOtherApps)); - mSpaceFreeTextView.setText( - DownloadUtils.getStringForBytes(context, FREE_STRINGS, mFreeBytes)); + + String spaceFree = DownloadUtils.getStringForBytes(context, FREE_STRINGS, mFreeBytes); + String spaceUsedByOtherApps = + DownloadUtils.getStringForBytes(context, OTHER_STRINGS, bytesUsedByOtherApps); + SpannableString spannable = new SpannableString( + context.getResources().getString(R.string.download_manager_ui_space_free_and_other, + spaceFree, spaceUsedByOtherApps)); + ForegroundColorSpan colorSpan = new ForegroundColorSpan( + ApiCompatibilityUtils.getColor(context.getResources(), R.color.black_alpha_54)); + spannable.setSpan(colorSpan, 0, spaceFree.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); + mSpaceFreeAndOtherAppsTextView.setText(spannable); // Set a minimum size for the download size so that it shows up in the progress bar. long onePercentOfSystem = fileSystemBytes == 0 ? 0 : fileSystemBytes / 100; long fudgedBytesUsedByDownloads = Math.max(bytesUsedByDownloads, onePercentOfSystem); - long fudgedbytesUsedByOtherApps = Math.max(0, bytesUsedTotal - fudgedBytesUsedByDownloads); // Indicate how much space has been used as a progress bar. The percentage used by // downloads is shown by the non-overlapped area of the primary and secondary progressbar. int percentageUsedTotal = computePercentage(bytesUsedTotal, fileSystemBytes); - int percentageOtherApps = computePercentage(fudgedbytesUsedByOtherApps, fileSystemBytes); + int percentageDownloaded = computePercentage(fudgedBytesUsedByDownloads, fileSystemBytes); mSpaceBar.setProgress(percentageUsedTotal); - mSpaceBar.setSecondaryProgress(percentageOtherApps); + mSpaceBar.setSecondaryProgress(percentageDownloaded); for (Observer observer : mObservers) observer.onSpaceDisplayUpdated(this); }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/history/HistoryAdapter.java b/chrome/android/java/src/org/chromium/chrome/browser/history/HistoryAdapter.java index 2122fb1..6ee49bf 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/history/HistoryAdapter.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/history/HistoryAdapter.java
@@ -8,6 +8,7 @@ import android.support.v7.widget.RecyclerView; import android.support.v7.widget.RecyclerView.ViewHolder; import android.text.SpannableString; +import android.text.SpannableStringBuilder; import android.text.TextPaint; import android.text.method.LinkMovementMethod; import android.view.LayoutInflater; @@ -18,6 +19,7 @@ import android.widget.TextView; import org.chromium.base.ApiCompatibilityUtils; +import org.chromium.base.ContextUtils; import org.chromium.base.VisibleForTesting; import org.chromium.chrome.R; import org.chromium.chrome.browser.ChromeFeatureList; @@ -51,10 +53,11 @@ private final HistoryManager mHistoryManager; private final ArrayList<HistoryItemView> mItemViews; private RecyclerView mRecyclerView; + private final SpannableString mSignedInNotSyncedText; + private final SpannableString mSignedInSyncedText; + private final SpannableString mOtherFormsOfBrowsingHistoryText; - private TextView mSignedInNotSyncedTextView; - private TextView mSignedInSyncedTextView; - private TextView mOtherFormsOfBrowsingHistoryTextView; + private TextView mPrivacyDisclaimerTextView; private View mPrivacyDisclaimerBottomSpace; private Button mClearBrowsingDataButton; private HeaderItem mPrivacyDisclaimerHeaderItem; @@ -72,7 +75,6 @@ private boolean mClearBrowsingDataButtonVisible; private long mNextQueryEndTime; private String mQueryText = EMPTY_QUERY; - private int mDefaultTextMargin; public HistoryAdapter(SelectionDelegate<HistoryItem> delegate, HistoryManager manager, HistoryProvider provider) { @@ -82,6 +84,20 @@ mHistoryProvider.setObserver(this); mHistoryManager = manager; mItemViews = new ArrayList<>(); + + mSignedInNotSyncedText = initializePrivacyDisclaimerText( + R.string.android_history_no_synced_results, LEARN_MORE_LINK); + mSignedInSyncedText = initializePrivacyDisclaimerText( + R.string.android_history_has_synced_results, LEARN_MORE_LINK); + // For some test, the native library is not loaded, so ChromeFeatureList#isInitialized is + // checked to prevent crashing. + boolean flagEnabled = ChromeFeatureList.isInitialized() + && ChromeFeatureList.isEnabled(ChromeFeatureList.TABS_IN_CBD); + int disclaimerTextId = flagEnabled ? R.string.android_history_other_forms_of_history_new + : R.string.android_history_other_forms_of_history; + String disclaimerUrl = flagEnabled ? MY_ACTIVITY_LINK : GOOGLE_HISTORY_LINK; + mOtherFormsOfBrowsingHistoryText = + initializePrivacyDisclaimerText(disclaimerTextId, disclaimerUrl); } /** @@ -191,7 +207,7 @@ } initialize(); updateClearBrowsingDataButtonVisibility(); - setPrivacyDisclaimerVisibility(); + setPrivacyDisclaimer(); } /** @@ -273,7 +289,7 @@ public void hasOtherFormsOfBrowsingData(boolean hasOtherForms, boolean hasSyncedResults) { mHasOtherFormsOfBrowsingData = hasOtherForms; mHasSyncedData = hasSyncedResults; - setPrivacyDisclaimerVisibility(); + setPrivacyDisclaimer(); } @Override @@ -288,30 +304,15 @@ * items for them. */ void generateHeaderItems() { - ViewGroup privacyDisclaimers = + ViewGroup privacyDisclaimerContainer = (ViewGroup) View.inflate(mHistoryManager.getSelectableListLayout().getContext(), R.layout.history_privacy_disclaimer_header, null); - mSignedInNotSyncedTextView = - (TextView) privacyDisclaimers.findViewById(R.id.signed_in_not_synced); - setPrivacyDisclaimerText(mSignedInNotSyncedTextView, - R.string.android_history_no_synced_results, LEARN_MORE_LINK); - - mSignedInSyncedTextView = (TextView) privacyDisclaimers.findViewById(R.id.signed_in_synced); - setPrivacyDisclaimerText(mSignedInSyncedTextView, - R.string.android_history_has_synced_results, LEARN_MORE_LINK); - - mOtherFormsOfBrowsingHistoryTextView = - (TextView) privacyDisclaimers.findViewById(R.id.other_forms_of_browsing_history); - boolean flagEnabled = ChromeFeatureList.isEnabled(ChromeFeatureList.TABS_IN_CBD); - int disclaimerTextId = flagEnabled ? R.string.android_history_other_forms_of_history_new - : R.string.android_history_other_forms_of_history; - String disclaimerUrl = flagEnabled ? MY_ACTIVITY_LINK : GOOGLE_HISTORY_LINK; - setPrivacyDisclaimerText( - mOtherFormsOfBrowsingHistoryTextView, disclaimerTextId, disclaimerUrl); - + mPrivacyDisclaimerTextView = + privacyDisclaimerContainer.findViewById(R.id.privacy_disclaimer); + mPrivacyDisclaimerTextView.setMovementMethod(LinkMovementMethod.getInstance()); mPrivacyDisclaimerBottomSpace = - privacyDisclaimers.findViewById(R.id.privacy_disclaimer_bottom_space); + privacyDisclaimerContainer.findViewById(R.id.privacy_disclaimer_bottom_space); ViewGroup clearBrowsingDataButtonContainer = (ViewGroup) View.inflate(mHistoryManager.getSelectableListLayout().getContext(), @@ -326,10 +327,10 @@ } }); - mPrivacyDisclaimerHeaderItem = new HeaderItem(0, privacyDisclaimers); + mPrivacyDisclaimerHeaderItem = new HeaderItem(0, privacyDisclaimerContainer); mClearBrowsingDataButtonHeaderItem = new HeaderItem(1, clearBrowsingDataButtonContainer); updateClearBrowsingDataButtonVisibility(); - setPrivacyDisclaimerVisibility(); + setPrivacyDisclaimer(); } /** @@ -342,7 +343,14 @@ setHeaders(args.toArray(new HeaderItem[args.size()])); } - private void setPrivacyDisclaimerText(final TextView view, int stringId, final String url) { + /** + * Create a {@SpannableString} for privacy disclaimer. + * @param stringId The string resource id. + * @param url The url to open when clicked. + * @return The {@SpannableString} with the specified string resource and url. + */ + private SpannableString initializePrivacyDisclaimerText(int stringId, final String url) { + final Resources resources = ContextUtils.getApplicationContext().getResources(); NoUnderlineClickableSpan link = new NoUnderlineClickableSpan() { @Override public void onClick(View view) { @@ -352,15 +360,12 @@ @Override public void updateDrawState(TextPaint textPaint) { super.updateDrawState(textPaint); - textPaint.setColor(ApiCompatibilityUtils.getColor( - view.getResources(), R.color.google_blue_700)); + textPaint.setColor( + ApiCompatibilityUtils.getColor(resources, R.color.google_blue_700)); } }; - SpannableString spannable = SpanApplier.applySpans( - view.getResources().getString(stringId), - new SpanApplier.SpanInfo("<link>", "</link>", link)); - view.setText(spannable); - view.setMovementMethod(LinkMovementMethod.getInstance()); + return SpanApplier.applySpans( + resources.getString(stringId), new SpanApplier.SpanInfo("<link>", "</link>", link)); } /** @@ -373,17 +378,24 @@ } /** - * Set visibility for privacy disclaimer layout and views. + * Set text of privacy disclaimer and visibility of its container. */ - void setPrivacyDisclaimerVisibility() { + void setPrivacyDisclaimer() { boolean isSignedIn = ChromeSigninController.get().isSignedIn(); boolean shouldShowPrivacyDisclaimers = hasPrivacyDisclaimers() && mHistoryManager.shouldShowInfoHeaderIfAvailable(); - mSignedInNotSyncedTextView.setVisibility( - !mHasSyncedData && isSignedIn ? View.VISIBLE : View.GONE); - mSignedInSyncedTextView.setVisibility(mHasSyncedData ? View.VISIBLE : View.GONE); - mOtherFormsOfBrowsingHistoryTextView.setVisibility( - mHasOtherFormsOfBrowsingData ? View.VISIBLE : View.GONE); + + SpannableStringBuilder builder = new SpannableStringBuilder(); + if (!mHasSyncedData && isSignedIn) builder.append(mSignedInNotSyncedText); + if (mHasSyncedData) builder.append(mSignedInSyncedText); + if (mHasOtherFormsOfBrowsingData) { + // If mHasOtherFormsOfBrowsingData is true, one of the other conditions must have + // already been met so a space is needed to separate the sentences. + builder.append(" "); + builder.append(mOtherFormsOfBrowsingHistoryText); + } + mPrivacyDisclaimerTextView.setText(builder); + // Prevent from refreshing the recycler view if header visibility is not changed. if (mPrivacyDisclaimersVisible == shouldShowPrivacyDisclaimers) return; mPrivacyDisclaimersVisible = shouldShowPrivacyDisclaimers; @@ -401,26 +413,24 @@ if (mIsInitialized) setHeaders(); } - private int getDefaultTextMargin(Resources resources) { - if (mDefaultTextMargin == 0) { - mDefaultTextMargin = resources.getDimensionPixelSize(R.dimen.list_item_default_margin); - } - return mDefaultTextMargin; + @VisibleForTesting + String getSignedInNotSyncedTextForTests() { + return mSignedInNotSyncedText.toString(); } @VisibleForTesting - TextView getSignedInNotSyncedViewForTests() { - return mSignedInNotSyncedTextView; + String getSignedInSyncedTextForTests() { + return mSignedInSyncedText.toString(); } @VisibleForTesting - TextView getSignedInSyncedViewForTests() { - return mSignedInSyncedTextView; + String getOtherFormsOfBrowsingHistoryTextForTests() { + return mOtherFormsOfBrowsingHistoryText.toString(); } @VisibleForTesting - TextView getOtherFormsOfBrowsingHistoryViewForTests() { - return mOtherFormsOfBrowsingHistoryTextView; + String getPrivacyDisclaimerTextForTests() { + return mPrivacyDisclaimerTextView.getText().toString(); } @VisibleForTesting
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/history/HistoryManager.java b/chrome/android/java/src/org/chromium/chrome/browser/history/HistoryManager.java index 30a8fb4b..dd76f09 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/history/HistoryManager.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/history/HistoryManager.java
@@ -223,7 +223,7 @@ .putBoolean(PREF_SHOW_HISTORY_INFO, mShouldShowInfoHeader) .apply(); mToolbar.updateInfoMenuItem(shouldShowInfoButton(), shouldShowInfoHeaderIfAvailable()); - mHistoryAdapter.setPrivacyDisclaimerVisibility(); + mHistoryAdapter.setPrivacyDisclaimer(); } return false; }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/offlinepages/OfflinePageBridge.java b/chrome/android/java/src/org/chromium/chrome/browser/offlinepages/OfflinePageBridge.java index 583eb72..742efa0 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/offlinepages/OfflinePageBridge.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/offlinepages/OfflinePageBridge.java
@@ -13,6 +13,7 @@ import org.chromium.base.annotations.JNINamespace; import org.chromium.chrome.browser.ChromeActivity; import org.chromium.chrome.browser.profiles.Profile; +import org.chromium.chrome.browser.tab.Tab; import org.chromium.components.offlinepages.DeletePageResult; import org.chromium.content_public.browser.WebContents; @@ -147,6 +148,15 @@ } /** + * @Return the string representing the origin of the tab. + */ + @CalledByNative + private static String getEncodedOriginApp(Tab tab) { + return new OfflinePageOrigin(ContextUtils.getApplicationContext(), tab) + .encodeAsJsonString(); + } + + /** * Adds an observer to offline page model changes. * @param observer The observer to be added. */
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omaha/UpdateMenuItemHelper.java b/chrome/android/java/src/org/chromium/chrome/browser/omaha/UpdateMenuItemHelper.java index 7f7cb95..2a6b690 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/omaha/UpdateMenuItemHelper.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/omaha/UpdateMenuItemHelper.java
@@ -49,6 +49,7 @@ private static final String FIELD_TRIAL_NAME = "UpdateMenuItem"; private static final String ENABLED_VALUE = "true"; private static final String CUSTOM_SUMMARY = "custom_summary"; + private static final String MIN_REQUIRED_STORAGE_MB = "min_required_storage_for_update_mb"; // UMA constants for logging whether the menu item was clicked. private static final int ITEM_NOT_CLICKED = 0; @@ -117,8 +118,7 @@ mUpdateUrl = MarketURLGetter.getMarketUrl(activity); mLatestVersion = VersionNumberGetter.getInstance().getLatestKnownVersion(activity); - mUpdateAvailable = true; - recordInternalStorageSize(); + mUpdateAvailable = checkForSufficientStorage(); } else { mUpdateAvailable = false; } @@ -389,7 +389,28 @@ return value; } - private void recordInternalStorageSize() { + /** + * Returns an integer value for a Finch parameter, or the default value if no parameter exists + * in the current configuration. Also checks for a command-line switch with the same name. + * @param paramName The name of the Finch parameter (or command-line switch) to get a value for. + * @param defaultValue The default value to return when there's no param or switch. + * @return An integer value -- either the param or the default. + */ + private static int getIntParamValueOrDefault(String paramName, int defaultValue) { + String value = CommandLine.getInstance().getSwitchValue(paramName); + if (TextUtils.isEmpty(value)) { + value = VariationsAssociatedData.getVariationParamValue(FIELD_TRIAL_NAME, paramName); + } + if (TextUtils.isEmpty(value)) return defaultValue; + + try { + return Integer.parseInt(value); + } catch (NumberFormatException e) { + return defaultValue; + } + } + + private boolean checkForSufficientStorage() { assert !ThreadUtils.runningOnUiThread(); File path = Environment.getDataDirectory(); @@ -402,6 +423,13 @@ } RecordHistogram.recordLinearCountHistogram( "GoogleUpdate.InfoBar.InternalStorageSizeAvailable", (int) size, 1, 200, 100); + RecordHistogram.recordLinearCountHistogram( + "GoogleUpdate.InfoBar.DeviceFreeSpace", (int) size, 1, 1000, 50); + + int minRequiredStorage = getIntParamValueOrDefault(MIN_REQUIRED_STORAGE_MB, -1); + if (minRequiredStorage == -1) return true; + + return size >= minRequiredStorage; } @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR2)
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/BottomToolbarPhone.java b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/BottomToolbarPhone.java index bb307732..07d2dee9 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/BottomToolbarPhone.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/BottomToolbarPhone.java
@@ -660,6 +660,8 @@ updateToolbarBackground(mVisualState); updateVisualsForToolbarState(); + mBottomToolbarTopShadow.setImageResource(R.drawable.toolbar_shadow); + invalidate(); requestLayout(); }
diff --git a/chrome/android/java/strings/android_chrome_strings.grd b/chrome/android/java/strings/android_chrome_strings.grd index 8f5c5a1..bf5597d 100644 --- a/chrome/android/java/strings/android_chrome_strings.grd +++ b/chrome/android/java/strings/android_chrome_strings.grd
@@ -2096,6 +2096,9 @@ <message name="IDS_OPEN_DOWNLOADED_LABEL" desc="A text label on the snackbar widget to open the downloaded file."> Open </message> + <message name="IDS_DOWNLOAD_MANAGER_UI_SPACE_FREE_AND_OTHER" desc="Formatted string indicating how much storage is available to use on this device and how much storage space on this device has been used by other apps."> + <ph name="SPACE_FREE">%1$s<ex>4.8 GB available</ex></ph> (<ph name="SPACE_OTHER">%2$s<ex>5.22 GB other apps</ex></ph>) + </message> <message name="IDS_DOWNLOAD_MANAGER_UI_SPACE_FREE_KB" desc="String indicating how much storage is available to use on the device, in kilobytes."> <ph name="kilobytes">%1$3.2f<ex>12.5</ex></ph> KB available </message>
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/download/DownloadForegroundServiceManagerTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/download/DownloadForegroundServiceManagerTest.java index dd411fc8..647781a 100644 --- a/chrome/android/javatests/src/org/chromium/chrome/browser/download/DownloadForegroundServiceManagerTest.java +++ b/chrome/android/javatests/src/org/chromium/chrome/browser/download/DownloadForegroundServiceManagerTest.java
@@ -65,14 +65,14 @@ void startAndBindServiceInternal(Context context) {} @Override - void stopAndUnbindService(boolean isComplete) { - mIsNotificationKilled = isComplete; + void stopAndUnbindService(boolean isCancelled) { + mIsNotificationKilled = isCancelled; mIsServiceBound = false; - super.stopAndUnbindService(isComplete); + super.stopAndUnbindService(isCancelled); } @Override - void stopAndUnbindServiceInternal(boolean isComplete) {} + void stopAndUnbindServiceInternal(boolean isCancelled) {} @Override void startOrUpdateForegroundService(int notificationId, Notification notification) { @@ -210,14 +210,14 @@ assertFalse(mDownloadServiceManager.mIsServiceBound); assertFalse(mDownloadServiceManager.mIsNotificationKilled); - // Service restarts and then completes, so notification is killed. + // Service restarts and then is cancelled, so notification is killed. mDownloadServiceManager.updateDownloadStatus( mContext, DownloadStatus.IN_PROGRESS, FAKE_DOWNLOAD_1, mNotification); assertTrue(mDownloadServiceManager.mIsServiceBound); mDownloadServiceManager.onServiceConnected(); mDownloadServiceManager.updateDownloadStatus( - mContext, DownloadStatus.COMPLETE, FAKE_DOWNLOAD_1, mNotification); + mContext, DownloadStatus.CANCEL, FAKE_DOWNLOAD_1, mNotification); assertFalse(mDownloadServiceManager.mIsServiceBound); assertTrue(mDownloadServiceManager.mIsNotificationKilled); }
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/history/HistoryActivityTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/history/HistoryActivityTest.java index 34af9361..73cc61e 100644 --- a/chrome/android/javatests/src/org/chromium/chrome/browser/history/HistoryActivityTest.java +++ b/chrome/android/javatests/src/org/chromium/chrome/browser/history/HistoryActivityTest.java
@@ -29,6 +29,7 @@ import org.chromium.base.test.util.Restriction; import org.chromium.base.test.util.RetryOnFailure; import org.chromium.chrome.R; +import org.chromium.chrome.browser.ChromeFeatureList; import org.chromium.chrome.browser.IntentHandler; import org.chromium.chrome.browser.preferences.PrefServiceBridge; import org.chromium.chrome.browser.profiles.Profile; @@ -40,6 +41,7 @@ import org.chromium.chrome.browser.widget.selection.SelectableItemViewHolder; import org.chromium.chrome.browser.widget.selection.SelectionDelegate.SelectionObserver; import org.chromium.chrome.test.util.ChromeRestriction; +import org.chromium.chrome.test.util.browser.Features; import org.chromium.chrome.test.util.browser.signin.SigninTestUtil; import org.chromium.chrome.test.util.browser.sync.SyncTestUtil; import org.chromium.components.signin.ChromeSigninController; @@ -225,11 +227,7 @@ public void testPrivacyDisclaimers_SignedOut() { ChromeSigninController signinController = ChromeSigninController.get(); signinController.setSignedInAccountName(null); - - assertEquals(View.GONE, mAdapter.getSignedInNotSyncedViewForTests().getVisibility()); - assertEquals(View.GONE, mAdapter.getSignedInSyncedViewForTests().getVisibility()); - assertEquals(View.GONE, - mAdapter.getOtherFormsOfBrowsingHistoryViewForTests().getVisibility()); + assertTrue(mAdapter.getPrivacyDisclaimerTextForTests().isEmpty()); } @SmallTest @@ -239,10 +237,8 @@ setHasOtherFormsOfBrowsingData(false, false); - assertEquals(View.VISIBLE, mAdapter.getSignedInNotSyncedViewForTests().getVisibility()); - assertEquals(View.GONE, mAdapter.getSignedInSyncedViewForTests().getVisibility()); - assertEquals(View.GONE, - mAdapter.getOtherFormsOfBrowsingHistoryViewForTests().getVisibility()); + assertEquals(mAdapter.getSignedInNotSyncedTextForTests(), + mAdapter.getPrivacyDisclaimerTextForTests()); signinController.setSignedInAccountName(null); } @@ -254,25 +250,23 @@ setHasOtherFormsOfBrowsingData(false, true); - assertEquals(View.GONE, mAdapter.getSignedInNotSyncedViewForTests().getVisibility()); - assertEquals(View.VISIBLE, mAdapter.getSignedInSyncedViewForTests().getVisibility()); - assertEquals(View.GONE, - mAdapter.getOtherFormsOfBrowsingHistoryViewForTests().getVisibility()); + assertEquals(mAdapter.getSignedInSyncedTextForTests(), + mAdapter.getPrivacyDisclaimerTextForTests()); signinController.setSignedInAccountName(null); } @SmallTest + @Features(@Features.Register(ChromeFeatureList.TABS_IN_CBD)) public void testPrivacyDisclaimers_SignedInSyncedAndOtherForms() { ChromeSigninController signinController = ChromeSigninController.get(); signinController.setSignedInAccountName("test@gmail.com"); setHasOtherFormsOfBrowsingData(true, true); - assertEquals(View.GONE, mAdapter.getSignedInNotSyncedViewForTests().getVisibility()); - assertEquals(View.VISIBLE, mAdapter.getSignedInSyncedViewForTests().getVisibility()); - assertEquals(View.VISIBLE, - mAdapter.getOtherFormsOfBrowsingHistoryViewForTests().getVisibility()); + String expected = String.format("%1$s %2$s", mAdapter.getSignedInSyncedTextForTests(), + mAdapter.getOtherFormsOfBrowsingHistoryTextForTests()); + assertEquals(expected, mAdapter.getPrivacyDisclaimerTextForTests()); signinController.setSignedInAccountName(null); } @@ -568,7 +562,7 @@ @Override public void run() { mAdapter.setClearBrowsingDataButtonVisibilityForTest(false); - mAdapter.setPrivacyDisclaimerVisibility(); + mAdapter.setPrivacyDisclaimer(); } });
diff --git a/chrome/app/chrome_crash_reporter_client_win.cc b/chrome/app/chrome_crash_reporter_client_win.cc index d656a8ad..b2329c1 100644 --- a/chrome/app/chrome_crash_reporter_client_win.cc +++ b/chrome/app/chrome_crash_reporter_client_win.cc
@@ -169,7 +169,6 @@ // TODO(asvitkine): Remove after fixing https://crbug.com/736675 {"bad_histogram", kMediumSize}, - {"from_location", kMediumSize}, }; // This dynamic set of keys is used for sets of key value pairs when gathering
diff --git a/chrome/app/generated_resources.grd b/chrome/app/generated_resources.grd index 4f27643..cfdff8e6 100644 --- a/chrome/app/generated_resources.grd +++ b/chrome/app/generated_resources.grd
@@ -10422,6 +10422,9 @@ <message name="IDS_USER_MANAGER_ADD_PROFILE_PROFILES_LOCKED_ERROR" desc="Error message displayed when trying to add a profile while all profiles are locked."> Please unlock your profile before adding a person. </message> + <message name="IDS_USER_MANAGER_PROMPT_MESSAGE" desc="Prompt message displayed on UserManager when force-sign-in policy is enabled."> + Please unlock your profile with your corporate account. + </message> <message name="IDS_LOGIN_POD_USER_REMOVE_WARNING_NONSYNC" desc="Main text shown as a warning when attempting to remove an user."> This will permanently delete your browsing data from this device. </message>
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn index 8f0c0e3c..34885f2 100644 --- a/chrome/browser/BUILD.gn +++ b/chrome/browser/BUILD.gn
@@ -2219,6 +2219,7 @@ "offline_pages/offline_page_mhtml_archiver.cc", "offline_pages/offline_page_mhtml_archiver.h", "offline_pages/offline_page_model_factory.h", + "offline_pages/offline_page_origin_utils.h", "offline_pages/offline_page_request_interceptor.cc", "offline_pages/offline_page_request_interceptor.h", "offline_pages/offline_page_request_job.cc", @@ -2274,6 +2275,7 @@ "offline_pages/android/offline_page_bridge.cc", "offline_pages/android/offline_page_bridge.h", "offline_pages/android/offline_page_model_factory.cc", + "offline_pages/android/offline_page_origin_utils_android.cc", "offline_pages/android/offline_page_utils_android.cc", "offline_pages/android/prefetch_background_task_android.cc", "offline_pages/android/prefetch_background_task_android.h",
diff --git a/chrome/browser/chromeos/BUILD.gn b/chrome/browser/chromeos/BUILD.gn index b94dc53e..7ebfd648 100644 --- a/chrome/browser/chromeos/BUILD.gn +++ b/chrome/browser/chromeos/BUILD.gn
@@ -1684,6 +1684,7 @@ "arc/arc_session_manager_unittest.cc", "arc/arc_support_host_unittest.cc", "arc/arc_util_unittest.cc", + "arc/boot_phase_monitor/arc_boot_phase_monitor_bridge_unittest.cc", "arc/downloads_watcher/arc_downloads_watcher_service_unittest.cc", "arc/extensions/arc_support_message_host_unittest.cc", "arc/fileapi/arc_content_file_system_async_file_util_unittest.cc",
diff --git a/chrome/browser/chromeos/arc/arc_session_manager.h b/chrome/browser/chromeos/arc/arc_session_manager.h index da3ff410..a1afa7d2 100644 --- a/chrome/browser/chromeos/arc/arc_session_manager.h +++ b/chrome/browser/chromeos/arc/arc_session_manager.h
@@ -254,6 +254,9 @@ // require ToS acceptance. Returns false in other cases, including one when // ARC is not currently running. bool is_directly_started() const { return directly_started_; } + void set_directly_started_for_testing(bool directly_started) { + directly_started_ = directly_started; + } // Injectors for testing. void SetArcSessionRunnerForTesting(
diff --git a/chrome/browser/chromeos/arc/boot_phase_monitor/arc_boot_phase_monitor_bridge.cc b/chrome/browser/chromeos/arc/boot_phase_monitor/arc_boot_phase_monitor_bridge.cc index e26a8b42..1a173d4 100644 --- a/chrome/browser/chromeos/arc/boot_phase_monitor/arc_boot_phase_monitor_bridge.cc +++ b/chrome/browser/chromeos/arc/boot_phase_monitor/arc_boot_phase_monitor_bridge.cc
@@ -81,15 +81,19 @@ : arc_bridge_service_(bridge_service), account_id_(multi_user_util::GetAccountIdFromProfile( Profile::FromBrowserContext(context))), - binding_(this) { + binding_(this), + first_app_launch_delay_recorder_( + base::BindRepeating(&RecordAppLaunchDelay)) { arc_bridge_service_->boot_phase_monitor()->AddObserver(this); - ArcSessionManager::Get()->AddObserver(this); + auto* arc_session_manager = ArcSessionManager::Get(); + DCHECK(arc_session_manager); + arc_session_manager->AddObserver(this); } ArcBootPhaseMonitorBridge::~ArcBootPhaseMonitorBridge() { DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); arc_bridge_service_->boot_phase_monitor()->RemoveObserver(this); - ArcSessionManager* arc_session_manager = ArcSessionManager::Get(); + auto* arc_session_manager = ArcSessionManager::Get(); DCHECK(arc_session_manager); arc_session_manager->RemoveObserver(this); } @@ -102,7 +106,7 @@ if (boot_completed_) { VLOG(2) << "ARC has already fully started. Recording the UMA now."; - RecordAppLaunchDelay(base::TimeDelta()); + first_app_launch_delay_recorder_.Run(base::TimeDelta()); return; } app_launch_time_ = base::TimeTicks::Now(); @@ -137,8 +141,10 @@ VLOG(2) << "ArcInstanceThrottle created in OnBootCompleted()"; } - if (!app_launch_time_.is_null()) - RecordAppLaunchDelay(base::TimeTicks::Now() - app_launch_time_); + if (!app_launch_time_.is_null()) { + first_app_launch_delay_recorder_.Run(base::TimeTicks::Now() - + app_launch_time_); + } } void ArcBootPhaseMonitorBridge::OnArcInitialStart() {
diff --git a/chrome/browser/chromeos/arc/boot_phase_monitor/arc_boot_phase_monitor_bridge.h b/chrome/browser/chromeos/arc/boot_phase_monitor/arc_boot_phase_monitor_bridge.h index ee84e39..1bc4bb1 100644 --- a/chrome/browser/chromeos/arc/boot_phase_monitor/arc_boot_phase_monitor_bridge.h +++ b/chrome/browser/chromeos/arc/boot_phase_monitor/arc_boot_phase_monitor_bridge.h
@@ -7,6 +7,7 @@ #include <memory> +#include "base/callback.h" #include "base/macros.h" #include "base/threading/thread_checker.h" #include "base/time/time.h" @@ -34,6 +35,9 @@ public mojom::BootPhaseMonitorHost, public ArcSessionManager::Observer { public: + using FirstAppLaunchDelayRecorder = + base::RepeatingCallback<void(base::TimeDelta)>; + // Returns singleton instance for the given BrowserContext, // or nullptr if the browser |context| is not allowed to use ARC. static ArcBootPhaseMonitorBridge* GetForBrowserContext( @@ -65,6 +69,16 @@ void OnArcSessionStopped(ArcStopReason stop_reason) override; void OnArcSessionRestarting() override; + void RecordFirstAppLaunchDelayUMAForTesting() { + RecordFirstAppLaunchDelayUMAInternal(); + } + + ArcInstanceThrottle* throttle_for_testing() const { return throttle_.get(); } + void set_first_app_launch_delay_recorder_for_testing( + const FirstAppLaunchDelayRecorder& first_app_launch_delay_recorder) { + first_app_launch_delay_recorder_ = first_app_launch_delay_recorder; + } + private: void RecordFirstAppLaunchDelayUMAInternal(); void Reset(); @@ -74,6 +88,7 @@ ArcBridgeService* const arc_bridge_service_; // Owned by ArcServiceManager. const AccountId account_id_; mojo::Binding<mojom::BootPhaseMonitorHost> binding_; + FirstAppLaunchDelayRecorder first_app_launch_delay_recorder_; // The following variables must be reset every time when the instance stops or // restarts.
diff --git a/chrome/browser/chromeos/arc/boot_phase_monitor/arc_boot_phase_monitor_bridge_unittest.cc b/chrome/browser/chromeos/arc/boot_phase_monitor/arc_boot_phase_monitor_bridge_unittest.cc new file mode 100644 index 0000000..5c2616b --- /dev/null +++ b/chrome/browser/chromeos/arc/boot_phase_monitor/arc_boot_phase_monitor_bridge_unittest.cc
@@ -0,0 +1,203 @@ +// 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 "chrome/browser/chromeos/arc/boot_phase_monitor/arc_boot_phase_monitor_bridge.h" + +#include "base/threading/platform_thread.h" +#include "chrome/test/base/testing_profile.h" +#include "chromeos/dbus/dbus_thread_manager.h" +#include "chromeos/dbus/fake_session_manager_client.h" +#include "components/arc/arc_bridge_service.h" +#include "components/arc/test/fake_arc_session.h" +#include "content/public/test/test_browser_thread_bundle.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace arc { + +namespace { + +class ArcBootPhaseMonitorBridgeTest : public testing::Test { + public: + ArcBootPhaseMonitorBridgeTest() = default; + ~ArcBootPhaseMonitorBridgeTest() override = default; + + void SetUp() override { + chromeos::DBusThreadManager::GetSetterForTesting()->SetSessionManagerClient( + base::MakeUnique<chromeos::FakeSessionManagerClient>()); + chromeos::DBusThreadManager::Initialize(); + + record_uma_counter_ = 0; + testing_profile_ = base::MakeUnique<TestingProfile>(); + arc_session_manager_ = base::MakeUnique<ArcSessionManager>( + base::MakeUnique<ArcSessionRunner>(base::Bind(FakeArcSession::Create))); + bridge_service_ = base::MakeUnique<ArcBridgeService>(); + + boot_phase_monitor_bridge_ = base::MakeUnique<ArcBootPhaseMonitorBridge>( + testing_profile_.get(), bridge_service_.get()); + boot_phase_monitor_bridge_->set_first_app_launch_delay_recorder_for_testing( + base::BindRepeating(&ArcBootPhaseMonitorBridgeTest::RecordUMA, + base::Unretained(this))); + } + + void TearDown() override { + boot_phase_monitor_bridge_->Shutdown(); + boot_phase_monitor_bridge_.reset(); + + bridge_service_.reset(); + arc_session_manager_.reset(); + testing_profile_.reset(); + } + + protected: + ArcSessionManager* arc_session_manager() const { + return arc_session_manager_.get(); + } + ArcBootPhaseMonitorBridge* boot_phase_monitor_bridge() const { + return boot_phase_monitor_bridge_.get(); + } + size_t record_uma_counter() const { return record_uma_counter_; } + base::TimeDelta last_time_delta() const { return last_time_delta_; } + + private: + void RecordUMA(base::TimeDelta delta) { + last_time_delta_ = delta; + ++record_uma_counter_; + } + + content::TestBrowserThreadBundle thread_bundle_; + std::unique_ptr<TestingProfile> testing_profile_; + std::unique_ptr<ArcSessionManager> arc_session_manager_; + std::unique_ptr<ArcBridgeService> bridge_service_; + std::unique_ptr<ArcBootPhaseMonitorBridge> boot_phase_monitor_bridge_; + + size_t record_uma_counter_; + base::TimeDelta last_time_delta_; + + DISALLOW_COPY_AND_ASSIGN(ArcBootPhaseMonitorBridgeTest); +}; + +// Tests that ArcBootPhaseMonitorBridge can be constructed and destructed. +TEST_F(ArcBootPhaseMonitorBridgeTest, TestConstructDestruct) {} + +// Tests that the throttle is created on BootCompleted. +TEST_F(ArcBootPhaseMonitorBridgeTest, TestThrottleCreation) { + // Tell |arc_session_manager_| that this is not opt-in boot. + arc_session_manager()->set_directly_started_for_testing(true); + + // Initially, |throttle_| is null. + EXPECT_EQ(nullptr, boot_phase_monitor_bridge()->throttle_for_testing()); + // OnBootCompleted() creates it. + boot_phase_monitor_bridge()->OnBootCompleted(); + EXPECT_NE(nullptr, boot_phase_monitor_bridge()->throttle_for_testing()); + // ..but it's removed when the session stops. + boot_phase_monitor_bridge()->OnArcSessionStopped(ArcStopReason::SHUTDOWN); + EXPECT_EQ(nullptr, boot_phase_monitor_bridge()->throttle_for_testing()); +} + +// Tests the same but with OnSessionRestarting(). +TEST_F(ArcBootPhaseMonitorBridgeTest, TestThrottleCreation_Restart) { + // Tell |arc_session_manager_| that this is not opt-in boot. + arc_session_manager()->set_directly_started_for_testing(true); + + EXPECT_EQ(nullptr, boot_phase_monitor_bridge()->throttle_for_testing()); + boot_phase_monitor_bridge()->OnBootCompleted(); + EXPECT_NE(nullptr, boot_phase_monitor_bridge()->throttle_for_testing()); + // Call OnArcSessionRestarting() instead, and confirm that |throttle_| is + // gone. + boot_phase_monitor_bridge()->OnArcSessionRestarting(); + EXPECT_EQ(nullptr, boot_phase_monitor_bridge()->throttle_for_testing()); + // Also make sure that |throttle| is created again once restarting id done. + boot_phase_monitor_bridge()->OnBootCompleted(); + EXPECT_NE(nullptr, boot_phase_monitor_bridge()->throttle_for_testing()); +} + +// Tests that the throttle is created on ArcInitialStart when opting in. +TEST_F(ArcBootPhaseMonitorBridgeTest, TestThrottleCreation_OptIn) { + // Tell |arc_session_manager_| that this *is* opt-in boot. + arc_session_manager()->set_directly_started_for_testing(false); + + // Initially, |throttle_| is null. + EXPECT_EQ(nullptr, boot_phase_monitor_bridge()->throttle_for_testing()); + // OnArcInitialStart(), which is called when the user accepts ToS, creates + // |throttle_|. + boot_phase_monitor_bridge()->OnArcInitialStart(); + EXPECT_NE(nullptr, boot_phase_monitor_bridge()->throttle_for_testing()); + // ..and OnBootCompleted() does not delete it. + boot_phase_monitor_bridge()->OnBootCompleted(); + EXPECT_NE(nullptr, boot_phase_monitor_bridge()->throttle_for_testing()); + // ..but it's removed when the session stops. + boot_phase_monitor_bridge()->OnArcSessionStopped(ArcStopReason::SHUTDOWN); + EXPECT_EQ(nullptr, boot_phase_monitor_bridge()->throttle_for_testing()); +} + +// Tests that the UMA recording function is never called unless +// RecordFirstAppLaunchDelayUMA is called. +TEST_F(ArcBootPhaseMonitorBridgeTest, TestRecordUMA_None) { + EXPECT_EQ(0U, record_uma_counter()); + boot_phase_monitor_bridge()->OnBootCompleted(); + EXPECT_EQ(0U, record_uma_counter()); + boot_phase_monitor_bridge()->OnArcSessionStopped(ArcStopReason::SHUTDOWN); + EXPECT_EQ(0U, record_uma_counter()); +} + +// Tests that RecordFirstAppLaunchDelayUMA() actually calls the UMA recording +// function (but only after OnBootCompleted.) +TEST_F(ArcBootPhaseMonitorBridgeTest, TestRecordUMA_AppLaunchBeforeBoot) { + EXPECT_EQ(0U, record_uma_counter()); + // Calling RecordFirstAppLaunchDelayUMA() before boot shouldn't immediately + // record UMA. + boot_phase_monitor_bridge()->RecordFirstAppLaunchDelayUMAForTesting(); + EXPECT_EQ(0U, record_uma_counter()); + // Sleep for 1ms just to make sure 0 won't be passed to RecordUMA(). + base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(1)); + // UMA recording should be done on BootCompleted. + boot_phase_monitor_bridge()->OnBootCompleted(); + EXPECT_EQ(1U, record_uma_counter()); + // In this case, |delta| passed to the UMA recording function should be >0. + EXPECT_LT(base::TimeDelta(), last_time_delta()); +} + +// Tests the same with calling RecordFirstAppLaunchDelayUMA() after boot. +TEST_F(ArcBootPhaseMonitorBridgeTest, TestRecordUMA_AppLaunchAfterBoot) { + EXPECT_EQ(0U, record_uma_counter()); + boot_phase_monitor_bridge()->OnBootCompleted(); + EXPECT_EQ(0U, record_uma_counter()); + // Calling RecordFirstAppLaunchDelayUMA() after boot should immediately record + // UMA. + boot_phase_monitor_bridge()->RecordFirstAppLaunchDelayUMAForTesting(); + EXPECT_EQ(1U, record_uma_counter()); + // In this case, |delta| passed to the UMA recording function should be 0. + EXPECT_TRUE(last_time_delta().is_zero()); +} + +// Tests the same with calling RecordFirstAppLaunchDelayUMA() twice. +TEST_F(ArcBootPhaseMonitorBridgeTest, + TestRecordUMA_AppLaunchesBeforeAndAfterBoot) { + EXPECT_EQ(0U, record_uma_counter()); + boot_phase_monitor_bridge()->RecordFirstAppLaunchDelayUMAForTesting(); + EXPECT_EQ(0U, record_uma_counter()); + boot_phase_monitor_bridge()->OnBootCompleted(); + EXPECT_EQ(1U, record_uma_counter()); + EXPECT_LT(base::TimeDelta(), last_time_delta()); + // Call the record function again and check that the counter is not changed. + boot_phase_monitor_bridge()->RecordFirstAppLaunchDelayUMAForTesting(); + EXPECT_EQ(1U, record_uma_counter()); +} + +// Tests the same with calling RecordFirstAppLaunchDelayUMA() twice after boot. +TEST_F(ArcBootPhaseMonitorBridgeTest, TestRecordUMA_AppLaunchesAfterBoot) { + EXPECT_EQ(0U, record_uma_counter()); + boot_phase_monitor_bridge()->OnBootCompleted(); + EXPECT_EQ(0U, record_uma_counter()); + boot_phase_monitor_bridge()->RecordFirstAppLaunchDelayUMAForTesting(); + EXPECT_EQ(1U, record_uma_counter()); + EXPECT_TRUE(last_time_delta().is_zero()); + // Call the record function again and check that the counter is not changed. + boot_phase_monitor_bridge()->RecordFirstAppLaunchDelayUMAForTesting(); + EXPECT_EQ(1U, record_uma_counter()); +} + +} // namespace + +} // namespace arc
diff --git a/chrome/browser/chromeos/arc/boot_phase_monitor/arc_instance_throttle.cc b/chrome/browser/chromeos/arc/boot_phase_monitor/arc_instance_throttle.cc index ecd5272..74797d0 100644 --- a/chrome/browser/chromeos/arc/boot_phase_monitor/arc_instance_throttle.cc +++ b/chrome/browser/chromeos/arc/boot_phase_monitor/arc_instance_throttle.cc
@@ -20,13 +20,16 @@ } // namespace ArcInstanceThrottle::ArcInstanceThrottle() { + if (!ash::Shell::HasInstance()) // for unit testing. + return; ash::Shell::Get()->activation_client()->AddObserver(this); ThrottleInstance(ash::wm::GetActiveWindow()); } ArcInstanceThrottle::~ArcInstanceThrottle() { - if (ash::Shell::HasInstance()) - ash::Shell::Get()->activation_client()->RemoveObserver(this); + if (!ash::Shell::HasInstance()) + return; + ash::Shell::Get()->activation_client()->RemoveObserver(this); } void ArcInstanceThrottle::OnWindowActivated(ActivationReason reason,
diff --git a/chrome/browser/chromeos/enrollment_dialog_view.cc b/chrome/browser/chromeos/enrollment_dialog_view.cc index 75d6357e..0892055 100644 --- a/chrome/browser/chromeos/enrollment_dialog_view.cc +++ b/chrome/browser/chromeos/enrollment_dialog_view.cc
@@ -94,6 +94,8 @@ target_uri_(target_uri), connect_(connect), added_cert_(false) { + set_margins(ChromeLayoutProvider::Get()->GetInsetsMetric( + views::INSETS_DIALOG_CONTENTS)); chrome::RecordDialogCreation(chrome::DialogIdentifier::ENROLLMENT); } @@ -164,7 +166,7 @@ label->SetMultiLine(true); label->SetAllowCharacterBreak(true); - views::GridLayout* grid_layout = views::GridLayout::CreatePanel(this); + views::GridLayout* grid_layout = views::GridLayout::CreateAndInstall(this); views::ColumnSet* columns = grid_layout->AddColumnSet(0); columns->AddColumn(views::GridLayout::FILL, // Horizontal resize.
diff --git a/chrome/browser/chromeos/file_manager/volume_manager_unittest.cc b/chrome/browser/chromeos/file_manager/volume_manager_unittest.cc index b8e0852..1f0f17fb 100644 --- a/chrome/browser/chromeos/file_manager/volume_manager_unittest.cc +++ b/chrome/browser/chromeos/file_manager/volume_manager_unittest.cc
@@ -948,4 +948,82 @@ EXPECT_FALSE(volume.get()); } +TEST_F(VolumeManagerTest, OnRenameEvent_Started) { + LoggingObserver observer; + volume_manager()->AddObserver(&observer); + + volume_manager()->OnRenameEvent( + chromeos::disks::DiskMountManager::RENAME_STARTED, + chromeos::RENAME_ERROR_NONE, "device1"); + + ASSERT_EQ(1U, observer.events().size()); + const LoggingObserver::Event& event = observer.events()[0]; + EXPECT_EQ(LoggingObserver::Event::RENAME_STARTED, event.type); + EXPECT_EQ("device1", event.device_path); + EXPECT_TRUE(event.success); + + volume_manager()->RemoveObserver(&observer); +} + +TEST_F(VolumeManagerTest, OnRenameEvent_StartFailed) { + LoggingObserver observer; + volume_manager()->AddObserver(&observer); + + volume_manager()->OnRenameEvent( + chromeos::disks::DiskMountManager::RENAME_STARTED, + chromeos::RENAME_ERROR_UNKNOWN, "device1"); + + ASSERT_EQ(1U, observer.events().size()); + const LoggingObserver::Event& event = observer.events()[0]; + EXPECT_EQ(LoggingObserver::Event::RENAME_STARTED, event.type); + EXPECT_EQ("device1", event.device_path); + EXPECT_FALSE(event.success); + + volume_manager()->RemoveObserver(&observer); +} + +TEST_F(VolumeManagerTest, OnRenameEvent_Completed) { + LoggingObserver observer; + volume_manager()->AddObserver(&observer); + + volume_manager()->OnRenameEvent( + chromeos::disks::DiskMountManager::RENAME_COMPLETED, + chromeos::RENAME_ERROR_NONE, "device1"); + + ASSERT_EQ(1U, observer.events().size()); + const LoggingObserver::Event& event = observer.events()[0]; + EXPECT_EQ(LoggingObserver::Event::RENAME_COMPLETED, event.type); + EXPECT_EQ("device1", event.device_path); + EXPECT_TRUE(event.success); + + // When "rename" is successfully done, VolumeManager requests to mount it. + ASSERT_EQ(1U, disk_mount_manager_->mount_requests().size()); + const FakeDiskMountManager::MountRequest& mount_request = + disk_mount_manager_->mount_requests()[0]; + EXPECT_EQ("device1", mount_request.source_path); + EXPECT_EQ("", mount_request.source_format); + EXPECT_EQ(chromeos::MOUNT_TYPE_DEVICE, mount_request.type); + + volume_manager()->RemoveObserver(&observer); +} + +TEST_F(VolumeManagerTest, OnRenameEvent_CompletedFailed) { + LoggingObserver observer; + volume_manager()->AddObserver(&observer); + + volume_manager()->OnRenameEvent( + chromeos::disks::DiskMountManager::RENAME_COMPLETED, + chromeos::RENAME_ERROR_UNKNOWN, "device1"); + + ASSERT_EQ(1U, observer.events().size()); + const LoggingObserver::Event& event = observer.events()[0]; + EXPECT_EQ(LoggingObserver::Event::RENAME_COMPLETED, event.type); + EXPECT_EQ("device1", event.device_path); + EXPECT_FALSE(event.success); + + EXPECT_EQ(0U, disk_mount_manager_->mount_requests().size()); + + volume_manager()->RemoveObserver(&observer); +} + } // namespace file_manager
diff --git a/chrome/browser/chromeos/login/ui/simple_web_view_dialog.cc b/chrome/browser/chromeos/login/ui/simple_web_view_dialog.cc index 5d55bc35..439f432 100644 --- a/chrome/browser/chromeos/login/ui/simple_web_view_dialog.cc +++ b/chrome/browser/chromeos/login/ui/simple_web_view_dialog.cc
@@ -67,8 +67,7 @@ views::View* forward, views::View* reload, views::View* location_bar) { - GridLayout* layout = new GridLayout(this); - SetLayoutManager(layout); + GridLayout* layout = GridLayout::CreateAndInstall(this); const int related_horizontal_spacing = ChromeLayoutProvider::Get()->GetDistanceMetric( @@ -210,8 +209,7 @@ toolbar_row->Init(back_, forward_, reload_, location_bar_); // Layout. - GridLayout* layout = new GridLayout(this); - SetLayoutManager(layout); + GridLayout* layout = GridLayout::CreateAndInstall(this); views::ColumnSet* column_set = layout->AddColumnSet(0); column_set->AddColumn(GridLayout::FILL, GridLayout::FILL, 1,
diff --git a/chrome/browser/chromeos/options/vpn_config_view.cc b/chrome/browser/chromeos/options/vpn_config_view.cc index d0635f6..ee3ed01 100644 --- a/chrome/browser/chromeos/options/vpn_config_view.cc +++ b/chrome/browser/chromeos/options/vpn_config_view.cc
@@ -31,6 +31,7 @@ #include "ui/base/l10n/l10n_util.h" #include "ui/base/models/combobox_model.h" #include "ui/events/event.h" +#include "ui/views/border.h" #include "ui/views/controls/button/checkbox.h" #include "ui/views/controls/combobox/combobox.h" #include "ui/views/controls/label.h" @@ -236,7 +237,6 @@ enable_group_name_(false), user_passphrase_required_(false), title_(0), - layout_(NULL), server_textfield_(NULL), service_text_(NULL), service_textfield_(NULL), @@ -495,6 +495,10 @@ } void VPNConfigView::Init() { + const views::LayoutProvider* provider = views::LayoutProvider::Get(); + SetBorder(views::CreateEmptyBorder( + provider->GetInsetsMetric(views::INSETS_DIALOG_CONTENTS))); + const NetworkState* vpn = NULL; if (!service_path_.empty()) { vpn = NetworkHandler::Get()->network_state_handler()-> @@ -502,13 +506,12 @@ DCHECK(vpn && vpn->type() == shill::kTypeVPN); } - layout_ = views::GridLayout::CreatePanel(this); - views::LayoutProvider* provider = views::LayoutProvider::Get(); + views::GridLayout* layout = views::GridLayout::CreateAndInstall(this); // Observer any changes to the certificate list. CertLibrary::Get()->AddObserver(this); - views::ColumnSet* column_set = layout_->AddColumnSet(0); + views::ColumnSet* column_set = layout->AddColumnSet(0); // Label. column_set->AddColumn(views::GridLayout::LEADING, views::GridLayout::FILL, 1, views::GridLayout::USE_PREF, 0, 0); @@ -538,15 +541,15 @@ enable_group_name_ = true; // Server label and input. - layout_->StartRow(0, 0); + layout->StartRow(0, 0); views::View* server_label = new views::Label(l10n_util::GetStringUTF16( IDS_OPTIONS_SETTINGS_INTERNET_OPTIONS_VPN_SERVER_HOSTNAME)); - layout_->AddView(server_label); + layout->AddView(server_label); server_textfield_ = new views::Textfield(); server_textfield_->set_controller(this); - layout_->AddView(server_textfield_); - layout_->AddPaddingRow( + layout->AddView(server_textfield_); + layout->AddPaddingRow( 0, provider->GetDistanceMetric(views::DISTANCE_RELATED_CONTROL_VERTICAL)); if (!service_path_.empty()) { server_label->SetEnabled(false); @@ -554,26 +557,26 @@ } // Service label and name or input. - layout_->StartRow(0, 0); - layout_->AddView(new views::Label(l10n_util::GetStringUTF16( + layout->StartRow(0, 0); + layout->AddView(new views::Label(l10n_util::GetStringUTF16( IDS_OPTIONS_SETTINGS_INTERNET_OPTIONS_VPN_SERVICE_NAME))); if (service_path_.empty()) { service_textfield_ = new views::Textfield(); service_textfield_->set_controller(this); - layout_->AddView(service_textfield_); + layout->AddView(service_textfield_); service_text_ = NULL; } else { service_text_ = new views::Label(); service_text_->SetHorizontalAlignment(gfx::ALIGN_LEFT); - layout_->AddView(service_text_); + layout->AddView(service_text_); service_textfield_ = NULL; } - layout_->AddPaddingRow( + layout->AddPaddingRow( 0, provider->GetDistanceMetric(views::DISTANCE_RELATED_CONTROL_VERTICAL)); // Provider type label and select. - layout_->StartRow(0, 0); - layout_->AddView(new views::Label(l10n_util::GetStringUTF16( + layout->StartRow(0, 0); + layout->AddView(new views::Label(l10n_util::GetStringUTF16( IDS_OPTIONS_SETTINGS_INTERNET_OPTIONS_VPN_PROVIDER_TYPE))); if (service_path_.empty()) { provider_type_combobox_model_.reset( @@ -581,131 +584,129 @@ provider_type_combobox_ = new views::Combobox( provider_type_combobox_model_.get()); provider_type_combobox_->set_listener(this); - layout_->AddView(provider_type_combobox_); + layout->AddView(provider_type_combobox_); provider_type_text_label_ = NULL; } else { provider_type_text_label_ = new views::Label(); provider_type_text_label_->SetHorizontalAlignment(gfx::ALIGN_LEFT); - layout_->AddView(provider_type_text_label_); + layout->AddView(provider_type_text_label_); provider_type_combobox_ = NULL; } - layout_->AddPaddingRow( + layout->AddPaddingRow( 0, provider->GetDistanceMetric(views::DISTANCE_RELATED_CONTROL_VERTICAL)); // PSK passphrase label, input and visible button. - layout_->StartRow(0, 0); + layout->StartRow(0, 0); psk_passphrase_label_ = new views::Label(l10n_util::GetStringUTF16( IDS_OPTIONS_SETTINGS_INTERNET_OPTIONS_VPN_PSK_PASSPHRASE)); - layout_->AddView(psk_passphrase_label_); + layout->AddView(psk_passphrase_label_); psk_passphrase_textfield_ = new PassphraseTextfield(); psk_passphrase_textfield_->set_controller(this); - layout_->AddView(psk_passphrase_textfield_); - layout_->AddView( - new ControlledSettingIndicatorView(psk_passphrase_ui_data_)); - layout_->AddPaddingRow( + layout->AddView(psk_passphrase_textfield_); + layout->AddView(new ControlledSettingIndicatorView(psk_passphrase_ui_data_)); + layout->AddPaddingRow( 0, provider->GetDistanceMetric(views::DISTANCE_RELATED_CONTROL_VERTICAL)); // Server CA certificate if (service_path_.empty()) { - layout_->StartRow(0, 0); + layout->StartRow(0, 0); server_ca_cert_label_ = new views::Label(l10n_util::GetStringUTF16( IDS_OPTIONS_SETTINGS_INTERNET_OPTIONS_CERT_SERVER_CA)); - layout_->AddView(server_ca_cert_label_); + layout->AddView(server_ca_cert_label_); server_ca_cert_combobox_model_.reset( new internal::VpnServerCACertComboboxModel()); server_ca_cert_combobox_ = new views::Combobox( server_ca_cert_combobox_model_.get()); - layout_->AddView(server_ca_cert_combobox_); - layout_->AddView(new ControlledSettingIndicatorView(ca_cert_ui_data_)); - layout_->AddPaddingRow(0, provider->GetDistanceMetric( - views::DISTANCE_RELATED_CONTROL_VERTICAL)); + layout->AddView(server_ca_cert_combobox_); + layout->AddView(new ControlledSettingIndicatorView(ca_cert_ui_data_)); + layout->AddPaddingRow(0, provider->GetDistanceMetric( + views::DISTANCE_RELATED_CONTROL_VERTICAL)); } else { server_ca_cert_label_ = NULL; server_ca_cert_combobox_ = NULL; } // User certificate label and input. - layout_->StartRow(0, 0); + layout->StartRow(0, 0); user_cert_label_ = new views::Label(l10n_util::GetStringUTF16( IDS_OPTIONS_SETTINGS_INTERNET_OPTIONS_VPN_USER_CERT)); - layout_->AddView(user_cert_label_); + layout->AddView(user_cert_label_); user_cert_combobox_model_.reset( new internal::VpnUserCertComboboxModel()); user_cert_combobox_ = new views::Combobox(user_cert_combobox_model_.get()); user_cert_combobox_->set_listener(this); - layout_->AddView(user_cert_combobox_); - layout_->AddView(new ControlledSettingIndicatorView(user_cert_ui_data_)); - layout_->AddPaddingRow( + layout->AddView(user_cert_combobox_); + layout->AddView(new ControlledSettingIndicatorView(user_cert_ui_data_)); + layout->AddPaddingRow( 0, provider->GetDistanceMetric(views::DISTANCE_RELATED_CONTROL_VERTICAL)); // Username label and input. - layout_->StartRow(0, 0); - layout_->AddView(new views::Label(l10n_util::GetStringUTF16( + layout->StartRow(0, 0); + layout->AddView(new views::Label(l10n_util::GetStringUTF16( IDS_OPTIONS_SETTINGS_INTERNET_OPTIONS_VPN_USERNAME))); username_textfield_ = new views::Textfield(); username_textfield_->set_controller(this); username_textfield_->SetEnabled(username_ui_data_.IsEditable()); - layout_->AddView(username_textfield_); - layout_->AddView(new ControlledSettingIndicatorView(username_ui_data_)); - layout_->AddPaddingRow( + layout->AddView(username_textfield_); + layout->AddView(new ControlledSettingIndicatorView(username_ui_data_)); + layout->AddPaddingRow( 0, provider->GetDistanceMetric(views::DISTANCE_RELATED_CONTROL_VERTICAL)); // User passphrase label, input and visble button. - layout_->StartRow(0, 0); - layout_->AddView(new views::Label(l10n_util::GetStringUTF16( + layout->StartRow(0, 0); + layout->AddView(new views::Label(l10n_util::GetStringUTF16( IDS_OPTIONS_SETTINGS_INTERNET_OPTIONS_VPN_USER_PASSPHRASE))); user_passphrase_textfield_ = new PassphraseTextfield(); user_passphrase_textfield_->set_controller(this); user_passphrase_textfield_->SetEnabled(user_passphrase_ui_data_.IsEditable()); - layout_->AddView(user_passphrase_textfield_); - layout_->AddView( - new ControlledSettingIndicatorView(user_passphrase_ui_data_)); - layout_->AddPaddingRow( + layout->AddView(user_passphrase_textfield_); + layout->AddView(new ControlledSettingIndicatorView(user_passphrase_ui_data_)); + layout->AddPaddingRow( 0, provider->GetDistanceMetric(views::DISTANCE_RELATED_CONTROL_VERTICAL)); // OTP label and input. - layout_->StartRow(0, 0); + layout->StartRow(0, 0); otp_label_ = new views::Label(l10n_util::GetStringUTF16( IDS_OPTIONS_SETTINGS_INTERNET_OPTIONS_VPN_OTP)); - layout_->AddView(otp_label_); + layout->AddView(otp_label_); otp_textfield_ = new views::Textfield(); otp_textfield_->set_controller(this); - layout_->AddView(otp_textfield_); - layout_->AddPaddingRow( + layout->AddView(otp_textfield_); + layout->AddPaddingRow( 0, provider->GetDistanceMetric(views::DISTANCE_RELATED_CONTROL_VERTICAL)); // Group Name label and input. - layout_->StartRow(0, 0); + layout->StartRow(0, 0); group_name_label_ = new views::Label(l10n_util::GetStringUTF16( IDS_OPTIONS_SETTINGS_INTERNET_OPTIONS_VPN_GROUP_NAME)); - layout_->AddView(group_name_label_); + layout->AddView(group_name_label_); group_name_textfield_ = new views::Textfield(); group_name_textfield_->set_controller(this); - layout_->AddView(group_name_textfield_); - layout_->AddView(new ControlledSettingIndicatorView(group_name_ui_data_)); - layout_->AddPaddingRow( + layout->AddView(group_name_textfield_); + layout->AddView(new ControlledSettingIndicatorView(group_name_ui_data_)); + layout->AddPaddingRow( 0, provider->GetDistanceMetric(views::DISTANCE_RELATED_CONTROL_VERTICAL)); // Save credentials - layout_->StartRow(0, 0); + layout->StartRow(0, 0); save_credentials_checkbox_ = new views::Checkbox( l10n_util::GetStringUTF16( IDS_OPTIONS_SETTINGS_INTERNET_OPTIONS_SAVE_CREDENTIALS)); save_credentials_checkbox_->SetEnabled( save_credentials_ui_data_.IsEditable()); - layout_->SkipColumns(1); - layout_->AddView(save_credentials_checkbox_); - layout_->AddView( + layout->SkipColumns(1); + layout->AddView(save_credentials_checkbox_); + layout->AddView( new ControlledSettingIndicatorView(save_credentials_ui_data_)); // Error label. - layout_->StartRow(0, 0); - layout_->SkipColumns(1); + layout->StartRow(0, 0); + layout->SkipColumns(1); error_label_ = new views::Label(); error_label_->SetHorizontalAlignment(gfx::ALIGN_LEFT); error_label_->SetEnabledColor(SK_ColorRED); - layout_->AddView(error_label_); + layout->AddView(error_label_); // Set or hide the UI, update comboboxes and error labels. Refresh();
diff --git a/chrome/browser/chromeos/options/vpn_config_view.h b/chrome/browser/chromeos/options/vpn_config_view.h index b583a731..03d9b4a 100644 --- a/chrome/browser/chromeos/options/vpn_config_view.h +++ b/chrome/browser/chromeos/options/vpn_config_view.h
@@ -26,7 +26,6 @@ namespace views { class Checkbox; -class GridLayout; class Label; } @@ -159,7 +158,6 @@ int title_; - views::GridLayout* layout_; views::Textfield* server_textfield_; views::Label* service_text_; views::Textfield* service_textfield_;
diff --git a/chrome/browser/chromeos/options/wifi_config_view.cc b/chrome/browser/chromeos/options/wifi_config_view.cc index 4293aace..08e42ac 100644 --- a/chrome/browser/chromeos/options/wifi_config_view.cc +++ b/chrome/browser/chromeos/options/wifi_config_view.cc
@@ -36,6 +36,7 @@ #include "ui/base/material_design/material_design_controller.h" #include "ui/base/resource/resource_bundle.h" #include "ui/events/event.h" +#include "ui/views/border.h" #include "ui/views/controls/button/checkbox.h" #include "ui/views/controls/button/image_button.h" #include "ui/views/controls/combobox/combobox.h" @@ -904,6 +905,10 @@ } void WifiConfigView::Init(bool show_8021x) { + views::LayoutProvider* provider = views::LayoutProvider::Get(); + SetBorder(views::CreateEmptyBorder( + provider->GetInsetsMetric(views::INSETS_DIALOG_CONTENTS))); + const NetworkState* network = GetNetworkState(); if (network) { if (network->type() == shill::kTypeWifi) { @@ -938,8 +943,7 @@ ParseUIProperty(&passphrase_ui_data_, network, ::onc::wifi::kPassphrase); } - views::GridLayout* layout = views::GridLayout::CreatePanel(this); - views::LayoutProvider* provider = views::LayoutProvider::Get(); + views::GridLayout* layout = views::GridLayout::CreateAndInstall(this); const int column_view_set_id = 0; views::ColumnSet* column_set = layout->AddColumnSet(column_view_set_id);
diff --git a/chrome/browser/chromeos/options/wimax_config_view.cc b/chrome/browser/chromeos/options/wimax_config_view.cc index 10f2128..6a1bbd4 100644 --- a/chrome/browser/chromeos/options/wimax_config_view.cc +++ b/chrome/browser/chromeos/options/wimax_config_view.cc
@@ -27,6 +27,7 @@ #include "ui/base/l10n/l10n_util.h" #include "ui/base/resource/resource_bundle.h" #include "ui/events/event.h" +#include "ui/views/border.h" #include "ui/views/controls/button/checkbox.h" #include "ui/views/controls/button/image_button.h" #include "ui/views/controls/label.h" @@ -196,6 +197,10 @@ } void WimaxConfigView::Init() { + const views::LayoutProvider* provider = views::LayoutProvider::Get(); + SetBorder(views::CreateEmptyBorder( + provider->GetInsetsMetric(views::INSETS_DIALOG_CONTENTS))); + const NetworkState* wimax = NetworkHandler::Get()->network_state_handler()-> GetNetworkState(service_path_); DCHECK(wimax && wimax->type() == shill::kTypeWimax); @@ -207,8 +212,7 @@ WifiConfigView::ParseUIProperty( &passphrase_ui_data_, wimax, ::onc::wifi::kPassphrase); - views::GridLayout* layout = views::GridLayout::CreatePanel(this); - views::LayoutProvider* provider = views::LayoutProvider::Get(); + views::GridLayout* layout = views::GridLayout::CreateAndInstall(this); const int column_view_set_id = 0; views::ColumnSet* column_set = layout->AddColumnSet(column_view_set_id);
diff --git a/chrome/browser/chromeos/profiles/multiprofiles_session_aborted_dialog.cc b/chrome/browser/chromeos/profiles/multiprofiles_session_aborted_dialog.cc index 3ed1efe..8d742ca 100644 --- a/chrome/browser/chromeos/profiles/multiprofiles_session_aborted_dialog.cc +++ b/chrome/browser/chromeos/profiles/multiprofiles_session_aborted_dialog.cc
@@ -118,8 +118,7 @@ constexpr int kTopInset = 10; constexpr int kOtherInset = 40; // Create the views and layout manager and set them up. - views::GridLayout* grid_layout = new views::GridLayout(this); - SetLayoutManager(grid_layout); + views::GridLayout* grid_layout = views::GridLayout::CreateAndInstall(this); SetBorder(views::CreateEmptyBorder(kTopInset, kOtherInset, kOtherInset, kOtherInset)); @@ -148,7 +147,6 @@ grid_layout->StartRow(0, 0); grid_layout->AddView(label); - SetLayoutManager(grid_layout); Layout(); }
diff --git a/chrome/browser/chromeos/ui/request_pin_view.cc b/chrome/browser/chromeos/ui/request_pin_view.cc index fbb2000..21794797 100644 --- a/chrome/browser/chromeos/ui/request_pin_view.cc +++ b/chrome/browser/chromeos/ui/request_pin_view.cc
@@ -162,7 +162,10 @@ } void RequestPinView::Init() { - views::GridLayout* layout = views::GridLayout::CreatePanel(this); + set_margins(ChromeLayoutProvider::Get()->GetInsetsMetric( + views::INSETS_DIALOG_CONTENTS)); + + views::GridLayout* layout = views::GridLayout::CreateAndInstall(this); int column_view_set_id = 0; views::ColumnSet* column_set = layout->AddColumnSet(column_view_set_id);
diff --git a/chrome/browser/offline_pages/android/offline_page_bridge.cc b/chrome/browser/offline_pages/android/offline_page_bridge.cc index 60a83d8f..115ecca 100644 --- a/chrome/browser/offline_pages/android/offline_page_bridge.cc +++ b/chrome/browser/offline_pages/android/offline_page_bridge.cc
@@ -17,6 +17,7 @@ #include "base/bind.h" #include "base/logging.h" #include "base/memory/ptr_util.h" +#include "chrome/browser/android/tab_android.h" #include "chrome/browser/offline_pages/offline_page_mhtml_archiver.h" #include "chrome/browser/offline_pages/offline_page_model_factory.h" #include "chrome/browser/offline_pages/offline_page_utils.h" @@ -282,6 +283,18 @@ return ToJavaOfflinePageItem(env, offline_page); } +// static +std::string OfflinePageBridge::GetEncodedOriginApp( + const content::WebContents* web_contents) { + TabAndroid* tab = TabAndroid::FromWebContents(web_contents); + if (!tab) + return ""; + JNIEnv* env = base::android::AttachCurrentThread(); + return ConvertJavaStringToUTF8( + env, + Java_OfflinePageBridge_getEncodedOriginApp(env, tab->GetJavaObject())); +} + OfflinePageBridge::OfflinePageBridge(JNIEnv* env, content::BrowserContext* browser_context, OfflinePageModel* offline_page_model)
diff --git a/chrome/browser/offline_pages/android/offline_page_bridge.h b/chrome/browser/offline_pages/android/offline_page_bridge.h index 2fd6758..800e5b8 100644 --- a/chrome/browser/offline_pages/android/offline_page_bridge.h +++ b/chrome/browser/offline_pages/android/offline_page_bridge.h
@@ -17,6 +17,7 @@ namespace content { class BrowserContext; +class WebContents; } namespace offline_pages { @@ -33,6 +34,9 @@ JNIEnv* env, const OfflinePageItem& offline_page); + static std::string GetEncodedOriginApp( + const content::WebContents* web_contents); + OfflinePageBridge(JNIEnv* env, content::BrowserContext* browser_context, OfflinePageModel* offline_page_model);
diff --git a/chrome/browser/offline_pages/android/offline_page_origin_utils_android.cc b/chrome/browser/offline_pages/android/offline_page_origin_utils_android.cc new file mode 100644 index 0000000..31d32cd --- /dev/null +++ b/chrome/browser/offline_pages/android/offline_page_origin_utils_android.cc
@@ -0,0 +1,16 @@ +// 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 "chrome/browser/offline_pages/offline_page_origin_utils.h" + +#include "chrome/browser/offline_pages/android/offline_page_bridge.h" +#include "content/public/browser/web_contents.h" + +namespace offline_pages { +// static +std::string OfflinePageOriginUtils::GetEncodedOriginAppFor( + content::WebContents* web_contents) { + return android::OfflinePageBridge::GetEncodedOriginApp(web_contents); +} +} // namespace offline_pages
diff --git a/chrome/browser/offline_pages/offline_page_origin_utils.h b/chrome/browser/offline_pages/offline_page_origin_utils.h new file mode 100644 index 0000000..2f26b86 --- /dev/null +++ b/chrome/browser/offline_pages/offline_page_origin_utils.h
@@ -0,0 +1,25 @@ +// 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 CHROME_BROWSER_OFFLINE_PAGES_OFFLINE_PAGE_ORIGIN_UTILS_H_ +#define CHROME_BROWSER_OFFLINE_PAGES_OFFLINE_PAGE_ORIGIN_UTILS_H_ + +#include <string> + +namespace content { +class WebContents; +} + +namespace offline_pages { + +// Utility class for retrieving origin for a download request - whether it +// came on behalf of some app via CCT or Chrome. Methods are platform-specific. +class OfflinePageOriginUtils { + public: + // Retrieves the encoded origin from the |web_contents|. + static std::string GetEncodedOriginAppFor(content::WebContents* web_contents); +}; +} // namespace offline_pages + +#endif // CHROME_BROWSER_OFFLINE_PAGES_OFFLINE_PAGE_ORIGIN_UTILS_H_
diff --git a/chrome/browser/offline_pages/offline_page_utils.cc b/chrome/browser/offline_pages/offline_page_utils.cc index f017a45e..435fc437 100644 --- a/chrome/browser/offline_pages/offline_page_utils.cc +++ b/chrome/browser/offline_pages/offline_page_utils.cc
@@ -14,10 +14,11 @@ #include "base/threading/thread_task_runner_handle.h" #include "base/time/time.h" #include "build/build_config.h" -#include "chrome/browser/offline_pages/offline_page_tab_helper.h" #include "chrome/browser/net/net_error_tab_helper.h" #include "chrome/browser/offline_pages/offline_page_mhtml_archiver.h" #include "chrome/browser/offline_pages/offline_page_model_factory.h" +#include "chrome/browser/offline_pages/offline_page_origin_utils.h" +#include "chrome/browser/offline_pages/offline_page_tab_helper.h" #include "chrome/browser/offline_pages/request_coordinator_factory.h" #include "components/offline_pages/core/background/request_coordinator.h" #include "components/offline_pages/core/background/save_page_request.h" @@ -300,7 +301,9 @@ const std::string& name_space, const GURL& url, DownloadUIActionFlags ui_action) { - ScheduleDownload(web_contents, name_space, url, ui_action, ""); + std::string origin = + OfflinePageOriginUtils::GetEncodedOriginAppFor(web_contents); + ScheduleDownload(web_contents, name_space, url, ui_action, origin); } // static
diff --git a/chrome/browser/plugins/pdf_plugin_placeholder_observer.cc b/chrome/browser/plugins/pdf_plugin_placeholder_observer.cc index 65fc578..e8ec95a 100644 --- a/chrome/browser/plugins/pdf_plugin_placeholder_observer.cc +++ b/chrome/browser/plugins/pdf_plugin_placeholder_observer.cc
@@ -5,8 +5,29 @@ #include "chrome/browser/plugins/pdf_plugin_placeholder_observer.h" #include "chrome/common/render_messages.h" +#include "content/public/browser/browser_context.h" #include "content/public/browser/child_process_security_policy.h" +#include "content/public/browser/download_item.h" +#include "content/public/browser/download_manager.h" +#include "content/public/browser/download_url_parameters.h" #include "content/public/browser/render_frame_host.h" +#include "content/public/browser/render_process_host.h" +#include "content/public/browser/render_view_host.h" +#include "content/public/browser/storage_partition.h" +#include "net/traffic_annotation/network_traffic_annotation.h" +#include "ppapi/features/features.h" + +namespace { + +#if BUILDFLAG(ENABLE_PLUGINS) +void OnDownloadStarted(content::DownloadItem* item, + content::DownloadInterruptReason interrupt_reason) { + if (item && interrupt_reason == content::DOWNLOAD_INTERRUPT_REASON_NONE) + item->SetOpenWhenComplete(true); +} +#endif // BUILDFLAG(ENABLE_PLUGINS) + +} // namespace DEFINE_WEB_CONTENTS_USER_DATA_KEY(PDFPluginPlaceholderObserver); @@ -36,11 +57,59 @@ return; } - web_contents()->OpenURL(content::OpenURLParams( - url, - content::Referrer::SanitizeForRequest( - url, content::Referrer(web_contents()->GetURL(), - blink::kWebReferrerPolicyDefault)), - WindowOpenDisposition::CURRENT_TAB, ui::PAGE_TRANSITION_AUTO_BOOKMARK, - false)); + content::Referrer referrer = content::Referrer::SanitizeForRequest( + url, content::Referrer(web_contents()->GetURL(), + blink::kWebReferrerPolicyDefault)); + +#if BUILDFLAG(ENABLE_PLUGINS) + content::StoragePartition* storage_partition = + content::BrowserContext::GetStoragePartition( + web_contents()->GetBrowserContext(), + render_frame_host->GetSiteInstance()); + net::NetworkTrafficAnnotationTag traffic_annotation = + net::DefineNetworkTrafficAnnotation("pdf_plugin_placeholder", R"( + semantics { + sender: "PDF Plugin Placeholder" + description: + "When the PDF Viewer is unavailable, a placeholder is shown for " + "embedded PDFs. This placeholder allows the user to download and " + "open the PDF file via a button." + trigger: + "The user clicks the 'View PDF' button in the PDF placeholder." + data: "None." + destination: WEBSITE + } + policy { + cookies_allowed: NO + setting: + "This feature can be disabled via 'Download PDF files instead of " + "automatically opening them in Chrome' in settings under content. " + "The feature is disabled by default." + chrome_policy { + AlwaysOpenPdfExternally { + AlwaysOpenPdfExternally: false + } + } + })"); + std::unique_ptr<content::DownloadUrlParameters> params = + base::MakeUnique<content::DownloadUrlParameters>( + url, web_contents()->GetRenderProcessHost()->GetID(), + web_contents()->GetRenderViewHost()->GetRoutingID(), + render_frame_host->GetRoutingID(), + storage_partition->GetURLRequestContext(), traffic_annotation); + params->set_referrer(referrer); + params->set_callback(base::Bind(&OnDownloadStarted)); + + content::BrowserContext::GetDownloadManager( + web_contents()->GetBrowserContext()) + ->DownloadUrl(std::move(params)); + +#else // !BUILDFLAG(ENABLE_PLUGINS) + content::OpenURLParams open_url_params( + url, referrer, WindowOpenDisposition::CURRENT_TAB, + ui::PAGE_TRANSITION_AUTO_BOOKMARK, false); + // On Android, PDFs downloaded with a user gesture are auto-opened. + open_url_params.user_gesture = true; + web_contents()->OpenURL(open_url_params); +#endif // BUILDFLAG(ENABLE_PLUGINS) }
diff --git a/chrome/browser/resources/md_user_manager/user_manager.html b/chrome/browser/resources/md_user_manager/user_manager.html index c838cf33..95537c92 100644 --- a/chrome/browser/resources/md_user_manager/user_manager.html +++ b/chrome/browser/resources/md_user_manager/user_manager.html
@@ -56,6 +56,7 @@ * manager. */ #outer-container { + -webkit-box-orient: vertical; min-height: 0; overflow-x: hidden; overflow-y: auto; @@ -308,11 +309,23 @@ }; @apply(--action-button); } + + #user-manager-prompt-message { + font-size: 19px; + margin-bottom: 45px; + text-align: center; + } + + #user-manager-prompt-message:empty { + display: none; + } + </style> </head> <body> <user-manager-pages> <div id="outer-container"> + <div id="user-manager-prompt-message">$i18n{userManagerPromptMessage}</div> <user-manager-tutorial></user-manager-tutorial> <div id="oobe" class="faded"> <div id="inner-container">
diff --git a/chrome/browser/ui/BUILD.gn b/chrome/browser/ui/BUILD.gn index b0f6c6d5..8344b6b 100644 --- a/chrome/browser/ui/BUILD.gn +++ b/chrome/browser/ui/BUILD.gn
@@ -2857,6 +2857,8 @@ "cocoa/multi_key_equivalent_button.mm", "cocoa/new_tab_button.h", "cocoa/new_tab_button.mm", + "cocoa/nsview_additions.h", + "cocoa/nsview_additions.mm", "cocoa/omnibox/omnibox_popup_cell.h", "cocoa/omnibox/omnibox_popup_cell.mm", "cocoa/omnibox/omnibox_popup_matrix.h",
diff --git a/chrome/browser/ui/ash/app_list/app_list_browsertest.cc b/chrome/browser/ui/ash/app_list/app_list_browsertest.cc new file mode 100644 index 0000000..0ad0ae1 --- /dev/null +++ b/chrome/browser/ui/ash/app_list/app_list_browsertest.cc
@@ -0,0 +1,99 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/browser/apps/app_browsertest_util.h" +#include "chrome/browser/chromeos/ash_config.h" +#include "chrome/browser/extensions/extension_browsertest.h" +#include "chrome/browser/ui/ash/app_list/app_list_controller_ash.h" +#include "chrome/browser/ui/ash/app_list/app_list_service_ash.h" +#include "chrome/browser/ui/ash/app_list/test/app_list_service_ash_test_api.h" +#include "chrome/browser/ui/extensions/app_launch_params.h" +#include "chrome/browser/ui/extensions/application_launch.h" +#include "chrome/test/base/in_process_browser_test.h" +#include "content/public/browser/notification_service.h" +#include "content/public/browser/notification_types.h" +#include "content/public/test/test_utils.h" +#include "ui/app_list/presenter/app_list_presenter_impl.h" +#include "ui/app_list/views/app_list_item_view.h" +#include "ui/app_list/views/apps_grid_view.h" +#include "ui/aura/window.h" +#include "ui/display/display.h" +#include "ui/display/screen.h" +#include "ui/events/test/event_generator.h" +#include "ui/wm/core/window_util.h" + +using AppListTest = InProcessBrowserTest; +using AppListControllerDelegateAshTest = extensions::PlatformAppBrowserTest; + +// Test that clicking on app list context menus doesn't close the app list. +IN_PROC_BROWSER_TEST_F(AppListTest, ClickingContextMenuDoesNotDismiss) { + // Show the app list on the primary display. + AppListServiceAsh* service = AppListServiceAsh::GetInstance(); + app_list::AppListPresenterImpl* presenter = service->GetAppListPresenter(); + presenter->Show(display::Screen::GetScreen()->GetPrimaryDisplay().id()); + aura::Window* window = presenter->GetWindow(); + ASSERT_TRUE(window); + + // Show a context menu for the first app list item view. + AppListServiceAshTestApi test_api; + app_list::AppsGridView* grid_view = test_api.GetRootGridView(); + app_list::AppListItemView* item_view = grid_view->GetItemViewAt(0); + item_view->ShowContextMenu(gfx::Point(), ui::MENU_SOURCE_MOUSE); + + // Find the context menu as a transient child of the app list. + aura::Window* transient_parent = + chromeos::GetAshConfig() == ash::Config::MASH ? window->parent() : window; + const std::vector<aura::Window*>& transient_children = + wm::GetTransientChildren(transient_parent); + ASSERT_EQ(1u, transient_children.size()); + aura::Window* menu = transient_children[0]; + + // Press the left mouse button on the menu window, AppListPresenterDelegateMus + // should not close the app list nor the context menu on this pointer event. + ui::test::EventGenerator menu_event_generator(menu); + menu_event_generator.set_current_location(menu->GetBoundsInScreen().origin()); + menu_event_generator.PressLeftButton(); + + // Check that the window and the app list are still open. + ASSERT_EQ(window, presenter->GetWindow()); + EXPECT_EQ(1u, wm::GetTransientChildren(transient_parent).size()); +} + +// Test AppListControllerDelegateAsh::IsAppOpen for extension apps. +IN_PROC_BROWSER_TEST_F(AppListControllerDelegateAshTest, IsExtensionAppOpen) { + AppListControllerDelegateAsh delegate(nullptr); + EXPECT_FALSE(delegate.IsAppOpen("fake_extension_app_id")); + + base::FilePath extension_path = test_data_dir_.AppendASCII("app"); + const extensions::Extension* extension_app = LoadExtension(extension_path); + ASSERT_NE(nullptr, extension_app); + EXPECT_FALSE(delegate.IsAppOpen(extension_app->id())); + { + content::WindowedNotificationObserver app_loaded_observer( + content::NOTIFICATION_LOAD_COMPLETED_MAIN_FRAME, + content::NotificationService::AllSources()); + OpenApplication(AppLaunchParams( + profile(), extension_app, extensions::LAUNCH_CONTAINER_WINDOW, + WindowOpenDisposition::NEW_WINDOW, extensions::SOURCE_TEST)); + app_loaded_observer.Wait(); + } + EXPECT_TRUE(delegate.IsAppOpen(extension_app->id())); +} + +// Test AppListControllerDelegateAsh::IsAppOpen for platform apps. +IN_PROC_BROWSER_TEST_F(AppListControllerDelegateAshTest, IsPlatformAppOpen) { + AppListControllerDelegateAsh delegate(nullptr); + EXPECT_FALSE(delegate.IsAppOpen("fake_platform_app_id")); + + const extensions::Extension* app = InstallPlatformApp("minimal"); + EXPECT_FALSE(delegate.IsAppOpen(app->id())); + { + content::WindowedNotificationObserver app_loaded_observer( + content::NOTIFICATION_LOAD_COMPLETED_MAIN_FRAME, + content::NotificationService::AllSources()); + LaunchPlatformApp(app); + app_loaded_observer.Wait(); + } + EXPECT_TRUE(delegate.IsAppOpen(app->id())); +}
diff --git a/chrome/browser/ui/ash/app_list/app_list_controller_ash_browsertest.cc b/chrome/browser/ui/ash/app_list/app_list_controller_ash_browsertest.cc deleted file mode 100644 index 92ed5965..0000000 --- a/chrome/browser/ui/ash/app_list/app_list_controller_ash_browsertest.cc +++ /dev/null
@@ -1,52 +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. - -#include "chrome/browser/apps/app_browsertest_util.h" -#include "chrome/browser/extensions/extension_browsertest.h" -#include "chrome/browser/ui/ash/app_list/app_list_controller_ash.h" -#include "chrome/browser/ui/extensions/app_launch_params.h" -#include "chrome/browser/ui/extensions/application_launch.h" -#include "content/public/browser/notification_service.h" -#include "content/public/browser/notification_types.h" -#include "content/public/test/test_utils.h" - -using AppListControllerDelegateAshTest = extensions::PlatformAppBrowserTest; - -// Test AppListControllerDelegateAsh::IsAppOpen for extension apps. -IN_PROC_BROWSER_TEST_F(AppListControllerDelegateAshTest, IsExtensionAppOpen) { - AppListControllerDelegateAsh delegate(nullptr); - EXPECT_FALSE(delegate.IsAppOpen("fake_extension_app_id")); - - base::FilePath extension_path = test_data_dir_.AppendASCII("app"); - const extensions::Extension* extension_app = LoadExtension(extension_path); - ASSERT_NE(nullptr, extension_app); - EXPECT_FALSE(delegate.IsAppOpen(extension_app->id())); - { - content::WindowedNotificationObserver app_loaded_observer( - content::NOTIFICATION_LOAD_COMPLETED_MAIN_FRAME, - content::NotificationService::AllSources()); - OpenApplication(AppLaunchParams( - profile(), extension_app, extensions::LAUNCH_CONTAINER_WINDOW, - WindowOpenDisposition::NEW_WINDOW, extensions::SOURCE_TEST)); - app_loaded_observer.Wait(); - } - EXPECT_TRUE(delegate.IsAppOpen(extension_app->id())); -} - -// Test AppListControllerDelegateAsh::IsAppOpen for platform apps. -IN_PROC_BROWSER_TEST_F(AppListControllerDelegateAshTest, IsPlatformAppOpen) { - AppListControllerDelegateAsh delegate(nullptr); - EXPECT_FALSE(delegate.IsAppOpen("fake_platform_app_id")); - - const extensions::Extension* app = InstallPlatformApp("minimal"); - EXPECT_FALSE(delegate.IsAppOpen(app->id())); - { - content::WindowedNotificationObserver app_loaded_observer( - content::NOTIFICATION_LOAD_COMPLETED_MAIN_FRAME, - content::NotificationService::AllSources()); - LaunchPlatformApp(app); - app_loaded_observer.Wait(); - } - EXPECT_TRUE(delegate.IsAppOpen(app->id())); -}
diff --git a/chrome/browser/ui/ash/app_list/app_list_presenter_delegate_mus.cc b/chrome/browser/ui/ash/app_list/app_list_presenter_delegate_mus.cc index 0eb2dc3..9ff2aea8 100644 --- a/chrome/browser/ui/ash/app_list/app_list_presenter_delegate_mus.cc +++ b/chrome/browser/ui/ash/app_list/app_list_presenter_delegate_mus.cc
@@ -7,10 +7,12 @@ #include "ui/app_list/presenter/app_list_presenter_impl.h" #include "ui/app_list/presenter/app_list_view_delegate_factory.h" #include "ui/app_list/views/app_list_view.h" +#include "ui/aura/window.h" #include "ui/display/display.h" #include "ui/display/screen.h" #include "ui/views/mus/mus_client.h" #include "ui/views/mus/pointer_watcher_event_router.h" +#include "ui/wm/core/window_util.h" namespace { @@ -115,11 +117,28 @@ const ui::PointerEvent& event, const gfx::Point& location_in_screen, gfx::NativeView target) { + // Looks for touch pressed and pointer down events outside the app list. + if (event.type() != ui::ET_TOUCH_PRESSED && + event.type() != ui::ET_POINTER_DOWN) { + return; + } + + // Bail if there is no app list, or if the event targets the app list. views::Widget* target_widget = views::Widget::GetTopLevelWidgetForNativeView(target); - // Dismiss app list on a mouse click or touch outside of the app list window. - if ((event.type() == ui::ET_TOUCH_PRESSED || - event.type() == ui::ET_POINTER_DOWN) && - (!target || (view_ && target_widget != view_->GetWidget()))) - presenter_->Dismiss(); + views::Widget* app_list_widget = view_ ? view_->GetWidget() : nullptr; + if (!app_list_widget || target_widget == app_list_widget) + return; + + // Bail if the event targets a context menu in the app list window. In mash, + // app list context menus are configured such that the parent of the app list + // window is the transient parent of the context menu window's parent. Yikes! + aura::Window* app_list = app_list_widget->GetNativeWindow(); + if (app_list && app_list->parent() && target && target->parent() && + wm::HasTransientAncestor(target->parent(), app_list->parent())) { + return; + } + + // Dismiss the app list for all other event targets, including null. + presenter_->Dismiss(); }
diff --git a/chrome/browser/ui/cocoa/nsview_additions.h b/chrome/browser/ui/cocoa/nsview_additions.h new file mode 100644 index 0000000..b9018cf --- /dev/null +++ b/chrome/browser/ui/cocoa/nsview_additions.h
@@ -0,0 +1,21 @@ +// 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 CHROME_BROWSER_UI_COCOA_NSVIEW_ADDITIONS_H_ +#define CHROME_BROWSER_UI_COCOA_NSVIEW_ADDITIONS_H_ + +#import <AppKit/AppKit.h> + +@interface NSView (ChromeBrowserAdditions) + +// If the UI is in RTL mode, swaps NSViewMinXMargin and NSViewMaxXMargin. ++ (NSAutoresizingMaskOptions)cr_localizedAutoresizingMask: + (NSAutoresizingMaskOptions)mask; + +// If the UI is in RTL mode, flips the rect in the receiver's bounds. +- (NSRect)cr_localizedRect:(NSRect)rect; + +@end + +#endif // CHROME_BROWSER_UI_COCOA_NSVIEW_ADDITIONS_H_
diff --git a/chrome/browser/ui/cocoa/nsview_additions.mm b/chrome/browser/ui/cocoa/nsview_additions.mm new file mode 100644 index 0000000..232d476 --- /dev/null +++ b/chrome/browser/ui/cocoa/nsview_additions.mm
@@ -0,0 +1,30 @@ +// 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. + +#import "chrome/browser/ui/cocoa/nsview_additions.h" + +#import "chrome/browser/ui/cocoa/l10n_util.h" + +@implementation NSView (ChromeBrowserAdditions) + ++ (NSAutoresizingMaskOptions)cr_localizedAutoresizingMask: + (NSAutoresizingMaskOptions)mask { + if (cocoa_l10n_util::ShouldDoExperimentalRTLLayout()) { + // If exactly one of NSViewMinXMargin and NSViewMaxXMargin are set… + if (((mask & NSViewMinXMargin) != 0) != ((mask & NSViewMaxXMargin) != 0)) { + // …then swap it with the opposite one. + mask ^= NSViewMinXMargin | NSViewMaxXMargin; + } + } + return mask; +} + +- (NSRect)cr_localizedRect:(NSRect)rect { + if (cocoa_l10n_util::ShouldDoExperimentalRTLLayout()) { + rect.origin.x = NSWidth(self.bounds) - NSWidth(rect) - NSMinX(rect); + } + return rect; +} + +@end
diff --git a/chrome/browser/ui/cocoa/nsview_additions_unittest.mm b/chrome/browser/ui/cocoa/nsview_additions_unittest.mm new file mode 100644 index 0000000..a3faa33 --- /dev/null +++ b/chrome/browser/ui/cocoa/nsview_additions_unittest.mm
@@ -0,0 +1,61 @@ +// 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. + +#import "chrome/browser/ui/cocoa/nsview_additions.h" + +#import "chrome/browser/ui/cocoa/test/cocoa_test_helper.h" +#import "chrome/browser/ui/cocoa/test/scoped_force_rtl_mac.h" + +namespace { + +class NSViewAdditionsTest : public ui::CocoaTest {}; + +TEST_F(NSViewAdditionsTest, LocalizedAutoresizingMask) { + const struct { + NSAutoresizingMaskOptions ltr; + NSAutoresizingMaskOptions rtl; + } kCases[] = { + // A mask without x margin options shouldn't be changed. + {NSViewWidthSizable | NSViewHeightSizable, + NSViewWidthSizable | NSViewHeightSizable}, + + // …nor should a mask with both x margin options. + {NSViewMinXMargin | NSViewMaxXMargin, + NSViewMinXMargin | NSViewMaxXMargin}, + + // MinXMargin becomes MaxXMargin, + {NSViewMinXMargin, NSViewMaxXMargin}, + + // MaxXMargin becomes MinXMargin. + {NSViewMaxXMargin, NSViewMinXMargin}, + + // Y margins should be left alone. + {NSViewMinYMargin | NSViewMinXMargin, + NSViewMinYMargin | NSViewMaxXMargin}, + }; + + for (const auto& pair : kCases) { + EXPECT_EQ(pair.ltr, [NSView cr_localizedAutoresizingMask:pair.ltr]); + } + + cocoa_l10n_util::ScopedForceRTLMac rtl; + + for (const auto& pair : kCases) { + EXPECT_EQ(pair.rtl, [NSView cr_localizedAutoresizingMask:pair.ltr]); + } +} + +TEST_F(NSViewAdditionsTest, LocalizedRect) { + NSView* contentView = test_window().contentView; + NSRect rect = NSMakeRect(0, 0, 10, 10); + EXPECT_TRUE(NSEqualRects(rect, [contentView cr_localizedRect:rect])); + + cocoa_l10n_util::ScopedForceRTLMac rtl; + + EXPECT_TRUE( + NSEqualRects(NSMakeRect(NSMaxX(contentView.bounds) - 10, 0, 10, 10), + [test_window().contentView cr_localizedRect:rect])); +} + +} // namespace
diff --git a/chrome/browser/ui/views/accessibility/invert_bubble_view.cc b/chrome/browser/ui/views/accessibility/invert_bubble_view.cc index 441bbec..d87f871 100644 --- a/chrome/browser/ui/views/accessibility/invert_bubble_view.cc +++ b/chrome/browser/ui/views/accessibility/invert_bubble_view.cc
@@ -81,6 +81,10 @@ } void InvertBubbleView::Init() { + const ChromeLayoutProvider* provider = ChromeLayoutProvider::Get(); + SetBorder(views::CreateEmptyBorder( + provider->GetInsetsMetric(views::INSETS_DIALOG_CONTENTS))); + ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance(); const gfx::FontList& original_font_list = rb.GetFontList(ui::ResourceBundle::MediumFont); @@ -108,7 +112,7 @@ close_->SetFontList(original_font_list); close_->set_listener(this); - views::GridLayout* layout = views::GridLayout::CreatePanel(this); + views::GridLayout* layout = views::GridLayout::CreateAndInstall(this); views::ColumnSet* columns = layout->AddColumnSet(0); for (int i = 0; i < 4; i++) { @@ -119,9 +123,9 @@ layout->StartRow(0, 0); layout->AddView(title, 4, 1); - layout->StartRowWithPadding(0, 0, 0, - ChromeLayoutProvider::Get()->GetDistanceMetric( - DISTANCE_RELATED_CONTROL_VERTICAL_SMALL)); + layout->StartRowWithPadding( + 0, 0, 0, + provider->GetDistanceMetric(DISTANCE_RELATED_CONTROL_VERTICAL_SMALL)); layout->AddView(high_contrast_); layout->AddView(dark_theme_); layout->AddView(learn_more_);
diff --git a/chrome/browser/ui/views/apps/app_info_dialog/app_info_permissions_panel.cc b/chrome/browser/ui/views/apps/app_info_dialog/app_info_permissions_panel.cc index ab3da0f..063c51bf0 100644 --- a/chrome/browser/ui/views/apps/app_info_dialog/app_info_permissions_panel.cc +++ b/chrome/browser/ui/views/apps/app_info_dialog/app_info_permissions_panel.cc
@@ -90,8 +90,7 @@ class BulletedPermissionsList : public views::View { public: BulletedPermissionsList() { - layout_ = new views::GridLayout(this); - SetLayoutManager(layout_); + layout_ = views::GridLayout::CreateAndInstall(this); // Create 3 columns: the bullet, the bullet text, and the revoke button. views::ColumnSet* column_set = layout_->AddColumnSet(kBulletColumnSetId);
diff --git a/chrome/browser/ui/views/bookmarks/bookmark_bubble_view.cc b/chrome/browser/ui/views/bookmarks/bookmark_bubble_view.cc index 001abcf7..d97d71e 100644 --- a/chrome/browser/ui/views/bookmarks/bookmark_bubble_view.cc +++ b/chrome/browser/ui/views/bookmarks/bookmark_bubble_view.cc
@@ -296,8 +296,7 @@ SetLayoutManager(new views::FillLayout()); bookmark_contents_view_ = new views::View(); - GridLayout* layout = new GridLayout(bookmark_contents_view_); - bookmark_contents_view_->SetLayoutManager(layout); + GridLayout* layout = GridLayout::CreateAndInstall(bookmark_contents_view_); // This column set is used for the labels and textfields. constexpr int kColumnId = 0;
diff --git a/chrome/browser/ui/views/bookmarks/bookmark_editor_view.cc b/chrome/browser/ui/views/bookmarks/bookmark_editor_view.cc index 8e84959..b477990 100644 --- a/chrome/browser/ui/views/bookmarks/bookmark_editor_view.cc +++ b/chrome/browser/ui/views/bookmarks/bookmark_editor_view.cc
@@ -72,6 +72,8 @@ DCHECK(profile); DCHECK(bb_model_); DCHECK(bb_model_->client()->CanBeEditedByUser(parent)); + set_margins(ChromeLayoutProvider::Get()->GetInsetsMetric( + views::INSETS_DIALOG_CONTENTS)); Init(); chrome::RecordDialogCreation(chrome::DialogIdentifier::BOOKMARK_EDITOR); } @@ -335,7 +337,7 @@ new_folder_button_->SetEnabled(false); } - GridLayout* layout = GridLayout::CreatePanel(this); + GridLayout* layout = GridLayout::CreateAndInstall(this); ChromeLayoutProvider* provider = ChromeLayoutProvider::Get(); const int labels_column_set_id = 0;
diff --git a/chrome/browser/ui/views/certificate_selector.cc b/chrome/browser/ui/views/certificate_selector.cc index dc75b6e..34450c71 100644 --- a/chrome/browser/ui/views/certificate_selector.cc +++ b/chrome/browser/ui/views/certificate_selector.cc
@@ -118,6 +118,9 @@ : web_contents_(web_contents) { CHECK(web_contents_); + set_margins(ChromeLayoutProvider::Get()->GetInsetsMetric( + views::INSETS_DIALOG_CONTENTS)); + // |provider_names| and |identities_| are parallel arrays. // The entry at index |i| is the provider name for |identities_[i]|. std::vector<std::string> provider_names; @@ -197,7 +200,7 @@ void CertificateSelector::InitWithText( std::unique_ptr<views::View> text_label) { - views::GridLayout* const layout = views::GridLayout::CreatePanel(this); + views::GridLayout* const layout = views::GridLayout::CreateAndInstall(this); const int kColumnSetId = 0; views::ColumnSet* const column_set = layout->AddColumnSet(kColumnSetId);
diff --git a/chrome/browser/ui/views/collected_cookies_views.cc b/chrome/browser/ui/views/collected_cookies_views.cc index b45cd14..e168893 100644 --- a/chrome/browser/ui/views/collected_cookies_views.cc +++ b/chrome/browser/ui/views/collected_cookies_views.cc
@@ -313,13 +313,12 @@ void CollectedCookiesViews::Init() { using views::GridLayout; - GridLayout* layout = new GridLayout(this); + GridLayout* layout = GridLayout::CreateAndInstall(this); ChromeLayoutProvider* provider = ChromeLayoutProvider::Get(); if (provider->UseExtraDialogPadding()) { SetBorder( views::CreateEmptyBorder(gfx::Insets(kTabbedPaneTopPadding, 0, 0, 0))); } - SetLayoutManager(layout); const int single_column_layout_id = 0; views::ColumnSet* column_set = layout->AddColumnSet(single_column_layout_id); @@ -387,7 +386,10 @@ using views::GridLayout; views::View* pane = new views::View(); - GridLayout* layout = GridLayout::CreatePanel(pane); + GridLayout* layout = GridLayout::CreateAndInstall(pane); + pane->SetBorder( + views::CreateEmptyBorder(ChromeLayoutProvider::Get()->GetInsetsMetric( + views::INSETS_DIALOG_CONTENTS))); int unrelated_vertical_distance = ChromeLayoutProvider::Get()->GetDistanceMetric( views::DISTANCE_UNRELATED_CONTROL_VERTICAL); @@ -451,7 +453,10 @@ using views::GridLayout; views::View* pane = new views::View(); - GridLayout* layout = GridLayout::CreatePanel(pane); + GridLayout* layout = GridLayout::CreateAndInstall(pane); + pane->SetBorder( + views::CreateEmptyBorder(ChromeLayoutProvider::Get()->GetInsetsMetric( + views::INSETS_DIALOG_CONTENTS))); int unrelated_vertical_distance = ChromeLayoutProvider::Get()->GetDistanceMetric( views::DISTANCE_UNRELATED_CONTROL_VERTICAL);
diff --git a/chrome/browser/ui/views/confirm_bubble_views.cc b/chrome/browser/ui/views/confirm_bubble_views.cc index 161a1f8..dd8199a 100644 --- a/chrome/browser/ui/views/confirm_bubble_views.cc +++ b/chrome/browser/ui/views/confirm_bubble_views.cc
@@ -9,6 +9,7 @@ #include "chrome/browser/ui/browser_dialogs.h" #include "chrome/browser/ui/confirm_bubble.h" #include "chrome/browser/ui/confirm_bubble_model.h" +#include "chrome/browser/ui/views/harmony/chrome_layout_provider.h" #include "components/constrained_window/constrained_window_views.h" #include "ui/base/ui_features.h" #include "ui/views/controls/label.h" @@ -19,7 +20,9 @@ ConfirmBubbleViews::ConfirmBubbleViews( std::unique_ptr<ConfirmBubbleModel> model) : model_(std::move(model)), link_(NULL) { - views::GridLayout* layout = views::GridLayout::CreatePanel(this); + set_margins(ChromeLayoutProvider::Get()->GetInsetsMetric( + views::INSETS_DIALOG_CONTENTS)); + views::GridLayout* layout = views::GridLayout::CreateAndInstall(this); // Use a fixed maximum message width, so longer messages will wrap. const int kMaxMessageWidth = 400;
diff --git a/chrome/browser/ui/views/content_setting_bubble_contents.cc b/chrome/browser/ui/views/content_setting_bubble_contents.cc index 3bc6f4bb1..06e97b2 100644 --- a/chrome/browser/ui/views/content_setting_bubble_contents.cc +++ b/chrome/browser/ui/views/content_setting_bubble_contents.cc
@@ -249,8 +249,7 @@ void ContentSettingBubbleContents::ListItemContainer::ResetLayout() { using views::GridLayout; - GridLayout* layout = new GridLayout(this); - SetLayoutManager(layout); + GridLayout* layout = GridLayout::CreateAndInstall(this); views::ColumnSet* item_list_column_set = layout->AddColumnSet(0); item_list_column_set->AddColumn(GridLayout::LEADING, GridLayout::FILL, 0, GridLayout::USE_PREF, 0, 0); @@ -344,8 +343,7 @@ void ContentSettingBubbleContents::Init() { using views::GridLayout; - GridLayout* layout = new views::GridLayout(this); - SetLayoutManager(layout); + GridLayout* layout = views::GridLayout::CreateAndInstall(this); const ChromeLayoutProvider* provider = ChromeLayoutProvider::Get(); const int related_control_horizontal_spacing = provider->GetDistanceMetric(views::DISTANCE_RELATED_CONTROL_HORIZONTAL);
diff --git a/chrome/browser/ui/views/cookie_info_view.cc b/chrome/browser/ui/views/cookie_info_view.cc index da53a246..67fdaa8 100644 --- a/chrome/browser/ui/views/cookie_info_view.cc +++ b/chrome/browser/ui/views/cookie_info_view.cc
@@ -146,8 +146,7 @@ l10n_util::GetStringUTF16(IDS_COOKIES_COOKIE_EXPIRES_LABEL)); expires_value_field_ = new views::Textfield; - views::GridLayout* layout = new views::GridLayout(this); - SetLayoutManager(layout); + views::GridLayout* layout = views::GridLayout::CreateAndInstall(this); ChromeLayoutProvider* provider = ChromeLayoutProvider::Get(); const gfx::Insets& dialog_insets = provider->GetInsetsMetric(views::INSETS_DIALOG);
diff --git a/chrome/browser/ui/views/create_application_shortcut_view.cc b/chrome/browser/ui/views/create_application_shortcut_view.cc index 6f80396..0ea459a 100644 --- a/chrome/browser/ui/views/create_application_shortcut_view.cc +++ b/chrome/browser/ui/views/create_application_shortcut_view.cc
@@ -51,6 +51,8 @@ menu_check_box_(nullptr), quick_launch_check_box_(nullptr), weak_ptr_factory_(this) { + set_margins(ChromeLayoutProvider::Get()->GetInsetsMetric( + views::INSETS_DIALOG_CONTENTS)); InitControls(); // Get shortcut and icon information; needed for creating the shortcut. @@ -106,7 +108,7 @@ ChromeLayoutProvider* provider = ChromeLayoutProvider::Get(); // Layout controls - views::GridLayout* layout = views::GridLayout::CreatePanel(this); + views::GridLayout* layout = views::GridLayout::CreateAndInstall(this); static const int kHeaderColumnSetId = 0; views::ColumnSet* column_set = layout->AddColumnSet(kHeaderColumnSetId);
diff --git a/chrome/browser/ui/views/crypto_module_password_dialog_view.cc b/chrome/browser/ui/views/crypto_module_password_dialog_view.cc index 256c7ea..d302903 100644 --- a/chrome/browser/ui/views/crypto_module_password_dialog_view.cc +++ b/chrome/browser/ui/views/crypto_module_password_dialog_view.cc
@@ -27,6 +27,8 @@ const std::string& hostname, const CryptoModulePasswordCallback& callback) : callback_(callback) { + set_margins(ChromeLayoutProvider::Get()->GetInsetsMetric( + views::INSETS_DIALOG_CONTENTS)); Init(hostname, slot_name, reason); chrome::RecordDialogCreation(chrome::DialogIdentifier::CRYPTO_PASSWORD); } @@ -125,7 +127,7 @@ ChromeLayoutProvider* provider = ChromeLayoutProvider::Get(); - views::GridLayout* layout = views::GridLayout::CreatePanel(this); + views::GridLayout* layout = views::GridLayout::CreateAndInstall(this); views::ColumnSet* reason_column_set = layout->AddColumnSet(0); reason_column_set->AddColumn(
diff --git a/chrome/browser/ui/views/desktop_ios_promotion/desktop_ios_promotion_bubble_view.cc b/chrome/browser/ui/views/desktop_ios_promotion/desktop_ios_promotion_bubble_view.cc index a3273db..0639acf 100644 --- a/chrome/browser/ui/views/desktop_ios_promotion/desktop_ios_promotion_bubble_view.cc +++ b/chrome/browser/ui/views/desktop_ios_promotion/desktop_ios_promotion_bubble_view.cc
@@ -40,8 +40,7 @@ base::MakeUnique<DesktopIOSPromotionBubbleController>(profile, this, entry_point)) { - views::GridLayout* layout = new views::GridLayout(this); - SetLayoutManager(layout); + views::GridLayout* layout = views::GridLayout::CreateAndInstall(this); ChromeLayoutProvider* provider = ChromeLayoutProvider::Get(); SetBorder(views::CreateEmptyBorder( 0,
diff --git a/chrome/browser/ui/views/download/download_danger_prompt_views.cc b/chrome/browser/ui/views/download/download_danger_prompt_views.cc index a9fc68d6..9f170f9 100644 --- a/chrome/browser/ui/views/download/download_danger_prompt_views.cc +++ b/chrome/browser/ui/views/download/download_danger_prompt_views.cc
@@ -8,6 +8,7 @@ #include "chrome/browser/download/download_stats.h" #include "chrome/browser/extensions/api/experience_sampling_private/experience_sampling.h" #include "chrome/browser/ui/browser_dialogs.h" +#include "chrome/browser/ui/views/harmony/chrome_layout_provider.h" #include "chrome/grit/chromium_strings.h" #include "chrome/grit/generated_resources.h" #include "components/constrained_window/constrained_window_views.h" @@ -95,7 +96,10 @@ contents_view_ = new views::View; - views::GridLayout* layout = views::GridLayout::CreatePanel(contents_view_); + set_margins(ChromeLayoutProvider::Get()->GetInsetsMetric( + views::INSETS_DIALOG_CONTENTS)); + views::GridLayout* layout = + views::GridLayout::CreateAndInstall(contents_view_); views::ColumnSet* column_set = layout->AddColumnSet(0); column_set->AddColumn(views::GridLayout::FILL, views::GridLayout::FILL, 1,
diff --git a/chrome/browser/ui/views/extensions/bookmark_app_confirmation_view.cc b/chrome/browser/ui/views/extensions/bookmark_app_confirmation_view.cc index 425ddfa..865bd18 100644 --- a/chrome/browser/ui/views/extensions/bookmark_app_confirmation_view.cc +++ b/chrome/browser/ui/views/extensions/bookmark_app_confirmation_view.cc
@@ -65,7 +65,8 @@ open_as_window_checkbox_(nullptr), title_tf_(nullptr) { const ChromeLayoutProvider* layout_provider = ChromeLayoutProvider::Get(); - views::GridLayout* layout = views::GridLayout::CreatePanel(this); + set_margins(layout_provider->GetInsetsMetric(views::INSETS_DIALOG_CONTENTS)); + views::GridLayout* layout = views::GridLayout::CreateAndInstall(this); const int column_set_id = 0; views::ColumnSet* column_set = layout->AddColumnSet(column_set_id);
diff --git a/chrome/browser/ui/views/extensions/extension_install_dialog_view.cc b/chrome/browser/ui/views/extensions/extension_install_dialog_view.cc index 6f62003..3a00480 100644 --- a/chrome/browser/ui/views/extensions/extension_install_dialog_view.cc +++ b/chrome/browser/ui/views/extensions/extension_install_dialog_view.cc
@@ -156,8 +156,7 @@ } // namespace BulletedView::BulletedView(views::View* view) { - views::GridLayout* layout = new views::GridLayout(this); - SetLayoutManager(layout); + views::GridLayout* layout = views::GridLayout::CreateAndInstall(this); views::ColumnSet* column_set = layout->AddColumnSet(0); column_set->AddColumn(views::GridLayout::CENTER, views::GridLayout::LEADING, @@ -290,8 +289,8 @@ // Create the scrollable view which will contain the permissions and retained // files/devices. It will span the full content width. CustomScrollableView* scrollable = new CustomScrollableView(); - views::GridLayout* scroll_layout = new views::GridLayout(scrollable); - scrollable->SetLayoutManager(scroll_layout); + views::GridLayout* scroll_layout = + views::GridLayout::CreateAndInstall(scrollable); views::ColumnSet* scrollable_column_set = scroll_layout->AddColumnSet(column_set_id); @@ -455,8 +454,7 @@ // done so that the extension icon can be shown on the right of the dialog // title, but on the same y-axis, and the scroll view used to contain other // content can have its scrollbar aligned with the right edge of the dialog. - views::GridLayout* layout = new views::GridLayout(container_); - container_->SetLayoutManager(layout); + views::GridLayout* layout = views::GridLayout::CreateAndInstall(container_); container_->SetBorder(views::CreateEmptyBorder(0, content_insets.left(), content_insets.bottom(), 0)); AddChildView(container_); @@ -630,9 +628,7 @@ ExpandableContainerView::DetailsView::DetailsView(int horizontal_space, bool parent_bulleted) - : layout_(new views::GridLayout(this)), - state_(0) { - SetLayoutManager(layout_); + : layout_(views::GridLayout::CreateAndInstall(this)), state_(0) { views::ColumnSet* column_set = layout_->AddColumnSet(0); const int padding = GetLeftPaddingForBulletedItems(parent_bulleted); column_set->AddPaddingColumn(0, padding); @@ -675,8 +671,7 @@ more_details_(NULL), arrow_toggle_(NULL), expanded_(false) { - views::GridLayout* layout = new views::GridLayout(this); - SetLayoutManager(layout); + views::GridLayout* layout = views::GridLayout::CreateAndInstall(this); int column_set_id = 0; views::ColumnSet* column_set = layout->AddColumnSet(column_set_id); column_set->AddColumn(views::GridLayout::LEADING, views::GridLayout::LEADING,
diff --git a/chrome/browser/ui/views/extensions/media_galleries_dialog_views.cc b/chrome/browser/ui/views/extensions/media_galleries_dialog_views.cc index a2a3adc..7b2f4fe 100644 --- a/chrome/browser/ui/views/extensions/media_galleries_dialog_views.cc +++ b/chrome/browser/ui/views/extensions/media_galleries_dialog_views.cc
@@ -99,9 +99,12 @@ contents_->RemoveAllChildViews(true); checkbox_map_.clear(); - int dialog_content_width = views::Widget::GetLocalizedContentsWidth( + const int dialog_content_width = views::Widget::GetLocalizedContentsWidth( IDS_MEDIA_GALLERIES_DIALOG_CONTENT_WIDTH_CHARS); - views::GridLayout* layout = views::GridLayout::CreatePanel(contents_); + views::GridLayout* layout = views::GridLayout::CreateAndInstall(contents_); + contents_->SetBorder( + views::CreateEmptyBorder(ChromeLayoutProvider::Get()->GetInsetsMetric( + views::INSETS_DIALOG_CONTENTS))); int column_set_id = 0; views::ColumnSet* columns = layout->AddColumnSet(column_set_id);
diff --git a/chrome/browser/ui/views/first_run_bubble.cc b/chrome/browser/ui/views/first_run_bubble.cc index 2e8d4f24..539e4a7 100644 --- a/chrome/browser/ui/views/first_run_bubble.cc +++ b/chrome/browser/ui/views/first_run_bubble.cc
@@ -68,7 +68,7 @@ views::Label* subtext = new views::Label( l10n_util::GetStringUTF16(IDS_FR_BUBBLE_SUBTEXT), {original_font_list}); - views::GridLayout* layout = views::GridLayout::CreatePanel(this); + views::GridLayout* layout = views::GridLayout::CreateAndInstall(this); SetBorder(views::CreateEmptyBorder(kTopInset, kLeftInset, kBottomInset, kRightInset));
diff --git a/chrome/browser/ui/views/first_run_dialog.cc b/chrome/browser/ui/views/first_run_dialog.cc index d90bd93..b409d21 100644 --- a/chrome/browser/ui/views/first_run_dialog.cc +++ b/chrome/browser/ui/views/first_run_dialog.cc
@@ -80,7 +80,9 @@ : profile_(profile), make_default_(NULL), report_crashes_(NULL) { - GridLayout* layout = GridLayout::CreatePanel(this); + set_margins(ChromeLayoutProvider::Get()->GetInsetsMetric( + views::INSETS_DIALOG_CONTENTS)); + GridLayout* layout = GridLayout::CreateAndInstall(this); views::ColumnSet* column_set = layout->AddColumnSet(0); column_set->AddColumn(GridLayout::FILL, GridLayout::CENTER, 0,
diff --git a/chrome/browser/ui/views/global_error_bubble_view.cc b/chrome/browser/ui/views/global_error_bubble_view.cc index 6b63fad..3d16cba 100644 --- a/chrome/browser/ui/views/global_error_bubble_view.cc +++ b/chrome/browser/ui/views/global_error_bubble_view.cc
@@ -116,8 +116,7 @@ message_labels.push_back(message_label); } - views::GridLayout* layout = new views::GridLayout(this); - SetLayoutManager(layout); + views::GridLayout* layout = views::GridLayout::CreateAndInstall(this); // First row, message labels. views::ColumnSet* cs = layout->AddColumnSet(0);
diff --git a/chrome/browser/ui/views/hung_renderer_view.cc b/chrome/browser/ui/views/hung_renderer_view.cc index ea381ada..30b2727 100644 --- a/chrome/browser/ui/views/hung_renderer_view.cc +++ b/chrome/browser/ui/views/hung_renderer_view.cc
@@ -223,6 +223,8 @@ HungRendererDialogView::HungRendererDialogView() : info_label_(nullptr), hung_pages_table_(nullptr), initialized_(false) { + set_margins(ChromeLayoutProvider::Get()->GetInsetsMetric( + views::INSETS_DIALOG_CONTENTS)); chrome::RecordDialogCreation(chrome::DialogIdentifier::HUNG_RENDERER); } @@ -415,7 +417,7 @@ using views::GridLayout; - GridLayout* layout = GridLayout::CreatePanel(this); + GridLayout* layout = GridLayout::CreateAndInstall(this); ChromeLayoutProvider* provider = ChromeLayoutProvider::Get(); constexpr int kColumnSetId = 0;
diff --git a/chrome/browser/ui/views/ime/ime_warning_bubble_view.cc b/chrome/browser/ui/views/ime/ime_warning_bubble_view.cc index 01aad4c..a4f7893 100644 --- a/chrome/browser/ui/views/ime/ime_warning_bubble_view.cc +++ b/chrome/browser/ui/views/ime/ime_warning_bubble_view.cc
@@ -159,8 +159,7 @@ // ----------------------------------------- // - views::GridLayout* layout = new views::GridLayout(this); - SetLayoutManager(layout); + views::GridLayout* layout = views::GridLayout::CreateAndInstall(this); int cs_id = 0;
diff --git a/chrome/browser/ui/views/intent_picker_bubble_view.cc b/chrome/browser/ui/views/intent_picker_bubble_view.cc index 03995b6..f9b98b41 100644 --- a/chrome/browser/ui/views/intent_picker_bubble_view.cc +++ b/chrome/browser/ui/views/intent_picker_bubble_view.cc
@@ -163,8 +163,7 @@ } void IntentPickerBubbleView::Init() { - views::GridLayout* layout = new views::GridLayout(this); - SetLayoutManager(layout); + views::GridLayout* layout = views::GridLayout::CreateAndInstall(this); // Creates a view to hold the views for each app. views::View* scrollable_view = new views::View();
diff --git a/chrome/browser/ui/views/login_view.cc b/chrome/browser/ui/views/login_view.cc index 109a75b..c2a7dbd 100644 --- a/chrome/browser/ui/views/login_view.cc +++ b/chrome/browser/ui/views/login_view.cc
@@ -9,6 +9,7 @@ #include "chrome/browser/ui/views/harmony/textfield_layout.h" #include "components/strings/grit/components_strings.h" #include "ui/base/l10n/l10n_util.h" +#include "ui/views/border.h" #include "ui/views/controls/label.h" #include "ui/views/controls/textfield/textfield.h" #include "ui/views/layout/grid_layout.h" @@ -48,9 +49,11 @@ // to textfield_layout.h to decide. constexpr int kMessageWidth = 320; ChromeLayoutProvider* provider = ChromeLayoutProvider::Get(); + SetBorder(views::CreateEmptyBorder( + provider->GetInsetsMetric(views::INSETS_DIALOG_CONTENTS))); // Initialize the Grid Layout Manager used for this dialog box. - GridLayout* layout = GridLayout::CreatePanel(this); + GridLayout* layout = GridLayout::CreateAndInstall(this); views::ColumnSet* column_set = layout->AddColumnSet(kHeaderColumnSetId); column_set->AddColumn(GridLayout::FILL, GridLayout::FILL, kStretchy, GridLayout::FIXED, kMessageWidth, 0);
diff --git a/chrome/browser/ui/views/page_info/chosen_object_row.cc b/chrome/browser/ui/views/page_info/chosen_object_row.cc index b4cbcb9a..3333d94 100644 --- a/chrome/browser/ui/views/page_info/chosen_object_row.cc +++ b/chrome/browser/ui/views/page_info/chosen_object_row.cc
@@ -17,8 +17,7 @@ ChosenObjectRow::ChosenObjectRow( std::unique_ptr<PageInfoUI::ChosenObjectInfo> info) : info_(std::move(info)) { - views::GridLayout* layout = new views::GridLayout(this); - SetLayoutManager(layout); + views::GridLayout* layout = views::GridLayout::CreateAndInstall(this); const int column_set_id = 0; views::ColumnSet* column_set = layout->AddColumnSet(column_set_id); column_set->AddColumn(views::GridLayout::FILL, views::GridLayout::FILL, 1,
diff --git a/chrome/browser/ui/views/page_info/page_info_bubble_view.cc b/chrome/browser/ui/views/page_info/page_info_bubble_view.cc index 0ab7f29..4908f52 100644 --- a/chrome/browser/ui/views/page_info/page_info_bubble_view.cc +++ b/chrome/browser/ui/views/page_info/page_info_bubble_view.cc
@@ -137,8 +137,7 @@ views::Link* link) { views::View* new_view = new views::View(); - views::GridLayout* layout = new views::GridLayout(new_view); - new_view->SetLayoutManager(layout); + views::GridLayout* layout = views::GridLayout::CreateAndInstall(new_view); const int column = 0; views::ColumnSet* column_set = layout->AddColumnSet(column); @@ -260,8 +259,7 @@ password_reuse_button_container_(nullptr), change_password_button_(nullptr), whitelist_password_reuse_button_(nullptr) { - views::GridLayout* layout = new views::GridLayout(this); - SetLayoutManager(layout); + views::GridLayout* layout = views::GridLayout::CreateAndInstall(this); const int label_column_status = 1; AddColumnWithSideMargin(layout, side_margin, label_column_status); @@ -515,8 +513,7 @@ // below the dialog title. set_margins(gfx::Insets(0, 0, margins().bottom(), 0)); - views::GridLayout* layout = new views::GridLayout(this); - SetLayoutManager(layout); + views::GridLayout* layout = views::GridLayout::CreateAndInstall(this); // Use a single ColumnSet here. Otherwise the preferred width doesn't properly // propagate up to the dialog width. @@ -697,8 +694,8 @@ } permissions_view_ = new views::View(); - views::GridLayout* layout = new views::GridLayout(permissions_view_); - permissions_view_->SetLayoutManager(layout); + views::GridLayout* layout = + views::GridLayout::CreateAndInstall(permissions_view_); site_settings_view_->AddChildView(permissions_view_);
diff --git a/chrome/browser/ui/views/passwords/credentials_selection_view.cc b/chrome/browser/ui/views/passwords/credentials_selection_view.cc index 5401203..8b43b953 100644 --- a/chrome/browser/ui/views/passwords/credentials_selection_view.cc +++ b/chrome/browser/ui/views/passwords/credentials_selection_view.cc
@@ -38,7 +38,7 @@ DCHECK(!password_forms_->empty()); // Layout. - views::GridLayout* layout = new views::GridLayout(this); + views::GridLayout* layout = views::GridLayout::CreateAndInstall(this); SetLayoutManager(layout); // ColumnSet.
diff --git a/chrome/browser/ui/views/passwords/manage_password_items_view.cc b/chrome/browser/ui/views/passwords/manage_password_items_view.cc index cb6285d1..92931eb 100644 --- a/chrome/browser/ui/views/passwords/manage_password_items_view.cc +++ b/chrome/browser/ui/views/passwords/manage_password_items_view.cc
@@ -284,7 +284,7 @@ void ManagePasswordItemsView::AddRows() { const int vertical_padding = ChromeLayoutProvider::Get()->GetDistanceMetric( views::DISTANCE_RELATED_CONTROL_VERTICAL); - views::GridLayout* layout = new views::GridLayout(this); + views::GridLayout* layout = views::GridLayout::CreateAndInstall(this); SetLayoutManager(layout); for (const std::unique_ptr<PasswordFormRow>& row : password_forms_rows_) { if (row != password_forms_rows_[0])
diff --git a/chrome/browser/ui/views/passwords/manage_passwords_bubble_view.cc b/chrome/browser/ui/views/passwords/manage_passwords_bubble_view.cc index d63a8f9..2ad4be4 100644 --- a/chrome/browser/ui/views/passwords/manage_passwords_bubble_view.cc +++ b/chrome/browser/ui/views/passwords/manage_passwords_bubble_view.cc
@@ -330,9 +330,8 @@ } void ManagePasswordsBubbleView::PendingView::CreateAndSetLayout() { - views::GridLayout* layout = new views::GridLayout(this); + views::GridLayout* layout = views::GridLayout::CreateAndInstall(this); layout->set_minimum_size(gfx::Size(kDesiredBubbleWidth, 0)); - SetLayoutManager(layout); // Create the edit, save and never buttons. if (!edit_button_ && @@ -494,9 +493,8 @@ ManagePasswordsBubbleView::ManageView::ManageView( ManagePasswordsBubbleView* parent) : parent_(parent) { - views::GridLayout* layout = new views::GridLayout(this); + views::GridLayout* layout = views::GridLayout::CreateAndInstall(this); layout->set_minimum_size(gfx::Size(kDesiredBubbleWidth, 0)); - SetLayoutManager(layout); BuildColumnSet(layout, SINGLE_VIEW_COLUMN_SET); layout->StartRow(0, SINGLE_VIEW_COLUMN_SET); @@ -587,9 +585,8 @@ ManagePasswordsBubbleView::SaveConfirmationView::SaveConfirmationView( ManagePasswordsBubbleView* parent) : parent_(parent) { - views::GridLayout* layout = new views::GridLayout(this); + views::GridLayout* layout = views::GridLayout::CreateAndInstall(this); layout->set_minimum_size(gfx::Size(kDesiredBubbleWidth, 0)); - SetLayoutManager(layout); views::StyledLabel* confirmation = new views::StyledLabel(parent_->model()->save_confirmation_text(), this); @@ -661,9 +658,8 @@ ManagePasswordsBubbleView::SignInPromoView::SignInPromoView( ManagePasswordsBubbleView* parent) : parent_(parent) { - views::GridLayout* layout = new views::GridLayout(this); + views::GridLayout* layout = views::GridLayout::CreateAndInstall(this); layout->set_minimum_size(gfx::Size(kDesiredBubbleWidth, 0)); - SetLayoutManager(layout); signin_button_ = views::MdTextButton::CreateSecondaryUiBlueButton( this, @@ -729,9 +725,8 @@ ManagePasswordsBubbleView* parent) : parent_(parent), selection_view_(nullptr) { ChromeLayoutProvider* layout_provider = ChromeLayoutProvider::Get(); - views::GridLayout* layout = new views::GridLayout(this); + views::GridLayout* layout = views::GridLayout::CreateAndInstall(this); layout->set_minimum_size(gfx::Size(kDesiredBubbleWidth, 0)); - SetLayoutManager(layout); // Credential row. if (parent->model()->ShouldShowMultipleAccountUpdateUI()) {
diff --git a/chrome/browser/ui/views/payments/credit_card_editor_view_controller.cc b/chrome/browser/ui/views/payments/credit_card_editor_view_controller.cc index ae740ce5..2ba676f 100644 --- a/chrome/browser/ui/views/payments/credit_card_editor_view_controller.cc +++ b/chrome/browser/ui/views/payments/credit_card_editor_view_controller.cc
@@ -293,8 +293,8 @@ view = std::move(exp_label); } else { // Two comboboxes, one for month and the other for year. - std::unique_ptr<views::GridLayout> combobox_layout = - base::MakeUnique<views::GridLayout>(view.get()); + views::GridLayout* combobox_layout = + views::GridLayout::CreateAndInstall(view.get()); views::ColumnSet* columns = combobox_layout->AddColumnSet(0); columns->AddColumn(views::GridLayout::LEADING, views::GridLayout::CENTER, 1, views::GridLayout::USE_PREF, 0, 0); @@ -328,7 +328,6 @@ combobox_layout->AddView(year_combobox.release(), 1, 1, views::GridLayout::FILL, views::GridLayout::FILL, 0, kInputFieldHeight); - view->SetLayoutManager(combobox_layout.release()); } // Set the initial validity of the custom view.
diff --git a/chrome/browser/ui/views/payments/cvc_unmask_view_controller.cc b/chrome/browser/ui/views/payments/cvc_unmask_view_controller.cc index 27a11bc..c75d125 100644 --- a/chrome/browser/ui/views/payments/cvc_unmask_view_controller.cc +++ b/chrome/browser/ui/views/payments/cvc_unmask_view_controller.cc
@@ -155,8 +155,7 @@ } void CvcUnmaskViewController::FillContentView(views::View* content_view) { - std::unique_ptr<views::GridLayout> layout = - base::MakeUnique<views::GridLayout>(content_view); + views::GridLayout* layout = views::GridLayout::CreateAndInstall(content_view); content_view->SetBorder(views::CreateEmptyBorder( kPaymentRequestRowVerticalInsets, kPaymentRequestRowHorizontalInsets, kPaymentRequestRowVerticalInsets, kPaymentRequestRowHorizontalInsets)); @@ -272,8 +271,6 @@ error_label->SetVisible(false); layout->AddView(error_label.release()); - - content_view->SetLayoutManager(layout.release()); } std::unique_ptr<views::Button> CvcUnmaskViewController::CreatePrimaryButton() {
diff --git a/chrome/browser/ui/views/payments/editor_view_controller.cc b/chrome/browser/ui/views/payments/editor_view_controller.cc index 1c83e1d..cdb7c7d 100644 --- a/chrome/browser/ui/views/payments/editor_view_controller.cc +++ b/chrome/browser/ui/views/payments/editor_view_controller.cc
@@ -251,8 +251,8 @@ constexpr int kShortFieldMinimumWidth = 176; constexpr int kLongFieldMinimumWidth = 272; - std::unique_ptr<views::GridLayout> editor_layout = - base::MakeUnique<views::GridLayout>(editor_view.get()); + views::GridLayout* editor_layout = + views::GridLayout::CreateAndInstall(editor_view.get()); // Column set for short fields. views::ColumnSet* columns_short = editor_layout->AddColumnSet(0); columns_short->AddColumn(views::GridLayout::LEADING, @@ -307,7 +307,7 @@ for (const auto& field : GetFieldDefinitions()) { bool valid = false; views::View* focusable_field = - CreateInputField(editor_layout.get(), field, &valid); + CreateInputField(editor_layout, field, &valid); if (!first_field) first_field = focusable_field; if (!initial_focus_field_view_ && !valid) @@ -331,8 +331,6 @@ l10n_util::GetStringUTF16(IDS_PAYMENTS_REQUIRED_FIELD_MESSAGE)) .release()); - editor_view->SetLayoutManager(editor_layout.release()); - return editor_view; }
diff --git a/chrome/browser/ui/views/payments/order_summary_view_controller.cc b/chrome/browser/ui/views/payments/order_summary_view_controller.cc index 0c16806..334700a3 100644 --- a/chrome/browser/ui/views/payments/order_summary_view_controller.cc +++ b/chrome/browser/ui/views/payments/order_summary_view_controller.cc
@@ -57,8 +57,7 @@ ui::NativeTheme::kColorId_SeparatorColor), row_insets)); - views::GridLayout* layout = new views::GridLayout(row.get()); - row->SetLayoutManager(layout); + views::GridLayout* layout = views::GridLayout::CreateAndInstall(row.get()); views::ColumnSet* columns = layout->AddColumnSet(0); // The first column has resize_percent = 1 so that it stretches all the way @@ -96,7 +95,7 @@ std::unique_ptr<views::View> amount_wrapper = base::MakeUnique<views::View>(); views::GridLayout* wrapper_layout = - new views::GridLayout(amount_wrapper.get()); + views::GridLayout::CreateAndInstall(amount_wrapper.get()); views::ColumnSet* wrapper_columns = wrapper_layout->AddColumnSet(0); wrapper_columns->AddColumn(views::GridLayout::LEADING, views::GridLayout::CENTER, 0, @@ -109,7 +108,6 @@ currency_text->set_id(static_cast<int>(currency_label_id)); wrapper_layout->AddView(currency_text.release()); wrapper_layout->AddView(amount_text.release()); - amount_wrapper->SetLayoutManager(wrapper_layout); layout->AddView(label_text.release()); layout->AddView(amount_wrapper.release());
diff --git a/chrome/browser/ui/views/payments/payment_request_dialog_view.cc b/chrome/browser/ui/views/payments/payment_request_dialog_view.cc index ca2e17c..a547962 100644 --- a/chrome/browser/ui/views/payments/payment_request_dialog_view.cc +++ b/chrome/browser/ui/views/payments/payment_request_dialog_view.cc
@@ -344,8 +344,8 @@ // would be under it. throbber_overlay_.SetBackground(views::CreateSolidBackground(SK_ColorWHITE)); - std::unique_ptr<views::GridLayout> layout = - base::MakeUnique<views::GridLayout>(&throbber_overlay_); + views::GridLayout* layout = + views::GridLayout::CreateAndInstall(&throbber_overlay_); views::ColumnSet* throbber_columns = layout->AddColumnSet(0); throbber_columns->AddPaddingColumn(0.5, 0); throbber_columns->AddColumn(views::GridLayout::Alignment::CENTER, @@ -367,7 +367,6 @@ layout->AddView(new views::Label( l10n_util::GetStringUTF16(IDS_PAYMENTS_PROCESSING_MESSAGE))); - throbber_overlay_.SetLayoutManager(layout.release()); AddChildView(&throbber_overlay_); }
diff --git a/chrome/browser/ui/views/payments/payment_request_item_list.cc b/chrome/browser/ui/views/payments/payment_request_item_list.cc index d55367a..e476ce94 100644 --- a/chrome/browser/ui/views/payments/payment_request_item_list.cc +++ b/chrome/browser/ui/views/payments/payment_request_item_list.cc
@@ -63,8 +63,7 @@ std::unique_ptr<views::View> content = CreateContentView(&accessible_item_description_); - views::GridLayout* layout = new views::GridLayout(this); - SetLayoutManager(layout); + views::GridLayout* layout = views::GridLayout::CreateAndInstall(this); // Add a column for the item's content view. views::ColumnSet* columns = layout->AddColumnSet(0);
diff --git a/chrome/browser/ui/views/payments/payment_request_sheet_controller.cc b/chrome/browser/ui/views/payments/payment_request_sheet_controller.cc index 07436a9..7c7b96f 100644 --- a/chrome/browser/ui/views/payments/payment_request_sheet_controller.cc +++ b/chrome/browser/ui/views/payments/payment_request_sheet_controller.cc
@@ -218,8 +218,7 @@ // layer) won't do proper clipping. view->SetPaintToLayer(); - views::GridLayout* layout = new views::GridLayout(view.get()); - view->SetLayoutManager(layout); + views::GridLayout* layout = views::GridLayout::CreateAndInstall(view.get()); // Note: each view is responsible for its own padding (insets). views::ColumnSet* columns = layout->AddColumnSet(0); @@ -237,13 +236,12 @@ // otherwise it'll be sized to the ScrollView's viewport height, preventing // the scroll bar from ever being shown. pane_ = new views::View; - views::GridLayout* pane_layout = new views::GridLayout(pane_); + views::GridLayout* pane_layout = views::GridLayout::CreateAndInstall(pane_); views::ColumnSet* pane_columns = pane_layout->AddColumnSet(0); pane_columns->AddColumn(views::GridLayout::Alignment::FILL, views::GridLayout::Alignment::LEADING, 0, views::GridLayout::SizeType::FIXED, GetActualDialogWidth(), GetActualDialogWidth()); - pane_->SetLayoutManager(pane_layout); pane_layout->StartRow(0, 0); // This is owned by its parent. It's the container passed to FillContentView. content_view_ = new views::View; @@ -345,8 +343,8 @@ container->SetBorder( views::CreateEmptyBorder(kInset, kInset, kInset, kInset)); - views::GridLayout* layout = new views::GridLayout(container.get()); - container->SetLayoutManager(layout); + views::GridLayout* layout = + views::GridLayout::CreateAndInstall(container.get()); views::ColumnSet* columns = layout->AddColumnSet(0); columns->AddColumn(views::GridLayout::LEADING, views::GridLayout::CENTER,
diff --git a/chrome/browser/ui/views/payments/payment_request_views_util.cc b/chrome/browser/ui/views/payments/payment_request_views_util.cc index c79047e..de8452bf 100644 --- a/chrome/browser/ui/views/payments/payment_request_views_util.cc +++ b/chrome/browser/ui/views/payments/payment_request_views_util.cc
@@ -184,8 +184,8 @@ const base::string16& title, views::ButtonListener* listener) { std::unique_ptr<views::View> container = base::MakeUnique<views::View>(); - views::GridLayout* layout = new views::GridLayout(container.get()); - container->SetLayoutManager(layout); + views::GridLayout* layout = + views::GridLayout::CreateAndInstall(container.get()); constexpr int kHeaderTopVerticalInset = 14; constexpr int kHeaderBottomVerticalInset = 8;
diff --git a/chrome/browser/ui/views/payments/payment_sheet_view_controller.cc b/chrome/browser/ui/views/payments/payment_sheet_view_controller.cc index 1c4e99f..e9262b9 100644 --- a/chrome/browser/ui/views/payments/payment_sheet_view_controller.cc +++ b/chrome/browser/ui/views/payments/payment_sheet_view_controller.cc
@@ -150,7 +150,7 @@ kPaymentRequestRowVerticalInsets, trailing_inset); std::unique_ptr<PaymentRequestRowView> row = base::MakeUnique<PaymentRequestRowView>(listener, clickable, row_insets); - views::GridLayout* layout = new views::GridLayout(row.get()); + views::GridLayout* layout = views::GridLayout::CreateAndInstall(row.get()); row->SetLayoutManager(layout); views::ColumnSet* columns = layout->AddColumnSet(0); @@ -211,8 +211,8 @@ bool bold) { std::unique_ptr<views::View> item_amount_line = base::MakeUnique<views::View>(); - std::unique_ptr<views::GridLayout> item_amount_layout = - base::MakeUnique<views::GridLayout>(item_amount_line.get()); + views::GridLayout* item_amount_layout = + views::GridLayout::CreateAndInstall(item_amount_line.get()); views::ColumnSet* item_amount_columns = item_amount_layout->AddColumnSet(0); item_amount_columns->AddColumn(views::GridLayout::LEADING, views::GridLayout::LEADING, 0, @@ -240,7 +240,6 @@ item_amount_layout->AddView(currency_label.release()); item_amount_layout->AddView(amount_label.release()); - item_amount_line->SetLayoutManager(item_amount_layout.release()); return item_amount_line; } @@ -413,7 +412,7 @@ } void PaymentSheetViewController::FillContentView(views::View* content_view) { - views::GridLayout* layout = new views::GridLayout(content_view); + views::GridLayout* layout = views::GridLayout::CreateAndInstall(content_view); content_view->SetLayoutManager(layout); views::ColumnSet* columns = layout->AddColumnSet(0); columns->AddColumn(views::GridLayout::FILL, views::GridLayout::CENTER, 1, @@ -563,8 +562,8 @@ std::unique_ptr<PaymentRequestRowView> PaymentSheetViewController::CreatePaymentSheetSummaryRow() { std::unique_ptr<views::View> inline_summary = base::MakeUnique<views::View>(); - std::unique_ptr<views::GridLayout> layout = - base::MakeUnique<views::GridLayout>(inline_summary.get()); + views::GridLayout* layout = + views::GridLayout::CreateAndInstall(inline_summary.get()); views::ColumnSet* columns = layout->AddColumnSet(0); columns->AddColumn(views::GridLayout::LEADING, views::GridLayout::LEADING, 1, views::GridLayout::USE_PREF, 0, 0); @@ -636,8 +635,6 @@ false, true) .release()); - inline_summary->SetLayoutManager(layout.release()); - PaymentSheetRowBuilder builder( this, l10n_util::GetStringUTF16(IDS_PAYMENTS_ORDER_SUMMARY_LABEL)); builder.Tag(PaymentSheetViewControllerTags::SHOW_ORDER_SUMMARY_BUTTON) @@ -731,7 +728,8 @@ if (selected_instrument) { std::unique_ptr<views::View> content_view = base::MakeUnique<views::View>(); - views::GridLayout* layout = new views::GridLayout(content_view.get()); + views::GridLayout* layout = + views::GridLayout::CreateAndInstall(content_view.get()); content_view->SetLayoutManager(layout); views::ColumnSet* columns = layout->AddColumnSet(0); columns->AddColumn(views::GridLayout::LEADING, views::GridLayout::CENTER, 1,
diff --git a/chrome/browser/ui/views/profiles/forced_reauthentication_dialog_view.cc b/chrome/browser/ui/views/profiles/forced_reauthentication_dialog_view.cc index 997d433..b15420e 100644 --- a/chrome/browser/ui/views/profiles/forced_reauthentication_dialog_view.cc +++ b/chrome/browser/ui/views/profiles/forced_reauthentication_dialog_view.cc
@@ -208,7 +208,7 @@ provider->GetInsetsMetric(views::INSETS_DIALOG_CONTENTS); SetBorder(views::CreateEmptyBorder(dialog_insets.top(), 0, dialog_insets.bottom(), 0)); - views::GridLayout* dialog_layout = new views::GridLayout(this); + views::GridLayout* dialog_layout = views::GridLayout::CreateAndInstall(this); SetLayoutManager(dialog_layout); // Use a column set with no padding.
diff --git a/chrome/browser/ui/views/profiles/profile_chooser_view.cc b/chrome/browser/ui/views/profiles/profile_chooser_view.cc index 76f0f4b9..7920a6a2 100644 --- a/chrome/browser/ui/views/profiles/profile_chooser_view.cc +++ b/chrome/browser/ui/views/profiles/profile_chooser_view.cc
@@ -124,8 +124,7 @@ // Creates a GridLayout with a single column. This ensures that all the child // views added get auto-expanded to fill the full width of the bubble. views::GridLayout* CreateSingleColumnLayout(views::View* view, int width) { - views::GridLayout* layout = new views::GridLayout(view); - view->SetLayoutManager(layout); + views::GridLayout* layout = views::GridLayout::CreateAndInstall(view); views::ColumnSet* columns = layout->AddColumnSet(0); columns->AddColumn(views::GridLayout::FILL, views::GridLayout::FILL, 0, @@ -452,8 +451,8 @@ TitleCard* title_card, int width) { views::View* titled_view = new views::View(); - views::GridLayout* layout = new views::GridLayout(titled_view); - titled_view->SetLayoutManager(layout); + views::GridLayout* layout = + views::GridLayout::CreateAndInstall(titled_view); ChromeLayoutProvider* provider = ChromeLayoutProvider::Get(); const gfx::Insets dialog_insets = @@ -1114,8 +1113,8 @@ // Container for the profile photo and avatar/user name. BackgroundColorHoverButton* current_profile_card = new BackgroundColorHoverButton(this, base::string16()); - views::GridLayout* grid_layout = new views::GridLayout(current_profile_card); - current_profile_card->SetLayoutManager(grid_layout); + views::GridLayout* grid_layout = + views::GridLayout::CreateAndInstall(current_profile_card); views::ColumnSet* columns = grid_layout->AddColumnSet(0); // BackgroundColorHoverButton has already accounted for the left and right // margins.
diff --git a/chrome/browser/ui/views/proximity_auth/proximity_auth_error_bubble_view.cc b/chrome/browser/ui/views/proximity_auth/proximity_auth_error_bubble_view.cc index 223f1a2..5fc2d8d 100644 --- a/chrome/browser/ui/views/proximity_auth/proximity_auth_error_bubble_view.cc +++ b/chrome/browser/ui/views/proximity_auth/proximity_auth_error_bubble_view.cc
@@ -94,7 +94,7 @@ // ---------------------------- // | icon | padding | message | // ---------------------------- - std::unique_ptr<views::GridLayout> layout(new views::GridLayout(this)); + views::GridLayout* layout = views::GridLayout::CreateAndInstall(this); views::ColumnSet* columns = layout->AddColumnSet(0); columns->AddColumn(views::GridLayout::LEADING, views::GridLayout::LEADING, 0, views::GridLayout::USE_PREF, 0, 0); @@ -120,7 +120,6 @@ layout->StartRow(0, 0); layout->AddView(warning_icon.release()); layout->AddView(label.release()); - SetLayoutManager(layout.release()); } ProximityAuthErrorBubbleView::~ProximityAuthErrorBubbleView() {}
diff --git a/chrome/browser/ui/views/sad_tab_view.cc b/chrome/browser/ui/views/sad_tab_view.cc index 93e78f2f..493763b6 100644 --- a/chrome/browser/ui/views/sad_tab_view.cc +++ b/chrome/browser/ui/views/sad_tab_view.cc
@@ -55,8 +55,7 @@ SetBackground(views::CreateThemedSolidBackground( this, ui::NativeTheme::kColorId_DialogBackground)); - views::GridLayout* layout = new views::GridLayout(this); - SetLayoutManager(layout); + views::GridLayout* layout = views::GridLayout::CreateAndInstall(this); const int column_set_id = 0; views::ColumnSet* columns = layout->AddColumnSet(column_set_id);
diff --git a/chrome/browser/ui/views/safe_browsing/password_reuse_modal_warning_dialog.cc b/chrome/browser/ui/views/safe_browsing/password_reuse_modal_warning_dialog.cc index a85c3dca..21bfc08c 100644 --- a/chrome/browser/ui/views/safe_browsing/password_reuse_modal_warning_dialog.cc +++ b/chrome/browser/ui/views/safe_browsing/password_reuse_modal_warning_dialog.cc
@@ -7,6 +7,7 @@ #include "base/strings/utf_string_conversions.h" #include "build/build_config.h" #include "chrome/app/vector_icons/vector_icons.h" +#include "chrome/browser/ui/views/harmony/chrome_layout_provider.h" #include "chrome/browser/ui/views/harmony/chrome_typography.h" #include "components/constrained_window/constrained_window_views.h" #include "components/strings/grit/components_strings.h" @@ -36,8 +37,11 @@ : show_softer_warning_( PasswordProtectionService::ShouldShowSofterWarning()), done_callback_(std::move(done_callback)) { + set_margins(ChromeLayoutProvider::Get()->GetInsetsMetric( + views::INSETS_DIALOG_CONTENTS)); + // TODO(jialiul): Dialog message should align with title. - views::GridLayout* layout = views::GridLayout::CreatePanel(this); + views::GridLayout* layout = views::GridLayout::CreateAndInstall(this); views::ColumnSet* column_set = layout->AddColumnSet(0); column_set->AddColumn(views::GridLayout::FILL, views::GridLayout::FILL, 1, views::GridLayout::FIXED, 400, 0);
diff --git a/chrome/browser/ui/views/session_crashed_bubble_view.cc b/chrome/browser/ui/views/session_crashed_bubble_view.cc index 876d5ba..223d5c6 100644 --- a/chrome/browser/ui/views/session_crashed_bubble_view.cc +++ b/chrome/browser/ui/views/session_crashed_bubble_view.cc
@@ -247,7 +247,7 @@ // Create a view to hold the checkbox and the text. views::View* uma_view = new views::View(); - GridLayout* uma_layout = new GridLayout(uma_view); + GridLayout* uma_layout = GridLayout::CreateAndInstall(uma_view); uma_view->SetLayoutManager(uma_layout); const int kReportColumnSetId = 0;
diff --git a/chrome/browser/ui/views/sync/one_click_signin_dialog_view.cc b/chrome/browser/ui/views/sync/one_click_signin_dialog_view.cc index 3caf029f..1f22a9e 100644 --- a/chrome/browser/ui/views/sync/one_click_signin_dialog_view.cc +++ b/chrome/browser/ui/views/sync/one_click_signin_dialog_view.cc
@@ -10,6 +10,7 @@ #include "base/logging.h" #include "chrome/browser/ui/browser.h" #include "chrome/browser/ui/browser_dialogs.h" +#include "chrome/browser/ui/views/harmony/chrome_layout_provider.h" #include "chrome/common/url_constants.h" #include "chrome/grit/chromium_strings.h" #include "chrome/grit/generated_resources.h" @@ -74,6 +75,8 @@ advanced_link_(nullptr), learn_more_link_(nullptr) { DCHECK(!start_sync_callback_.is_null()); + set_margins(ChromeLayoutProvider::Get()->GetInsetsMetric( + views::INSETS_DIALOG_CONTENTS)); chrome::RecordDialogCreation(chrome::DialogIdentifier::ONE_CLICK_SIGNIN); } @@ -85,7 +88,7 @@ } void OneClickSigninDialogView::Init() { - views::GridLayout* layout = views::GridLayout::CreatePanel(this); + views::GridLayout* layout = views::GridLayout::CreateAndInstall(this); // Column set for descriptive text and link. views::ColumnSet* cs = layout->AddColumnSet(0);
diff --git a/chrome/browser/ui/views/sync/profile_signin_confirmation_dialog_views.cc b/chrome/browser/ui/views/sync/profile_signin_confirmation_dialog_views.cc index cab51a4..0d3d229d 100644 --- a/chrome/browser/ui/views/sync/profile_signin_confirmation_dialog_views.cc +++ b/chrome/browser/ui/views/sync/profile_signin_confirmation_dialog_views.cc
@@ -14,6 +14,7 @@ #include "chrome/browser/ui/browser_navigator.h" #include "chrome/browser/ui/browser_navigator_params.h" #include "chrome/browser/ui/browser_window.h" +#include "chrome/browser/ui/views/harmony/chrome_layout_provider.h" #include "chrome/grit/chromium_strings.h" #include "chrome/grit/generated_resources.h" #include "components/constrained_window/constrained_window_views.h" @@ -205,11 +206,14 @@ // insets. SetBorder(views::CreateEmptyBorder(content_insets.top(), 0, content_insets.bottom(), 0)); - views::GridLayout* dialog_layout = new views::GridLayout(this); - SetLayoutManager(dialog_layout); + views::GridLayout* dialog_layout = views::GridLayout::CreateAndInstall(this); // Use GridLayout inside the prompt bar because StyledLabel requires it. - views::GridLayout* prompt_layout = views::GridLayout::CreatePanel(prompt_bar); + views::GridLayout* prompt_layout = + views::GridLayout::CreateAndInstall(prompt_bar); + prompt_bar->SetBorder( + views::CreateEmptyBorder(ChromeLayoutProvider::Get()->GetInsetsMetric( + views::INSETS_DIALOG_CONTENTS))); constexpr int kPromptBarColumnSetId = 0; prompt_layout->AddColumnSet(kPromptBarColumnSetId) ->AddColumn(views::GridLayout::FILL, views::GridLayout::CENTER, 100,
diff --git a/chrome/browser/ui/views/translate/translate_bubble_view.cc b/chrome/browser/ui/views/translate/translate_bubble_view.cc index e1a0a5f81..1b338b9 100644 --- a/chrome/browser/ui/views/translate/translate_bubble_view.cc +++ b/chrome/browser/ui/views/translate/translate_bubble_view.cc
@@ -544,8 +544,7 @@ } views::View* view = new views::View(); - views::GridLayout* layout = new views::GridLayout(view); - view->SetLayoutManager(layout); + views::GridLayout* layout = views::GridLayout::CreateAndInstall(view); using views::GridLayout; @@ -673,8 +672,7 @@ l10n_util::GetStringUTF16(IDS_TRANSLATE_BUBBLE_TRANSLATING)); views::View* view = new views::View(); - views::GridLayout* layout = new views::GridLayout(view); - view->SetLayoutManager(layout); + views::GridLayout* layout = views::GridLayout::CreateAndInstall(view); using views::GridLayout; @@ -725,8 +723,7 @@ l10n_util::GetStringUTF16(IDS_TRANSLATE_BUBBLE_TRANSLATED)); views::View* view = new views::View(); - views::GridLayout* layout = new views::GridLayout(view); - view->SetLayoutManager(layout); + views::GridLayout* layout = views::GridLayout::CreateAndInstall(view); using views::GridLayout; @@ -776,8 +773,7 @@ l10n_util::GetStringUTF16(IDS_TRANSLATE_BUBBLE_COULD_NOT_TRANSLATE)); views::View* view = new views::View(); - views::GridLayout* layout = new views::GridLayout(view); - view->SetLayoutManager(layout); + views::GridLayout* layout = views::GridLayout::CreateAndInstall(view); using views::GridLayout; @@ -857,8 +853,7 @@ } views::View* view = new views::View(); - views::GridLayout* layout = new views::GridLayout(view); - view->SetLayoutManager(layout); + views::GridLayout* layout = views::GridLayout::CreateAndInstall(view); using views::GridLayout;
diff --git a/chrome/browser/ui/views/try_chrome_dialog.cc b/chrome/browser/ui/views/try_chrome_dialog.cc index b4687b02..7b4ce00 100644 --- a/chrome/browser/ui/views/try_chrome_dialog.cc +++ b/chrome/browser/ui/views/try_chrome_dialog.cc
@@ -9,6 +9,7 @@ #include "base/logging.h" #include "base/strings/string16.h" #include "chrome/app/vector_icons/vector_icons.h" +#include "chrome/browser/ui/views/harmony/chrome_layout_provider.h" #include "chrome/browser/ui/views/harmony/chrome_typography.h" #include "chrome/grit/chromium_strings.h" #include "chrome/grit/generated_resources.h" @@ -156,7 +157,10 @@ views::View* root_view = popup_->GetRootView(); root_view->SetBackground(views::CreateSolidBackground(kBackgroundColor)); - views::GridLayout* layout = views::GridLayout::CreatePanel(root_view); + views::GridLayout* layout = views::GridLayout::CreateAndInstall(root_view); + root_view->SetBorder( + views::CreateEmptyBorder(ChromeLayoutProvider::Get()->GetInsetsMetric( + views::INSETS_DIALOG_CONTENTS))); layout->set_minimum_size(gfx::Size(kToastWidth, 0)); views::ColumnSet* columns;
diff --git a/chrome/browser/ui/views/uninstall_view.cc b/chrome/browser/ui/views/uninstall_view.cc index 5569240bb..ad6b948 100644 --- a/chrome/browser/ui/views/uninstall_view.cc +++ b/chrome/browser/ui/views/uninstall_view.cc
@@ -30,6 +30,8 @@ browsers_combo_(NULL), user_selection_(*user_selection), quit_closure_(quit_closure) { + set_margins(ChromeLayoutProvider::Get()->GetInsetsMetric( + views::INSETS_DIALOG_CONTENTS)); SetupControls(); } @@ -45,7 +47,7 @@ using views::ColumnSet; using views::GridLayout; - GridLayout* layout = GridLayout::CreatePanel(this); + GridLayout* layout = GridLayout::CreateAndInstall(this); // Message to confirm uninstallation. int column_set_id = 0;
diff --git a/chrome/browser/ui/webui/signin/user_manager_screen_handler.cc b/chrome/browser/ui/webui/signin/user_manager_screen_handler.cc index 2d2b383..f66590d 100644 --- a/chrome/browser/ui/webui/signin/user_manager_screen_handler.cc +++ b/chrome/browser/ui/webui/signin/user_manager_screen_handler.cc
@@ -848,6 +848,13 @@ localized_strings->SetString("browseAsGuestAllProfilesLockedError", l10n_util::GetStringUTF16( IDS_USER_MANAGER_GO_GUEST_PROFILES_LOCKED_ERROR)); + + base::string16 prompt_message; + if (signin_util::IsForceSigninEnabled()) { + prompt_message = l10n_util::GetStringUTF16(IDS_USER_MANAGER_PROMPT_MESSAGE); + } + + localized_strings->SetString("userManagerPromptMessage", prompt_message); } void UserManagerScreenHandler::SendUserList() {
diff --git a/chrome/browser/vr/ui_scene_manager.cc b/chrome/browser/vr/ui_scene_manager.cc index 784b907..ebdfa632 100644 --- a/chrome/browser/vr/ui_scene_manager.cc +++ b/chrome/browser/vr/ui_scene_manager.cc
@@ -758,8 +758,13 @@ // TODO(vollick): it would be nice if ceiling, floor and the grid were // UiElement subclasses and could respond to the OnSetMode signal. for (Rect* panel : background_panels_) { - panel->SetCenterColor(color_scheme().world_background); - panel->SetEdgeColor(color_scheme().world_background); + if (showing_web_vr_splash_screen_) { + panel->SetCenterColor(color_scheme().splash_screen_background); + panel->SetEdgeColor(color_scheme().splash_screen_background); + } else { + panel->SetCenterColor(color_scheme().world_background); + panel->SetEdgeColor(color_scheme().world_background); + } } ceiling_->SetCenterColor(color_scheme().ceiling); ceiling_->SetEdgeColor(color_scheme().world_background);
diff --git a/chrome/browser/vr/ui_scene_manager_unittest.cc b/chrome/browser/vr/ui_scene_manager_unittest.cc index 9169d135..05c9bd9f 100644 --- a/chrome/browser/vr/ui_scene_manager_unittest.cc +++ b/chrome/browser/vr/ui_scene_manager_unittest.cc
@@ -8,6 +8,7 @@ #include "base/message_loop/message_loop.h" #include "base/test/scoped_mock_time_message_loop_task_runner.h" #include "cc/base/math_util.h" +#include "chrome/browser/vr/color_scheme.h" #include "chrome/browser/vr/elements/ui_element.h" #include "chrome/browser/vr/elements/ui_element_debug_id.h" #include "chrome/browser/vr/target_property.h" @@ -238,8 +239,8 @@ // WebVR frame is not received. auto initial_elements = kBackgroundElements; initial_elements.insert(kSplashScreenText); - VerifyElementsVisible("Initial", initial_elements); + manager_->OnWebVrFrameAvailable(); VerifyElementsVisible( "Autopresented", std::set<UiElementDebugId>{ @@ -267,6 +268,9 @@ auto initial_elements = kBackgroundElements; initial_elements.insert(kSplashScreenText); VerifyElementsVisible("Initial", initial_elements); + EXPECT_EQ(ColorScheme::GetColorScheme(ColorScheme::kModeNormal) + .splash_screen_background, + GetBackgroundColor()); // Enter WebVR with autopresentation. manager_->SetWebVrMode(true, false);
diff --git a/chrome/common/crash_keys.cc b/chrome/common/crash_keys.cc index dab44ad..1b33498 100644 --- a/chrome/common/crash_keys.cc +++ b/chrome/common/crash_keys.cc
@@ -208,7 +208,6 @@ // TODO(asvitkine): Remove after fixing https://crbug.com/736675 {"bad_histogram", kMediumSize}, - {"from_location", kMediumSize}, }; // This dynamic set of keys is used for sets of key value pairs when gathering
diff --git a/chrome/profiling/json_exporter_unittest.cc b/chrome/profiling/json_exporter_unittest.cc index 48d1cf2..111266a4 100644 --- a/chrome/profiling/json_exporter_unittest.cc +++ b/chrome/profiling/json_exporter_unittest.cc
@@ -11,6 +11,7 @@ #include "base/json/json_writer.h" #include "base/process/process.h" #include "base/values.h" +#include "build/build_config.h" #include "chrome/profiling/backtrace_storage.h" #include "services/resource_coordinator/public/cpp/memory_instrumentation/os_metrics.h" #include "testing/gtest/include/gtest/gtest.h" @@ -93,7 +94,8 @@ } // namespace -TEST(ProfilingJsonExporterTest, Simple) { +// Test failing. crbug.com/759176 +TEST(ProfilingJsonExporterTest, DISABLED_Simple) { BacktraceStorage backtrace_storage; std::vector<Address> stack1;
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn index e91cbfed..d73588ba 100644 --- a/chrome/test/BUILD.gn +++ b/chrome/test/BUILD.gn
@@ -2032,7 +2032,7 @@ "../browser/chromeos/input_method/textinput_test_helper.cc", "../browser/chromeos/input_method/textinput_test_helper.h", "../browser/ui/ash/accelerator_commands_browsertest.cc", - "../browser/ui/ash/app_list/app_list_controller_ash_browsertest.cc", + "../browser/ui/ash/app_list/app_list_browsertest.cc", "../browser/ui/ash/chrome_new_window_client_browsertest.cc", "../browser/ui/ash/chrome_screenshot_grabber_browsertest.cc", "../browser/ui/ash/keyboard_controller_browsertest.cc", @@ -4717,6 +4717,7 @@ "../browser/ui/cocoa/notifications/notification_builder_mac_unittest.mm", "../browser/ui/cocoa/notifications/notification_response_builder_mac_unittest.mm", "../browser/ui/cocoa/nsmenuitem_additions_unittest.mm", + "../browser/ui/cocoa/nsview_additions_unittest.mm", "../browser/ui/cocoa/omnibox/omnibox_popup_cell_unittest.mm", "../browser/ui/cocoa/omnibox/omnibox_popup_matrix_unittest.mm", "../browser/ui/cocoa/omnibox/omnibox_popup_separator_view_unittest.mm",
diff --git a/chromecast/crash/cast_crash_keys.cc b/chromecast/crash/cast_crash_keys.cc index b01dc265c..3223f442 100644 --- a/chromecast/crash/cast_crash_keys.cc +++ b/chromecast/crash/cast_crash_keys.cc
@@ -73,10 +73,6 @@ // Temporary for https://crbug.com/685996. {"user-cloud-policy-manager-connect-trace", ::crash_keys::kMediumSize}, - - // TODO(bcwhite): Remove after fixing https://crbug.com/736675 - {"bad_histogram", ::crash_keys::kMediumSize}, - {"from_location", ::crash_keys::kMediumSize}, }; return base::debug::InitCrashKeys(fixed_keys, arraysize(fixed_keys),
diff --git a/chromeos/disks/disk_mount_manager_unittest.cc b/chromeos/disks/disk_mount_manager_unittest.cc index e60702d..baba418 100644 --- a/chromeos/disks/disk_mount_manager_unittest.cc +++ b/chromeos/disks/disk_mount_manager_unittest.cc
@@ -175,7 +175,8 @@ DEVICE_EVENT, // OnDeviceEvent() DISK_EVENT, // OnDiskEvent() FORMAT_EVENT, // OnFormatEvent() - MOUNT_EVENT // OnMountEvent() + MOUNT_EVENT, // OnMountEvent() + RENAME_EVENT // OnRenameEvent() }; // Represents every event notified to |DiskMountManager::Observer|. @@ -259,6 +260,30 @@ } }; +// Represents an invocation of |DiskMountManager::Observer::OnRenameEvent()|. +struct RenameEvent : public ObserverEvent { + DiskMountManager::RenameEvent event; + chromeos::RenameError error_code; + std::string device_path; + + RenameEvent(DiskMountManager::RenameEvent event, + chromeos::RenameError error_code, + const std::string& device_path) + : event(event), error_code(error_code), device_path(device_path) {} + + ObserverEventType type() const override { return RENAME_EVENT; } + + bool operator==(const RenameEvent& other) const { + return event == other.event && error_code == other.error_code && + device_path == other.device_path; + } + + std::string DebugString() const { + return StringPrintf("OnRenameEvent(%d, %d, %s)", event, error_code, + device_path.c_str()); + } +}; + // Represents an invocation of |DiskMountManager::Observer::OnMountEvent()|. struct MountEvent : public ObserverEvent { DiskMountManager::MountEvent event; @@ -325,8 +350,7 @@ void OnRenameEvent(DiskMountManager::RenameEvent event, chromeos::RenameError error_code, const std::string& device_path) override { - // TODO(klemenko): Add implementation when you will add unit tests for - // rename + events_.push_back(MakeUnique<RenameEvent>(event, error_code, device_path)); } void OnMountEvent( @@ -364,6 +388,14 @@ return static_cast<const FormatEvent&>(*events_[index]); } + // Verifies if the |index|th invocation is OnRenameEvent() and returns + // details. + const RenameEvent& GetRenameEvent(size_t index) { + DCHECK_GT(events_.size(), index); + DCHECK_EQ(RENAME_EVENT, events_[index]->type()); + return static_cast<const RenameEvent&>(*events_[index]); + } + // Verifies if the |index|th invocation is OnMountEvent() and returns details. const MountEvent& GetMountEvent(size_t index) { DCHECK_GT(events_.size(), index); @@ -405,6 +437,19 @@ return num_matched; } + // Counts the number of |RenameEvent| recorded so far that matches with + // |rename_event|. + size_t CountRenameEvents(const RenameEvent& exptected_rename_event) { + size_t num_matched = 0; + for (const auto& event : events_) { + if (event->type() != RENAME_EVENT) + continue; + if (static_cast<const RenameEvent&>(*event) == exptected_rename_event) + num_matched++; + } + return num_matched; + } + private: // Pointer to the manager object to which this |Observer| is registered. const DiskMountManager* manager_; @@ -964,4 +1009,351 @@ EXPECT_FALSE(observer_->GetMountEvent(1).disk->is_read_only()); } +// Tests that the observer gets notified on attempt to rename non existent mount +// point. +TEST_F(DiskMountManagerTest, Rename_NotMounted) { + DiskMountManager::GetInstance()->RenameMountedDevice("/mount/non_existent", + "MYUSB"); + ASSERT_EQ(1U, observer_->GetEventCount()); + EXPECT_EQ(RenameEvent(DiskMountManager::RENAME_COMPLETED, + chromeos::RENAME_ERROR_UNKNOWN, "/mount/non_existent"), + observer_->GetRenameEvent(0)); +} + +// Tests that the observer gets notified on attempt to rename read-only mount +// point. +TEST_F(DiskMountManagerTest, Rename_ReadOnly) { + DiskMountManager::GetInstance()->RenameMountedDevice(kReadOnlyDeviceMountPath, + "MYUSB"); + ASSERT_EQ(1U, observer_->GetEventCount()); + EXPECT_EQ(RenameEvent(DiskMountManager::RENAME_COMPLETED, + chromeos::RENAME_ERROR_DEVICE_NOT_ALLOWED, + kReadOnlyDeviceMountPath), + observer_->GetRenameEvent(0)); +} + +// Tests that it is not possible to rename archive mount point. +TEST_F(DiskMountManagerTest, Rename_Archive) { + DiskMountManager::GetInstance()->RenameMountedDevice("/archive/mount_path", + "MYUSB"); + ASSERT_EQ(1U, observer_->GetEventCount()); + EXPECT_EQ(RenameEvent(DiskMountManager::RENAME_COMPLETED, + chromeos::RENAME_ERROR_UNKNOWN, "/archive/source_path"), + observer_->GetRenameEvent(0)); +} + +// Tests that rename fails if the device cannot be unmounted. +TEST_F(DiskMountManagerTest, Rename_FailToUnmount) { + // Before renaming mounted device, the device should be unmounted. + // In this test unmount will fail, and there should be no attempt to + // rename the device. + + fake_cros_disks_client_->MakeUnmountFail(); + // Start test. + DiskMountManager::GetInstance()->RenameMountedDevice(kDevice1MountPath, + "MYUSB"); + + // Cros disks will respond asynchronoulsy, so let's drain the message loop. + base::RunLoop().RunUntilIdle(); + + // Observer should be notified that unmount attempt fails and rename task + // failed to start. + ASSERT_EQ(2U, observer_->GetEventCount()); + const MountEvent& mount_event = observer_->GetMountEvent(0); + EXPECT_EQ(DiskMountManager::UNMOUNTING, mount_event.event); + EXPECT_EQ(chromeos::MOUNT_ERROR_INTERNAL, mount_event.error_code); + EXPECT_EQ(kDevice1MountPath, mount_event.mount_point.mount_path); + + EXPECT_EQ(RenameEvent(DiskMountManager::RENAME_COMPLETED, + chromeos::RENAME_ERROR_UNKNOWN, kDevice1SourcePath), + observer_->GetRenameEvent(1)); + EXPECT_EQ(1, fake_cros_disks_client_->unmount_call_count()); + EXPECT_EQ(kDevice1MountPath, + fake_cros_disks_client_->last_unmount_device_path()); + EXPECT_EQ(chromeos::UNMOUNT_OPTIONS_NONE, + fake_cros_disks_client_->last_unmount_options()); + EXPECT_EQ(0, fake_cros_disks_client_->rename_call_count()); + + // The device mount should still be here. + EXPECT_TRUE(HasMountPoint(kDevice1MountPath)); +} + +// Tests that observer is notified when cros disks fails to start rename +// process. +TEST_F(DiskMountManagerTest, Rename_RenameFailsToStart) { + // Before renaming mounted device, the device should be unmounted. + // In this test, unmount will succeed, but call to Rename method will + // fail. + + fake_cros_disks_client_->MakeRenameFail(); + // Start the test. + DiskMountManager::GetInstance()->RenameMountedDevice(kDevice1MountPath, + "MYUSB"); + + // Cros disks will respond asynchronoulsy, so let's drain the message loop. + base::RunLoop().RunUntilIdle(); + + // Observer should be notified that the device was unmounted and rename task + // failed to start. + ASSERT_EQ(2U, observer_->GetEventCount()); + const MountEvent& mount_event = observer_->GetMountEvent(0); + EXPECT_EQ(DiskMountManager::UNMOUNTING, mount_event.event); + EXPECT_EQ(chromeos::MOUNT_ERROR_NONE, mount_event.error_code); + EXPECT_EQ(kDevice1MountPath, mount_event.mount_point.mount_path); + + EXPECT_EQ(RenameEvent(DiskMountManager::RENAME_COMPLETED, + chromeos::RENAME_ERROR_UNKNOWN, kDevice1SourcePath), + observer_->GetRenameEvent(1)); + + EXPECT_EQ(1, fake_cros_disks_client_->unmount_call_count()); + EXPECT_EQ(kDevice1MountPath, + fake_cros_disks_client_->last_unmount_device_path()); + EXPECT_EQ(chromeos::UNMOUNT_OPTIONS_NONE, + fake_cros_disks_client_->last_unmount_options()); + EXPECT_EQ(1, fake_cros_disks_client_->rename_call_count()); + EXPECT_EQ(kDevice1SourcePath, + fake_cros_disks_client_->last_rename_device_path()); + EXPECT_EQ("MYUSB", fake_cros_disks_client_->last_rename_volume_name()); + + // The device mount should be gone. + EXPECT_FALSE(HasMountPoint(kDevice1MountPath)); +} + +// Tests the case where there are two rename requests for the same device. +TEST_F(DiskMountManagerTest, Rename_ConcurrentRenameCalls) { + // Only the first rename request should be processed (the second unmount + // request fails because the device is already unmounted at that point). + // CrosDisksClient will report that the rename process for the first request + // is successfully started. + + fake_cros_disks_client_->set_unmount_listener( + base::Bind(&FakeCrosDisksClient::MakeUnmountFail, + base::Unretained(fake_cros_disks_client_))); + // Start the test. + DiskMountManager::GetInstance()->RenameMountedDevice(kDevice1MountPath, + "MYUSB1"); + DiskMountManager::GetInstance()->RenameMountedDevice(kDevice1MountPath, + "MYUSB2"); + + // Cros disks will respond asynchronoulsy, so let's drain the message loop. + base::RunLoop().RunUntilIdle(); + + // The observer should get a RENAME_STARTED event for one rename request and a + // RENAME_COMPLETED with an error code for the other rename request. The + // renaming will be started only for the first request. + // There should be only one UNMOUNTING event. The result of the second one + // should not be reported as the mount point will go away after the first + // request. + // + // Note that in this test the rename completion signal will not be simulated, + // so the observer should not get RENAME_COMPLETED signal. + + ASSERT_EQ(3U, observer_->GetEventCount()); + const MountEvent& mount_event = observer_->GetMountEvent(0); + EXPECT_EQ(DiskMountManager::UNMOUNTING, mount_event.event); + EXPECT_EQ(chromeos::MOUNT_ERROR_NONE, mount_event.error_code); + EXPECT_EQ(kDevice1MountPath, mount_event.mount_point.mount_path); + EXPECT_EQ(RenameEvent(DiskMountManager::RENAME_COMPLETED, + chromeos::RENAME_ERROR_UNKNOWN, kDevice1SourcePath), + observer_->GetRenameEvent(1)); + EXPECT_EQ(RenameEvent(DiskMountManager::RENAME_STARTED, + chromeos::RENAME_ERROR_NONE, kDevice1SourcePath), + observer_->GetRenameEvent(2)); + + EXPECT_EQ(2, fake_cros_disks_client_->unmount_call_count()); + EXPECT_EQ(kDevice1MountPath, + fake_cros_disks_client_->last_unmount_device_path()); + EXPECT_EQ(chromeos::UNMOUNT_OPTIONS_NONE, + fake_cros_disks_client_->last_unmount_options()); + EXPECT_EQ(1, fake_cros_disks_client_->rename_call_count()); + EXPECT_EQ(kDevice1SourcePath, + fake_cros_disks_client_->last_rename_device_path()); + EXPECT_EQ("MYUSB1", fake_cros_disks_client_->last_rename_volume_name()); + + // The device mount should be gone. + EXPECT_FALSE(HasMountPoint(kDevice1MountPath)); +} + +// Tests the case when the rename process actually starts and fails. +TEST_F(DiskMountManagerTest, Rename_RenameFails) { + // Both unmount and rename device calls are successful in this test. + + // Start the test. + DiskMountManager::GetInstance()->RenameMountedDevice(kDevice1MountPath, + "MYUSB"); + + // Wait for Unmount and Rename calls to end. + base::RunLoop().RunUntilIdle(); + + EXPECT_EQ(1, fake_cros_disks_client_->unmount_call_count()); + EXPECT_EQ(kDevice1MountPath, + fake_cros_disks_client_->last_unmount_device_path()); + EXPECT_EQ(chromeos::UNMOUNT_OPTIONS_NONE, + fake_cros_disks_client_->last_unmount_options()); + EXPECT_EQ(1, fake_cros_disks_client_->rename_call_count()); + EXPECT_EQ(kDevice1SourcePath, + fake_cros_disks_client_->last_rename_device_path()); + EXPECT_EQ("MYUSB", fake_cros_disks_client_->last_rename_volume_name()); + + // The device should be unmounted by now. + EXPECT_FALSE(HasMountPoint(kDevice1MountPath)); + + // Send failing RENAME_COMPLETED signal. + // The failure is marked by ! in fromt of the path (but this should change + // soon). + fake_cros_disks_client_->SendRenameCompletedEvent( + chromeos::RENAME_ERROR_UNKNOWN, kDevice1SourcePath); + + // The observer should get notified that the device was unmounted and that + // renaming has started. + // After the renaming starts, the test will simulate failing + // RENAME_COMPLETED signal, so the observer should also be notified the + // renaming has failed (RENAME_COMPLETED event). + ASSERT_EQ(3U, observer_->GetEventCount()); + VerifyMountEvent(observer_->GetMountEvent(0), DiskMountManager::UNMOUNTING, + chromeos::MOUNT_ERROR_NONE, kDevice1MountPath); + EXPECT_EQ(RenameEvent(DiskMountManager::RENAME_STARTED, + chromeos::RENAME_ERROR_NONE, kDevice1SourcePath), + observer_->GetRenameEvent(1)); + EXPECT_EQ(RenameEvent(DiskMountManager::RENAME_COMPLETED, + chromeos::RENAME_ERROR_UNKNOWN, kDevice1SourcePath), + observer_->GetRenameEvent(2)); +} + +// Tests the case when renaming completes successfully. +TEST_F(DiskMountManagerTest, Rename_RenameSuccess) { + DiskMountManager* manager = DiskMountManager::GetInstance(); + const DiskMountManager::DiskMap& disks = manager->disks(); + // Set up cros disks client mocks. + // Both unmount and rename device calls are successful in this test. + + // Start the test. + DiskMountManager::GetInstance()->RenameMountedDevice(kDevice1MountPath, + "MYUSB1"); + + // Wait for Unmount and Rename calls to end. + base::RunLoop().RunUntilIdle(); + + EXPECT_EQ(1, fake_cros_disks_client_->unmount_call_count()); + EXPECT_EQ(kDevice1MountPath, + fake_cros_disks_client_->last_unmount_device_path()); + EXPECT_EQ(chromeos::UNMOUNT_OPTIONS_NONE, + fake_cros_disks_client_->last_unmount_options()); + EXPECT_EQ(1, fake_cros_disks_client_->rename_call_count()); + EXPECT_EQ(kDevice1SourcePath, + fake_cros_disks_client_->last_rename_device_path()); + EXPECT_EQ("MYUSB1", fake_cros_disks_client_->last_rename_volume_name()); + + // The device should be unmounted by now. + EXPECT_FALSE(HasMountPoint(kDevice1MountPath)); + + // Simulate cros_disks reporting success. + fake_cros_disks_client_->SendRenameCompletedEvent(chromeos::RENAME_ERROR_NONE, + kDevice1SourcePath); + + // The observer should receive UNMOUNTING, RENAME_STARTED and RENAME_COMPLETED + // events (all of them without an error set). + ASSERT_EQ(3U, observer_->GetEventCount()); + VerifyMountEvent(observer_->GetMountEvent(0), DiskMountManager::UNMOUNTING, + chromeos::MOUNT_ERROR_NONE, kDevice1MountPath); + EXPECT_EQ(RenameEvent(DiskMountManager::RENAME_STARTED, + chromeos::RENAME_ERROR_NONE, kDevice1SourcePath), + observer_->GetRenameEvent(1)); + EXPECT_EQ(RenameEvent(DiskMountManager::RENAME_COMPLETED, + chromeos::RENAME_ERROR_NONE, kDevice1SourcePath), + observer_->GetRenameEvent(2)); + + // Disk should have new value for device label name + EXPECT_EQ("MYUSB1", disks.find(kDevice1SourcePath)->second->device_label()); +} + +// Tests that it's possible to rename the device twice in a row (this may not be +// true if the list of pending renames is not properly cleared). +TEST_F(DiskMountManagerTest, Rename_ConsecutiveRenameCalls) { + DiskMountManager* manager = DiskMountManager::GetInstance(); + const DiskMountManager::DiskMap& disks = manager->disks(); + // All unmount and rename device calls are successful in this test. + // Each of the should be made twice (once for each renaming task). + + // Start the test. + DiskMountManager::GetInstance()->RenameMountedDevice(kDevice1MountPath, + "MYUSB"); + + // Wait for Unmount and Rename calls to end. + base::RunLoop().RunUntilIdle(); + + EXPECT_EQ(1, fake_cros_disks_client_->unmount_call_count()); + EXPECT_EQ(kDevice1MountPath, + fake_cros_disks_client_->last_unmount_device_path()); + EXPECT_EQ(chromeos::UNMOUNT_OPTIONS_NONE, + fake_cros_disks_client_->last_unmount_options()); + EXPECT_EQ(1, fake_cros_disks_client_->rename_call_count()); + EXPECT_EQ(kDevice1SourcePath, + fake_cros_disks_client_->last_rename_device_path()); + EXPECT_EQ("MYUSB", fake_cros_disks_client_->last_rename_volume_name()); + EXPECT_EQ("", disks.find(kDevice1SourcePath)->second->base_mount_path()); + + // The device should be unmounted by now. + EXPECT_FALSE(HasMountPoint(kDevice1MountPath)); + + // Simulate cros_disks reporting success. + fake_cros_disks_client_->SendRenameCompletedEvent(chromeos::RENAME_ERROR_NONE, + kDevice1SourcePath); + + // Simulate the device remounting. + fake_cros_disks_client_->SendMountCompletedEvent( + chromeos::MOUNT_ERROR_NONE, kDevice1SourcePath, + chromeos::MOUNT_TYPE_DEVICE, kDevice1MountPath); + + EXPECT_TRUE(HasMountPoint(kDevice1MountPath)); + + auto previousMountPath = disks.find(kDevice1SourcePath)->second->mount_path(); + // Try renaming again. + DiskMountManager::GetInstance()->RenameMountedDevice(kDevice1MountPath, + "MYUSB2"); + + // Wait for Unmount and Rename calls to end. + base::RunLoop().RunUntilIdle(); + + EXPECT_EQ(2, fake_cros_disks_client_->unmount_call_count()); + EXPECT_EQ(kDevice1MountPath, + fake_cros_disks_client_->last_unmount_device_path()); + EXPECT_EQ(chromeos::UNMOUNT_OPTIONS_NONE, + fake_cros_disks_client_->last_unmount_options()); + EXPECT_EQ(2, fake_cros_disks_client_->rename_call_count()); + EXPECT_EQ(kDevice1SourcePath, + fake_cros_disks_client_->last_rename_device_path()); + EXPECT_EQ("MYUSB2", fake_cros_disks_client_->last_rename_volume_name()); + // Base mount path should be set to previous mount path. + EXPECT_EQ(previousMountPath, + disks.find(kDevice1SourcePath)->second->base_mount_path()); + + // Simulate cros_disks reporting success. + fake_cros_disks_client_->SendRenameCompletedEvent(chromeos::RENAME_ERROR_NONE, + kDevice1SourcePath); + + // The observer should receive UNMOUNTING, RENAME_STARTED and RENAME_COMPLETED + // events (all of them without an error set) twice (once for each renaming + // task). + // Also, there should be a MOUNTING event when the device remounting is + // simulated. + EXPECT_EQ(7U, observer_->GetEventCount()); + + EXPECT_EQ(2U, observer_->CountRenameEvents(RenameEvent( + DiskMountManager::RENAME_COMPLETED, + chromeos::RENAME_ERROR_NONE, kDevice1SourcePath))); + + EXPECT_EQ(2U, observer_->CountRenameEvents(RenameEvent( + DiskMountManager::RENAME_STARTED, + chromeos::RENAME_ERROR_NONE, kDevice1SourcePath))); + + EXPECT_EQ(2U, observer_->CountMountEvents(DiskMountManager::UNMOUNTING, + chromeos::MOUNT_ERROR_NONE, + kDevice1MountPath)); + + EXPECT_EQ(1U, observer_->CountMountEvents(DiskMountManager::MOUNTING, + chromeos::MOUNT_ERROR_NONE, + kDevice1MountPath)); +} + } // namespace
diff --git a/components/arc/BUILD.gn b/components/arc/BUILD.gn index 2634e8b..5708943 100644 --- a/components/arc/BUILD.gn +++ b/components/arc/BUILD.gn
@@ -7,8 +7,6 @@ static_library("arc") { sources = [ - "arc_util.cc", - "arc_util.h", "audio/arc_audio_bridge.cc", "audio/arc_audio_bridge.h", "bluetooth/arc_bluetooth_bridge.cc", @@ -113,6 +111,8 @@ "arc_session_runner.h", "arc_stop_reason.cc", "arc_stop_reason.h", + "arc_util.cc", + "arc_util.h", "instance_holder.h", ] @@ -123,6 +123,7 @@ "//components/signin/core/account_id", "//components/user_manager", "//mojo/edk/system", + "//ui/aura", ] public_deps = [
diff --git a/components/arc/arc_session_runner.cc b/components/arc/arc_session_runner.cc index 5401b22..ecdeff29 100644 --- a/components/arc/arc_session_runner.cc +++ b/components/arc/arc_session_runner.cc
@@ -8,6 +8,7 @@ #include "base/memory/ref_counted.h" #include "base/task_runner.h" #include "chromeos/dbus/dbus_thread_manager.h" +#include "components/arc/arc_util.h" namespace arc { @@ -236,6 +237,11 @@ } void ArcSessionRunner::EmitLoginPromptVisibleCalled() { + if (ShouldArcOnlyStartAfterLogin()) { + // Skip starting ARC for now. We'll have another chance to start the full + // instance after the user logs in. + return; + } // Since 'login-prompt-visible' Upstart signal starts all Upstart jobs the // container may depend on such as cras, EmitLoginPromptVisibleCalled() is the // safe place to start the container for login screen.
diff --git a/components/arc/arc_util.cc b/components/arc/arc_util.cc index 0ebc98c..83436fe 100644 --- a/components/arc/arc_util.cc +++ b/components/arc/arc_util.cc
@@ -33,6 +33,7 @@ constexpr char kAlwaysStart[] = "always-start"; constexpr char kAlwaysStartWithNoPlayStore[] = "always-start-with-no-play-store"; +constexpr char kOnlyStartAfterLogin[] = "only-start-after-login"; void SetArcCpuRestrictionCallback( login_manager::ContainerCpuRestrictionState state, @@ -106,6 +107,15 @@ play_store_available ? kAlwaysStart : kAlwaysStartWithNoPlayStore); } +bool ShouldArcOnlyStartAfterLogin() { + const auto* command_line = base::CommandLine::ForCurrentProcess(); + if (!command_line->HasSwitch(chromeos::switches::kArcStartMode)) + return false; + const std::string value = + command_line->GetSwitchValueASCII(chromeos::switches::kArcStartMode); + return value == kOnlyStartAfterLogin; +} + bool IsArcKioskAvailable() { const auto* command_line = base::CommandLine::ForCurrentProcess();
diff --git a/components/arc/arc_util.h b/components/arc/arc_util.h index 3214545..7558ac3 100644 --- a/components/arc/arc_util.h +++ b/components/arc/arc_util.h
@@ -51,6 +51,9 @@ // Store UI is added. void SetArcAlwaysStartForTesting(bool play_store_available); +// Returns true if ARC should only start after the user has logged in. +bool ShouldArcOnlyStartAfterLogin(); + // Returns true if ARC is installed and running ARC kiosk apps on the current // device is officially supported. // It doesn't follow that ARC is available for user sessions and
diff --git a/components/cronet/android/test/javatests/AndroidManifest.xml b/components/cronet/android/test/javatests/AndroidManifest.xml index e5e166655..8da4a5b7 100644 --- a/components/cronet/android/test/javatests/AndroidManifest.xml +++ b/components/cronet/android/test/javatests/AndroidManifest.xml
@@ -22,5 +22,10 @@ <instrumentation android:name="org.chromium.base.test.BaseChromiumAndroidJUnitRunner" android:targetPackage="org.chromium.net" android:label="Tests for org.chromium.net" - chromium-junit4="true" /> + chromium-junit4="true"> + <!-- Meta data used for BaseChromiumAndroidJUnitRunner to scan only + one package path for potential tests --> + <meta-data android:name="org.chromium.base.test.BaseChromiumAndroidJUnitRunner.TestListPackage" + android:value="org.chromium" /> + </instrumentation> </manifest>
diff --git a/components/metrics/metrics_log.cc b/components/metrics/metrics_log.cc index 412179394..02ac223e 100644 --- a/components/metrics/metrics_log.cc +++ b/components/metrics/metrics_log.cc
@@ -175,7 +175,7 @@ os->set_name(base::SysInfo::OperatingSystemName()); os->set_version(base::SysInfo::OperatingSystemVersion()); #if defined(OS_ANDROID) - os->set_fingerprint( + os->set_build_fingerprint( base::android::BuildInfo::GetInstance()->android_build_fp()); #endif }
diff --git a/components/metrics/metrics_log_unittest.cc b/components/metrics/metrics_log_unittest.cc index ca009f5..e3eb422 100644 --- a/components/metrics/metrics_log_unittest.cc +++ b/components/metrics/metrics_log_unittest.cc
@@ -154,7 +154,7 @@ system_profile->mutable_os()->set_version( base::SysInfo::OperatingSystemVersion()); #if defined(OS_ANDROID) - system_profile->mutable_os()->set_fingerprint( + system_profile->mutable_os()->set_build_fingerprint( base::android::BuildInfo::GetInstance()->android_build_fp()); #endif
diff --git a/components/metrics/proto/system_profile.proto b/components/metrics/proto/system_profile.proto index 5d1dd73..96203965 100644 --- a/components/metrics/proto/system_profile.proto +++ b/components/metrics/proto/system_profile.proto
@@ -89,7 +89,7 @@ optional string version = 2; // The fingerprint of the build. This field is used only on Android. - optional string fingerprint = 3; + optional string build_fingerprint = 3; // Whether the version of iOS appears to be "jailbroken". This field is // used only on iOS. Chrome for iOS detects whether device contains a
diff --git a/components/printing/renderer/print_render_frame_helper.cc b/components/printing/renderer/print_render_frame_helper.cc index bded6831..18469953 100644 --- a/components/printing/renderer/print_render_frame_helper.cc +++ b/components/printing/renderer/print_render_frame_helper.cc
@@ -1300,7 +1300,7 @@ const std::vector<int>& pages = print_pages_params_->pages; if (!print_preview_context_.CreatePreviewDocument( - prep_frame_view_.release(), pages, print_params.printed_doc_type)) { + std::move(prep_frame_view_), pages, print_params.printed_doc_type)) { return false; } @@ -2194,14 +2194,14 @@ } bool PrintRenderFrameHelper::PrintPreviewContext::CreatePreviewDocument( - PrepareFrameAndViewForPrint* prepared_frame, + std::unique_ptr<PrepareFrameAndViewForPrint> prepared_frame, const std::vector<int>& pages, SkiaDocumentType doc_type) { DCHECK_EQ(INITIALIZED, state_); state_ = RENDERING; // Need to make sure old object gets destroyed first. - prep_frame_view_.reset(prepared_frame); + prep_frame_view_ = std::move(prepared_frame); prep_frame_view_->StartPrinting(); total_page_count_ = prep_frame_view_->GetExpectedPageCount();
diff --git a/components/printing/renderer/print_render_frame_helper.h b/components/printing/renderer/print_render_frame_helper.h index 1922098b..6a52cd8b 100644 --- a/components/printing/renderer/print_render_frame_helper.h +++ b/components/printing/renderer/print_render_frame_helper.h
@@ -434,10 +434,10 @@ void OnPrintPreview(); // Create the print preview document. |pages| is empty to print all pages. - // Takes ownership of |prepared_frame|. - bool CreatePreviewDocument(PrepareFrameAndViewForPrint* prepared_frame, - const std::vector<int>& pages, - SkiaDocumentType doc_type); + bool CreatePreviewDocument( + std::unique_ptr<PrepareFrameAndViewForPrint> prepared_frame, + const std::vector<int>& pages, + SkiaDocumentType doc_type); // Called after a page gets rendered. |page_time| is how long the // rendering took.
diff --git a/components/viz/service/gl/gpu_service_impl.h b/components/viz/service/gl/gpu_service_impl.h index 9dd03c38..7d1d968 100644 --- a/components/viz/service/gl/gpu_service_impl.h +++ b/components/viz/service/gl/gpu_service_impl.h
@@ -169,8 +169,6 @@ gpu::GpuPreferences gpu_preferences_; - gpu::GpuDriverBugWorkarounds gpu_workarounds_; - // Information about the GPU, such as device and vendor ID. gpu::GPUInfo gpu_info_;
diff --git a/content/browser/browser_main_loop.cc b/content/browser/browser_main_loop.cc index 1711f5f..ebf10e6 100644 --- a/content/browser/browser_main_loop.cc +++ b/content/browser/browser_main_loop.cc
@@ -188,6 +188,13 @@ #include "media/device_monitors/device_monitor_mac.h" #endif +#if defined(OS_FUCHSIA) +#include <magenta/process.h> +#include <magenta/syscalls.h> + +#include "base/fuchsia/default_job.h" +#endif // defined(OS_FUCHSIA) + #if defined(OS_POSIX) && !defined(OS_MACOSX) #include "content/browser/renderer_host/render_sandbox_host_linux.h" #include "content/browser/zygote_host/zygote_host_impl_linux.h" @@ -458,6 +465,18 @@ base::TimeDelta::FromSeconds(60); #endif // !defined(OS_FUCHSIA) +#if defined(OS_FUCHSIA) +// Create and register the job which will contain all child processes +// of the browser process as well as their descendents. +void InitDefaultJob() { + base::ScopedMxHandle handle; + mx_status_t result = mx_job_create(mx_job_default(), 0, handle.receive()); + CHECK_EQ(MX_OK, result) << "mx_job_create(job): " + << mx_status_get_string(result); + base::SetDefaultJob(std::move(handle)); +} +#endif // defined(OS_FUCHSIA) + } // namespace #if defined(USE_X11) && !defined(OS_CHROMEOS) @@ -608,6 +627,10 @@ crypto::EnsureNSPRInit(); #endif +#if defined(OS_FUCHSIA) + InitDefaultJob(); +#endif + if (parsed_command_line_.HasSwitch(switches::kRendererProcessLimit)) { std::string limit_string = parsed_command_line_.GetSwitchValueASCII( switches::kRendererProcessLimit);
diff --git a/content/browser/web_contents/web_contents_impl.cc b/content/browser/web_contents/web_contents_impl.cc index 97345bc..8d1cb4e 100644 --- a/content/browser/web_contents/web_contents_impl.cc +++ b/content/browser/web_contents/web_contents_impl.cc
@@ -519,6 +519,7 @@ did_first_visually_non_empty_paint_(false), capturer_count_(0), should_normally_be_visible_(true), + should_normally_be_occluded_(false), did_first_set_visible_(false), is_being_destroyed_(false), is_notifying_observers_(false), @@ -1358,7 +1359,7 @@ } // Ensure that all views are un-occluded before capture begins. - WasUnOccluded(); + DoWasUnOccluded(); } void WebContentsImpl::DecrementCapturerCount() { @@ -1374,11 +1375,14 @@ const gfx::Size old_size = preferred_size_for_capture_; preferred_size_for_capture_ = gfx::Size(); OnPreferredSizeChanged(old_size); - } - if (IsHidden()) { - DVLOG(1) << "Executing delayed WasHidden()."; - WasHidden(); + if (IsHidden()) { + DVLOG(1) << "Executing delayed WasHidden()."; + WasHidden(); + } + + if (should_normally_be_occluded_) + WasOccluded(); } } @@ -1569,14 +1573,23 @@ } void WebContentsImpl::WasOccluded() { - if (capturer_count_ > 0) - return; + if (capturer_count_ == 0) { + for (RenderWidgetHostView* view : GetRenderWidgetHostViewsInTree()) + view->WasOccluded(); + } - for (RenderWidgetHostView* view : GetRenderWidgetHostViewsInTree()) - view->WasOccluded(); + should_normally_be_occluded_ = true; } void WebContentsImpl::WasUnOccluded() { + if (capturer_count_ == 0) + DoWasUnOccluded(); + + should_normally_be_occluded_ = false; +} + +void WebContentsImpl::DoWasUnOccluded() { + // TODO(fdoray): Only call WasUnOccluded on frames in the active viewport. for (RenderWidgetHostView* view : GetRenderWidgetHostViewsInTree()) view->WasUnOccluded(); }
diff --git a/content/browser/web_contents/web_contents_impl.h b/content/browser/web_contents/web_contents_impl.h index e2bc7f3..a594d87 100644 --- a/content/browser/web_contents/web_contents_impl.h +++ b/content/browser/web_contents/web_contents_impl.h
@@ -232,12 +232,6 @@ bool should_normally_be_visible() { return should_normally_be_visible_; } - // Indicate if the window has been occluded, and pass this to the views, only - // if there is no active capture going on (otherwise it is dropped on the - // floor). - void WasOccluded(); - void WasUnOccluded(); - // Broadcasts the mode change to all frames. void SetAccessibilityMode(ui::AXMode mode); @@ -374,6 +368,8 @@ void WasShown() override; void WasHidden() override; bool IsVisible() const override; + void WasOccluded() override; + void WasUnOccluded() override; bool NeedToFireBeforeUnload() override; void DispatchBeforeUnload() override; void AttachToOuterWebContentsFrame( @@ -1043,6 +1039,9 @@ // all the unique RenderWidgetHostViews. std::set<RenderWidgetHostView*> GetRenderWidgetHostViewsInTree(); + // Calls WasUnOccluded() on all RenderWidgetHostViews in the frame tree. + void DoWasUnOccluded(); + // Called with the result of a DownloadImage() request. void OnDidDownloadImage(const ImageDownloadCallback& callback, int id, @@ -1455,6 +1454,9 @@ // Tracks whether RWHV should be visible once capturer_count_ becomes zero. bool should_normally_be_visible_; + // Tracks whether RWHV should be occluded once |capturer_count_| becomes zero. + bool should_normally_be_occluded_; + // Tracks whether this WebContents was ever set to be visible. Used to // facilitate WebContents being loaded in the background by setting // |should_normally_be_visible_|. Ensures WasShown() will trigger when first
diff --git a/content/browser/web_contents/web_contents_impl_unittest.cc b/content/browser/web_contents/web_contents_impl_unittest.cc index 85f5209..e871c9c 100644 --- a/content/browser/web_contents/web_contents_impl_unittest.cc +++ b/content/browser/web_contents/web_contents_impl_unittest.cc
@@ -2858,20 +2858,34 @@ contents()->WasOccluded(); EXPECT_TRUE(view->is_occluded()); - // Add a capturer. This should cause the view to be un-occluded. + // Adding a capturer on an occluded WebContents should cause the view to be + // unoccluded. Removing the capturer should cause the view to be occluded + // again. contents()->IncrementCapturerCount(gfx::Size()); EXPECT_FALSE(view->is_occluded()); - // Try to occlude the view. This will fail to propagate because of the - // active capturer. + contents()->DecrementCapturerCount(); + EXPECT_TRUE(view->is_occluded()); + + // Adding a capturer on an unoccluded WebContents should not change the + // occlusion state of the view. Calling WasOccluded() on an unoccluded + // WebContents() that has a capturer should not change the occlusion state of + // the view. Removing the capturer should cause the view to become occluded. + contents()->WasUnOccluded(); + EXPECT_FALSE(view->is_occluded()); + contents()->IncrementCapturerCount(gfx::Size()); + EXPECT_FALSE(view->is_occluded()); + contents()->WasOccluded(); EXPECT_FALSE(view->is_occluded()); - // Remove the capturer and try again. contents()->DecrementCapturerCount(); - EXPECT_FALSE(view->is_occluded()); - contents()->WasOccluded(); EXPECT_TRUE(view->is_occluded()); + + // Calling WasUnoccluded() on a WebContents with no capturers should cause the + // view to become unoccluded. + contents()->WasUnOccluded(); + EXPECT_FALSE(view->is_occluded()); } // Tests that GetLastActiveTime starts with a real, non-zero time and updates
diff --git a/content/public/browser/web_contents.h b/content/public/browser/web_contents.h index 576bd5e..ddaf66425 100644 --- a/content/public/browser/web_contents.h +++ b/content/public/browser/web_contents.h
@@ -442,7 +442,8 @@ // Get the last time that the WebContents was made hidden. virtual base::TimeTicks GetLastHiddenTime() const = 0; - // Invoked when the WebContents becomes shown/hidden. + // Invoked when the WebContents becomes shown/hidden. A hidden WebContents + // isn't painted on the screen. virtual void WasShown() = 0; virtual void WasHidden() = 0; @@ -451,6 +452,12 @@ // always returns false when the page is still loading. virtual bool IsVisible() const = 0; + // Invoked when the WebContents becomes occluded/unoccluded. An occluded + // WebContents isn't painted on the screen, except in a window switching + // feature (e.g. Alt-Tab). + virtual void WasOccluded() = 0; + virtual void WasUnOccluded() = 0; + // Returns true if the before unload and unload listeners need to be // fired. The value of this changes over time. For example, if true and the // before unload listener is executed and allows the user to exit, then this
diff --git a/content/shell/browser/shell_views.cc b/content/shell/browser/shell_views.cc index bd20d90..c513c703 100644 --- a/content/shell/browser/shell_views.cc +++ b/content/shell/browser/shell_views.cc
@@ -115,8 +115,7 @@ void InitShellWindow() { SetBackground(views::CreateStandardPanelBackground()); - views::GridLayout* layout = new views::GridLayout(this); - SetLayoutManager(layout); + views::GridLayout* layout = views::GridLayout::CreateAndInstall(this); views::ColumnSet* column_set = layout->AddColumnSet(0); column_set->AddPaddingColumn(0, 2); @@ -129,8 +128,8 @@ // Add toolbar buttons and URL text field { layout->StartRow(0, 0); - views::GridLayout* toolbar_layout = new views::GridLayout(toolbar_view_); - toolbar_view_->SetLayoutManager(toolbar_layout); + views::GridLayout* toolbar_layout = + views::GridLayout::CreateAndInstall(toolbar_view_); views::ColumnSet* toolbar_column_set = toolbar_layout->AddColumnSet(0);
diff --git a/content/test/content_test_suite.cc b/content/test/content_test_suite.cc index ab0f2b5..a4563e8 100644 --- a/content/test/content_test_suite.cc +++ b/content/test/content_test_suite.cc
@@ -15,8 +15,10 @@ #include "content/public/test/test_content_client_initializer.h" #include "gpu/config/gpu_info_collector.h" #include "gpu/config/gpu_util.h" +#include "gpu/ipc/in_process_command_buffer.h" #include "media/base/media.h" #include "testing/gtest/include/gtest/gtest.h" +#include "ui/gl/init/gl_factory.h" #include "ui/gl/test/gl_surface_test_support.h" #if defined(OS_WIN) @@ -83,14 +85,19 @@ media::InitializeMediaLibrary(); // When running in a child process for Mac sandbox tests, the sandbox exists // to initialize GL, so don't do it here. - bool is_child_process = base::CommandLine::ForCurrentProcess()->HasSwitch( - switches::kTestChildProcess); + base::CommandLine* command_line = base::CommandLine::ForCurrentProcess(); + bool is_child_process = command_line->HasSwitch(switches::kTestChildProcess); if (!is_child_process) { gpu::GPUInfo gpu_info; gpu::CollectBasicGraphicsInfo(&gpu_info); - gpu::ApplyGpuDriverBugWorkarounds(gpu_info, - base::CommandLine::ForCurrentProcess()); - gl::GLSurfaceTestSupport::InitializeOneOff(); + gpu::GpuFeatureInfo gpu_feature_info = + gpu::GetGpuFeatureInfo(gpu_info, *command_line); + gpu::InProcessCommandBuffer::InitializeDefaultServiceForTesting( + gpu_feature_info); + gl::GLSurfaceTestSupport::InitializeNoExtensionsOneOff(); + gl::init::SetDisabledExtensionsPlatform( + gpu_feature_info.disabled_extensions); + gl::init::InitializeExtensionSettingsOneOffPlatform(); } testing::TestEventListeners& listeners = testing::UnitTest::GetInstance()->listeners();
diff --git a/gpu/command_buffer/service/gles2_cmd_decoder_unittest_base.cc b/gpu/command_buffer/service/gles2_cmd_decoder_unittest_base.cc index 8923aaf..3d8d53dc 100644 --- a/gpu/command_buffer/service/gles2_cmd_decoder_unittest_base.cc +++ b/gpu/command_buffer/service/gles2_cmd_decoder_unittest_base.cc
@@ -2236,7 +2236,7 @@ context_creation_attribs_.bind_generates_resource = true; gl::init::InitializeGLOneOffImplementation(gl::kGLImplementationEGLGLES2, - false, false, false); + false, false, false, true); surface_ = gl::init::CreateOffscreenGLSurface( context_creation_attribs_.offscreen_framebuffer_size); context_ = gl::init::CreateGLContext(
diff --git a/gpu/command_buffer/tests/fuzzer_main.cc b/gpu/command_buffer/tests/fuzzer_main.cc index f7d8f48..be6ff9e 100644 --- a/gpu/command_buffer/tests/fuzzer_main.cc +++ b/gpu/command_buffer/tests/fuzzer_main.cc
@@ -122,12 +122,12 @@ command_line->AppendSwitchASCII(switches::kUseANGLE, gl::kANGLEImplementationNullName); CHECK(gl::init::InitializeGLOneOffImplementation( - gl::kGLImplementationEGLGLES2, false, false, false)); + gl::kGLImplementationEGLGLES2, false, false, false, true)); #elif defined(GPU_FUZZER_USE_SWIFTSHADER) command_line->AppendSwitchASCII(switches::kUseGL, gl::kGLImplementationSwiftShaderName); CHECK(gl::init::InitializeGLOneOffImplementation( - gl::kGLImplementationSwiftShaderGL, false, false, false)); + gl::kGLImplementationSwiftShaderGL, false, false, false, true)); #endif #if !defined(GPU_FUZZER_USE_STUB)
diff --git a/gpu/ipc/gpu_in_process_thread_service.cc b/gpu/ipc/gpu_in_process_thread_service.cc index d1aedd6..ba6097f 100644 --- a/gpu/ipc/gpu_in_process_thread_service.cc +++ b/gpu/ipc/gpu_in_process_thread_service.cc
@@ -13,8 +13,11 @@ scoped_refptr<base::SingleThreadTaskRunner> task_runner, gpu::SyncPointManager* sync_point_manager, gpu::gles2::MailboxManager* mailbox_manager, - scoped_refptr<gl::GLShareGroup> share_group) - : gpu::InProcessCommandBuffer::Service(mailbox_manager, share_group), + scoped_refptr<gl::GLShareGroup> share_group, + const GpuFeatureInfo& gpu_feature_info) + : gpu::InProcessCommandBuffer::Service(mailbox_manager, + share_group, + gpu_feature_info), task_runner_(task_runner), sync_point_manager_(sync_point_manager) {}
diff --git a/gpu/ipc/gpu_in_process_thread_service.h b/gpu/ipc/gpu_in_process_thread_service.h index 6dbed869..464e713 100644 --- a/gpu/ipc/gpu_in_process_thread_service.h +++ b/gpu/ipc/gpu_in_process_thread_service.h
@@ -24,7 +24,8 @@ scoped_refptr<base::SingleThreadTaskRunner> task_runner, gpu::SyncPointManager* sync_point_manager, gpu::gles2::MailboxManager* mailbox_manager, - scoped_refptr<gl::GLShareGroup> share_group); + scoped_refptr<gl::GLShareGroup> share_group, + const GpuFeatureInfo& gpu_feature_info); // gpu::InProcessCommandBuffer::Service implementation. void ScheduleTask(const base::Closure& task) override;
diff --git a/gpu/ipc/in_process_command_buffer.cc b/gpu/ipc/in_process_command_buffer.cc index b91fd7c..1177f09 100644 --- a/gpu/ipc/in_process_command_buffer.cc +++ b/gpu/ipc/in_process_command_buffer.cc
@@ -86,10 +86,16 @@ ~GpuInProcessThreadHolder() override { Stop(); } + void SetGpuFeatureInfo(const GpuFeatureInfo& gpu_feature_info) { + DCHECK(!gpu_thread_service_.get()); + gpu_feature_info_ = gpu_feature_info; + } + const scoped_refptr<InProcessCommandBuffer::Service>& GetGpuThreadService() { if (!gpu_thread_service_) { gpu_thread_service_ = new GpuInProcessThreadService( - task_runner(), sync_point_manager_.get(), nullptr, nullptr); + task_runner(), sync_point_manager_.get(), nullptr, nullptr, + gpu_feature_info_); } return gpu_thread_service_; } @@ -97,6 +103,7 @@ private: std::unique_ptr<SyncPointManager> sync_point_manager_; scoped_refptr<InProcessCommandBuffer::Service> gpu_thread_service_; + GpuFeatureInfo gpu_feature_info_; }; base::LazyInstance<GpuInProcessThreadHolder>::DestructorAtExit @@ -128,13 +135,20 @@ } // anonyous namespace +// TODO(zmo): This constructor is used only by DeferredGpuCommandService for +// Android WebView. We will need to wire up the computed GpuFeatureInfo to +// here instead of computing from commandline switch.. InProcessCommandBuffer::Service::Service(const GpuPreferences& gpu_preferences) : Service(gpu_preferences, nullptr, nullptr) {} InProcessCommandBuffer::Service::Service( gpu::gles2::MailboxManager* mailbox_manager, - scoped_refptr<gl::GLShareGroup> share_group) - : Service(GpuPreferences(), mailbox_manager, share_group) {} + scoped_refptr<gl::GLShareGroup> share_group, + const GpuFeatureInfo& gpu_feature_info) + : Service(GpuPreferences(), + mailbox_manager, + share_group, + gpu_feature_info) {} InProcessCommandBuffer::Service::Service( const GpuPreferences& gpu_preferences, @@ -152,6 +166,25 @@ } } +InProcessCommandBuffer::Service::Service( + const GpuPreferences& gpu_preferences, + gpu::gles2::MailboxManager* mailbox_manager, + scoped_refptr<gl::GLShareGroup> share_group, + const GpuFeatureInfo& gpu_feature_info) + : gpu_preferences_(gpu_preferences), + gpu_feature_info_(gpu_feature_info), + gpu_driver_bug_workarounds_( + gpu_feature_info.enabled_gpu_driver_bug_workarounds), + mailbox_manager_(mailbox_manager), + share_group_(share_group), + shader_translator_cache_(gpu_preferences_) { + if (!mailbox_manager_) { + // TODO(piman): have embedders own the mailbox manager. + owned_mailbox_manager_ = gles2::MailboxManager::Create(gpu_preferences_); + mailbox_manager_ = owned_mailbox_manager_.get(); + } +} + InProcessCommandBuffer::Service::~Service() {} const gpu::GpuPreferences& InProcessCommandBuffer::Service::gpu_preferences() { @@ -215,6 +248,19 @@ Destroy(); } +// static +void InProcessCommandBuffer::InitializeDefaultServiceForTesting( + const GpuFeatureInfo& gpu_feature_info) { + // Call base::ThreadTaskRunnerHandle::IsSet() to ensure that it is + // instantiated before we create the GPU thread, otherwise shutdown order will + // delete the ThreadTaskRunnerHandle before the GPU thread's message loop, + // and when the message loop is shutdown, it will recreate + // ThreadTaskRunnerHandle, which will re-add a new task to the, AtExitManager, + // which causes a deadlock because it's already locked. + base::ThreadTaskRunnerHandle::IsSet(); + g_default_service.Get().SetGpuFeatureInfo(gpu_feature_info); +} + bool InProcessCommandBuffer::MakeCurrent() { CheckSequencedThread(); command_buffer_lock_.AssertAcquired(); @@ -357,6 +403,9 @@ gl_share_group_.get(), surface_.get(), GenerateGLContextAttribs( params.attribs, decoder_->GetContextGroup()->gpu_preferences())); + if (context_.get()) { + service_->gpu_feature_info().ApplyToGLContext(context_.get()); + } gl_share_group_->SetSharedContext(surface_.get(), context_.get()); } @@ -376,6 +425,9 @@ gl_share_group_.get(), surface_.get(), GenerateGLContextAttribs( params.attribs, decoder_->GetContextGroup()->gpu_preferences())); + if (context_.get()) { + service_->gpu_feature_info().ApplyToGLContext(context_.get()); + } } if (!context_.get()) {
diff --git a/gpu/ipc/in_process_command_buffer.h b/gpu/ipc/in_process_command_buffer.h index 110e8d4..eb04c78 100644 --- a/gpu/ipc/in_process_command_buffer.h +++ b/gpu/ipc/in_process_command_buffer.h
@@ -31,6 +31,7 @@ #include "gpu/command_buffer/service/image_manager.h" #include "gpu/command_buffer/service/service_discardable_manager.h" #include "gpu/config/gpu_driver_bug_workarounds.h" +#include "gpu/config/gpu_feature_info.h" #include "gpu/gpu_export.h" #include "gpu/ipc/service/image_transport_surface_delegate.h" #include "ui/gfx/gpu_memory_buffer.h" @@ -186,12 +187,20 @@ void UpdateVSyncParametersOnOriginThread(base::TimeTicks timebase, base::TimeDelta interval); + // Mostly the GpuFeatureInfo from GpuInit will be used to create a gpu thread + // service. In certain tests GpuInit is not part of the execution path, so + // the test suite need to compute it and pass it to the default service. + // See "gpu/ipc/in_process_command_buffer.cc". + static void InitializeDefaultServiceForTesting( + const GpuFeatureInfo& gpu_feature_info); + // The serializer interface to the GPU service (i.e. thread). class Service { public: explicit Service(const gpu::GpuPreferences& gpu_preferences); Service(gles2::MailboxManager* mailbox_manager, - scoped_refptr<gl::GLShareGroup> share_group); + scoped_refptr<gl::GLShareGroup> share_group, + const GpuFeatureInfo& gpu_feature_info); virtual ~Service(); @@ -210,6 +219,7 @@ virtual bool BlockThreadOnWaitSyncToken() const = 0; const GpuPreferences& gpu_preferences(); + const GpuFeatureInfo& gpu_feature_info() { return gpu_feature_info_; } const GpuDriverBugWorkarounds& gpu_driver_bug_workarounds(); scoped_refptr<gl::GLShareGroup> share_group(); gles2::MailboxManager* mailbox_manager() { return mailbox_manager_; } @@ -228,9 +238,14 @@ protected: Service(const gpu::GpuPreferences& gpu_preferences, gles2::MailboxManager* mailbox_manager, + scoped_refptr<gl::GLShareGroup> share_group, + const GpuFeatureInfo& gpu_feature_info); + Service(const gpu::GpuPreferences& gpu_preferences, + gles2::MailboxManager* mailbox_manager, scoped_refptr<gl::GLShareGroup> share_group); const GpuPreferences gpu_preferences_; + const GpuFeatureInfo gpu_feature_info_; const GpuDriverBugWorkarounds gpu_driver_bug_workarounds_; std::unique_ptr<gles2::MailboxManager> owned_mailbox_manager_; gles2::MailboxManager* mailbox_manager_ = nullptr;
diff --git a/ios/chrome/browser/content_suggestions/BUILD.gn b/ios/chrome/browser/content_suggestions/BUILD.gn index 88ce984..b8af6c00 100644 --- a/ios/chrome/browser/content_suggestions/BUILD.gn +++ b/ios/chrome/browser/content_suggestions/BUILD.gn
@@ -17,8 +17,6 @@ "content_suggestions_header_view_controller.mm", "content_suggestions_mediator.h", "content_suggestions_mediator.mm", - "content_suggestions_metrics_recorder.h", - "content_suggestions_metrics_recorder.mm", "content_suggestions_service_bridge_observer.h", "content_suggestions_service_bridge_observer.mm", "mediator_util.h",
diff --git a/ios/chrome/browser/content_suggestions/content_suggestions_alert_factory.mm b/ios/chrome/browser/content_suggestions/content_suggestions_alert_factory.mm index 206a0f98..c83b22c0 100644 --- a/ios/chrome/browser/content_suggestions/content_suggestions_alert_factory.mm +++ b/ios/chrome/browser/content_suggestions/content_suggestions_alert_factory.mm
@@ -44,6 +44,7 @@ action:^{ ContentSuggestionsItem* strongItem = weakItem; if (strongItem) { + // TODO(crbug.com/691979): Add metrics. [weakCommandHandler openNewTabWithSuggestionsItem:strongItem incognito:NO]; @@ -57,6 +58,7 @@ action:^{ ContentSuggestionsItem* strongItem = weakItem; if (strongItem) { + // TODO(crbug.com/691979): Add metrics. [weakCommandHandler openNewTabWithSuggestionsItem:strongItem incognito:YES]; @@ -72,6 +74,7 @@ action:^{ ContentSuggestionsItem* strongItem = weakItem; if (strongItem) { + // TODO(crbug.com/691979): Add metrics. [weakCommandHandler addItemToReadingList:strongItem]; } } @@ -84,6 +87,7 @@ action:^{ ContentSuggestionsItem* strongItem = weakItem; if (strongItem) { + // TODO(crbug.com/691979): Add metrics. [weakCommandHandler dismissSuggestion:strongItem atIndexPath:indexPath]; @@ -93,6 +97,7 @@ [alertCoordinator addItemWithTitle:l10n_util::GetNSString(IDS_APP_CANCEL) action:^{ + // TODO(crbug.com/691979): Add metrics. } style:UIAlertActionStyleCancel]; return alertCoordinator;
diff --git a/ios/chrome/browser/content_suggestions/content_suggestions_coordinator.mm b/ios/chrome/browser/content_suggestions/content_suggestions_coordinator.mm index 2073976..6637b52 100644 --- a/ios/chrome/browser/content_suggestions/content_suggestions_coordinator.mm +++ b/ios/chrome/browser/content_suggestions/content_suggestions_coordinator.mm
@@ -20,7 +20,6 @@ #import "ios/chrome/browser/content_suggestions/content_suggestions_alert_factory.h" #import "ios/chrome/browser/content_suggestions/content_suggestions_header_view_controller.h" #import "ios/chrome/browser/content_suggestions/content_suggestions_mediator.h" -#import "ios/chrome/browser/content_suggestions/content_suggestions_metrics_recorder.h" #import "ios/chrome/browser/content_suggestions/ntp_home_metrics.h" #include "ios/chrome/browser/favicon/ios_chrome_large_icon_cache_factory.h" #include "ios/chrome/browser/favicon/ios_chrome_large_icon_service_factory.h" @@ -94,7 +93,6 @@ @property(nonatomic, strong) GoogleLandingMediator* googleLandingMediator; @property(nonatomic, strong) ContentSuggestionsHeaderSynchronizer* headerCollectionInteractionHandler; -@property(nonatomic, strong) ContentSuggestionsMetricsRecorder* metricsRecorder; // Redefined as readwrite. @property(nonatomic, strong, readwrite) @@ -119,7 +117,6 @@ @synthesize webStateList = _webStateList; @synthesize dispatcher = _dispatcher; @synthesize delegate = _delegate; -@synthesize metricsRecorder = _metricsRecorder; - (void)start { if (self.visible || !self.browserState) { @@ -174,8 +171,6 @@ mostVisitedSite:std::move(mostVisitedFactory)]; self.contentSuggestionsMediator.commandHandler = self; self.contentSuggestionsMediator.headerProvider = self.headerController; - self.metricsRecorder = [[ContentSuggestionsMetricsRecorder alloc] init]; - self.metricsRecorder.delegate = self.contentSuggestionsMediator; self.suggestionsViewController = [[ContentSuggestionsViewController alloc] initWithStyle:CollectionViewControllerStyleDefault]; @@ -185,7 +180,6 @@ self.suggestionsViewController.suggestionsDelegate = self; self.suggestionsViewController.audience = self; self.suggestionsViewController.overscrollDelegate = self; - self.suggestionsViewController.metricsRecorder = self.metricsRecorder; [self.suggestionsViewController addChildViewController:self.headerController]; [self.headerController @@ -234,21 +228,12 @@ [self.dispatcher showReadingList]; } -- (void)openPageForItemAtIndexPath:(NSIndexPath*)indexPath { - CollectionViewItem* item = [self.suggestionsViewController.collectionViewModel - itemAtIndexPath:indexPath]; +- (void)openPageForItem:(CollectionViewItem*)item { + // TODO(crbug.com/691979): Add metrics. + ContentSuggestionsItem* suggestionItem = base::mac::ObjCCastStrict<ContentSuggestionsItem>(item); - [self.metricsRecorder - onSuggestionOpened:suggestionItem - atIndexPath:indexPath - sectionsShownAbove:[self.suggestionsViewController - numberOfSectionsAbove:indexPath.section] - suggestionsShownAbove:[self.suggestionsViewController - numberOfSuggestionsAbove:indexPath.section] - withAction:WindowOpenDisposition::CURRENT_TAB]; - // Use a referrer with a specific URL to mark this entry as coming from // ContentSuggestions. web::Referrer referrer; @@ -281,14 +266,6 @@ readLaterAction:(BOOL)readLaterAction { ContentSuggestionsItem* suggestionsItem = base::mac::ObjCCastStrict<ContentSuggestionsItem>(item); - - [self.metricsRecorder - onMenuOpenedForSuggestion:suggestionsItem - atIndexPath:indexPath - suggestionsShownAbove:[self.suggestionsViewController - numberOfSuggestionsAbove:indexPath - .section]]; - self.alertCoordinator = [ContentSuggestionsAlertFactory alertCoordinatorForSuggestionItem:suggestionsItem onViewController:self.suggestionsViewController @@ -355,40 +332,10 @@ incognito:(BOOL)incognito { new_tab_page_uma::RecordAction(self.browserState, new_tab_page_uma::ACTION_OPENED_SUGGESTION); - - NSIndexPath* indexPath = [self.suggestionsViewController.collectionViewModel - indexPathForItem:item]; - if (indexPath) { - WindowOpenDisposition disposition = - incognito ? WindowOpenDisposition::OFF_THE_RECORD - : WindowOpenDisposition::NEW_BACKGROUND_TAB; - [self.metricsRecorder - onSuggestionOpened:item - atIndexPath:indexPath - sectionsShownAbove:[self.suggestionsViewController - numberOfSectionsAbove:indexPath.section] - suggestionsShownAbove:[self.suggestionsViewController - numberOfSuggestionsAbove:indexPath.section] - withAction:disposition]; - } - [self openNewTabWithURL:item.URL incognito:incognito]; } - (void)addItemToReadingList:(ContentSuggestionsItem*)item { - NSIndexPath* indexPath = [self.suggestionsViewController.collectionViewModel - indexPathForItem:item]; - if (indexPath) { - [self.metricsRecorder - onSuggestionOpened:item - atIndexPath:indexPath - sectionsShownAbove:[self.suggestionsViewController - numberOfSectionsAbove:indexPath.section] - suggestionsShownAbove:[self.suggestionsViewController - numberOfSuggestionsAbove:indexPath.section] - withAction:WindowOpenDisposition::SAVE_TO_DISK]; - } - self.contentSuggestionsMediator.readingListNeedsReload = YES; ReadingListAddCommand* command = [[ReadingListAddCommand alloc] initWithURL:item.URL title:item.title]; @@ -404,6 +351,7 @@ indexPathForItem:item]; } + // TODO(crbug.com/691979): Add metrics. [self.contentSuggestionsMediator dismissSuggestion:item.suggestionIdentifier]; [self.suggestionsViewController dismissEntryAtIndexPath:itemIndexPath]; } @@ -586,6 +534,8 @@ // Opens the |URL| in a new tab |incognito| or not. - (void)openNewTabWithURL:(const GURL&)URL incognito:(BOOL)incognito { + // TODO(crbug.com/691979): Add metrics. + // Open the tab in background if it is non-incognito only. [self.URLLoader webPageOrderedOpen:URL referrer:web::Referrer()
diff --git a/ios/chrome/browser/content_suggestions/content_suggestions_mediator.h b/ios/chrome/browser/content_suggestions/content_suggestions_mediator.h index 35abe31..a9e61abf 100644 --- a/ios/chrome/browser/content_suggestions/content_suggestions_mediator.h +++ b/ios/chrome/browser/content_suggestions/content_suggestions_mediator.h
@@ -10,7 +10,6 @@ #include <memory> #import "ios/chrome/browser/content_suggestions/content_suggestions_mediator.h" -#import "ios/chrome/browser/content_suggestions/content_suggestions_metrics_recorder.h" #import "ios/chrome/browser/ui/content_suggestions/content_suggestions_data_source.h" namespace favicon { @@ -36,9 +35,7 @@ // Mediator for ContentSuggestions. Makes the interface between a // ntp_snippets::ContentSuggestionsService and the Objective-C services using // its data. -@interface ContentSuggestionsMediator - : NSObject<ContentSuggestionsDataSource, - ContentSuggestionsMetricsRecorderDelegate> +@interface ContentSuggestionsMediator : NSObject<ContentSuggestionsDataSource> // Initialize the mediator with the |contentService| to mediate. - (nullable instancetype)
diff --git a/ios/chrome/browser/content_suggestions/content_suggestions_mediator.mm b/ios/chrome/browser/content_suggestions/content_suggestions_mediator.mm index 8e212bb..404846cf 100644 --- a/ios/chrome/browser/content_suggestions/content_suggestions_mediator.mm +++ b/ios/chrome/browser/content_suggestions/content_suggestions_mediator.mm
@@ -128,7 +128,6 @@ base::MakeUnique<ContentSuggestionsServiceBridge>(self, contentService); _contentService = contentService; _sectionInformationByCategory = [[NSMutableDictionary alloc] init]; - _faviconMediator = [[ContentSuggestionsFaviconMediator alloc] initWithContentService:contentService largeIconService:largeIconService @@ -465,14 +464,6 @@ } } -#pragma mark - ContentSuggestionsMetricsRecorderDelegate - -- (ContentSuggestionsCategoryWrapper*)categoryWrapperForSectionInfo: - (ContentSuggestionsSectionInformation*)sectionInfo { - return [[self.sectionInformationByCategory allKeysForObject:sectionInfo] - firstObject]; -} - #pragma mark - Private // Converts the |suggestions| from |category| to CSCollectionViewItem and adds @@ -518,6 +509,13 @@ wrapperWithCategory:category]] = sectionInfo; } +// Returns a CategoryWrapper acting as a key for this section info. +- (ContentSuggestionsCategoryWrapper*)categoryWrapperForSectionInfo: + (ContentSuggestionsSectionInformation*)sectionInfo { + return [[self.sectionInformationByCategory allKeysForObject:sectionInfo] + firstObject]; +} + // If the |statusCode| is a success and |suggestions| is not empty, runs the // |callback| with the |suggestions| converted to Objective-C. - (void)didFetchMoreSuggestions:
diff --git a/ios/chrome/browser/content_suggestions/content_suggestions_metrics_recorder.h b/ios/chrome/browser/content_suggestions/content_suggestions_metrics_recorder.h deleted file mode 100644 index 62ec5015..0000000 --- a/ios/chrome/browser/content_suggestions/content_suggestions_metrics_recorder.h +++ /dev/null
@@ -1,50 +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 IOS_CHROME_BROWSER_CONTENT_SUGGESTIONS_CONTENT_SUGGESTIONS_METRICS_RECORDER_H_ -#define IOS_CHROME_BROWSER_CONTENT_SUGGESTIONS_CONTENT_SUGGESTIONS_METRICS_RECORDER_H_ - -#import <UIKit/UIKit.h> - -#import "ios/chrome/browser/ui/content_suggestions/content_suggestions_metrics_recording.h" - -@class ContentSuggestionsCategoryWrapper; -@class ContentSuggestionsItem; -@class ContentSuggestionsSectionInformation; - -// Delegate for the metrics recorder. -@protocol ContentSuggestionsMetricsRecorderDelegate - -// Returns a CategoryWrapper corresponding to this |sectionInfo|. -- (nullable ContentSuggestionsCategoryWrapper*)categoryWrapperForSectionInfo: - (nonnull ContentSuggestionsSectionInformation*)sectionInfo; - -@end - -// Records different metrics for ContentSuggestions -@interface ContentSuggestionsMetricsRecorder - : NSObject<ContentSuggestionsMetricsRecording> - -@property(nonatomic, weak, nullable) - id<ContentSuggestionsMetricsRecorderDelegate> - delegate; - -// Records the opening of an |item| suggestion at the |indexPath|. Also needs -// the number of |sectionsShownAbove| this section and the number of -// |suggestionsAbove| its section. The item is opened with |action|. -- (void)onSuggestionOpened:(nonnull ContentSuggestionsItem*)item - atIndexPath:(nonnull NSIndexPath*)indexPath - sectionsShownAbove:(NSInteger)sectionsShownAbove - suggestionsShownAbove:(NSInteger)suggestionsAbove - withAction:(WindowOpenDisposition)action; - -// Records the opening of a context menu for an |item| at the |indexPath|. Needs -// the number of |suggestionsAbove| the section of the item. -- (void)onMenuOpenedForSuggestion:(nonnull ContentSuggestionsItem*)item - atIndexPath:(nonnull NSIndexPath*)indexPath - suggestionsShownAbove:(NSInteger)suggestionsAbove; - -@end - -#endif // IOS_CHROME_BROWSER_CONTENT_SUGGESTIONS_CONTENT_SUGGESTIONS_METRICS_RECORDER_H_
diff --git a/ios/chrome/browser/content_suggestions/content_suggestions_metrics_recorder.mm b/ios/chrome/browser/content_suggestions/content_suggestions_metrics_recorder.mm deleted file mode 100644 index c595864..0000000 --- a/ios/chrome/browser/content_suggestions/content_suggestions_metrics_recorder.mm +++ /dev/null
@@ -1,95 +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. - -#import "ios/chrome/browser/content_suggestions/content_suggestions_metrics_recorder.h" - -#include "base/mac/foundation_util.h" -#include "components/ntp_snippets/content_suggestions_metrics.h" -#import "ios/chrome/browser/content_suggestions/content_suggestions_category_wrapper.h" -#import "ios/chrome/browser/ui/content_suggestions/cells/content_suggestions_item.h" -#import "ios/chrome/browser/ui/content_suggestions/cells/suggested_content.h" -#import "ios/chrome/browser/ui/content_suggestions/identifier/content_suggestion_identifier.h" -#import "ios/chrome/browser/ui/content_suggestions/identifier/content_suggestions_section_information.h" - -#if !defined(__has_feature) || !__has_feature(objc_arc) -#error "This file requires ARC support." -#endif - -@implementation ContentSuggestionsMetricsRecorder - -@synthesize delegate = _delegate; - -#pragma mark - Public - -- (void)onSuggestionOpened:(ContentSuggestionsItem*)item - atIndexPath:(NSIndexPath*)indexPath - sectionsShownAbove:(NSInteger)sectionsShownAbove - suggestionsShownAbove:(NSInteger)suggestionsAbove - withAction:(WindowOpenDisposition)action { - ContentSuggestionsSectionInformation* sectionInfo = - item.suggestionIdentifier.sectionInfo; - ContentSuggestionsCategoryWrapper* categoryWrapper = - [self.delegate categoryWrapperForSectionInfo:sectionInfo]; - - ntp_snippets::metrics::OnSuggestionOpened( - suggestionsAbove + indexPath.item, [categoryWrapper category], - sectionsShownAbove, indexPath.item, item.publishDate, item.score, action, - /*is_prefetched=*/false, /*is_offline=*/false); -} - -- (void)onMenuOpenedForSuggestion:(ContentSuggestionsItem*)item - atIndexPath:(NSIndexPath*)indexPath - suggestionsShownAbove:(NSInteger)suggestionsAbove { - ContentSuggestionsSectionInformation* sectionInfo = - item.suggestionIdentifier.sectionInfo; - ContentSuggestionsCategoryWrapper* categoryWrapper = - [self.delegate categoryWrapperForSectionInfo:sectionInfo]; - - ntp_snippets::metrics::OnSuggestionMenuOpened( - suggestionsAbove + indexPath.item, [categoryWrapper category], - indexPath.item, item.publishDate, item.score); -} - -#pragma mark - ContentSuggestionsMetricsRecording - -- (void)onSuggestionShown:(CollectionViewItem*)item - atIndexPath:(NSIndexPath*)indexPath - suggestionsShownAbove:(NSInteger)suggestionsAbove { - ContentSuggestionsItem* suggestion = - base::mac::ObjCCastStrict<ContentSuggestionsItem>(item); - ContentSuggestionsSectionInformation* sectionInfo = - suggestion.suggestionIdentifier.sectionInfo; - ContentSuggestionsCategoryWrapper* categoryWrapper = - [self.delegate categoryWrapperForSectionInfo:sectionInfo]; - - ntp_snippets::metrics::OnSuggestionShown( - suggestionsAbove + indexPath.item, [categoryWrapper category], - indexPath.item, suggestion.publishDate, suggestion.score, - suggestion.fetchDate, /*is_prefetched=*/false, /*is_offline=*/false); -} - -- (void)onMoreButtonTappedAtPosition:(NSInteger)position - inSection:(ContentSuggestionsSectionInformation*) - sectionInfo { - ContentSuggestionsCategoryWrapper* categoryWrapper = - [self.delegate categoryWrapperForSectionInfo:sectionInfo]; - - ntp_snippets::metrics::OnMoreButtonClicked([categoryWrapper category], - position); -} - -- (void)onSuggestionDismissed:(CollectionViewItem<SuggestedContent>*)item - atIndexPath:(NSIndexPath*)indexPath - suggestionsShownAbove:(NSInteger)suggestionsAbove { - ContentSuggestionsSectionInformation* sectionInfo = - item.suggestionIdentifier.sectionInfo; - ContentSuggestionsCategoryWrapper* categoryWrapper = - [self.delegate categoryWrapperForSectionInfo:sectionInfo]; - - ntp_snippets::metrics::OnSuggestionDismissed( - suggestionsAbove + indexPath.item, [categoryWrapper category], - indexPath.item, /*visited=*/false); -} - -@end
diff --git a/ios/chrome/browser/content_suggestions/mediator_util.mm b/ios/chrome/browser/content_suggestions/mediator_util.mm index 2072de8..81306c03 100644 --- a/ios/chrome/browser/content_suggestions/mediator_util.mm +++ b/ios/chrome/browser/content_suggestions/mediator_util.mm
@@ -62,7 +62,6 @@ initWithType:0 title:base::SysUTF16ToNSString(contentSuggestion.title()) url:contentSuggestion.url()]; - suggestion.metricsRecorded = NO; suggestion.publisher = base::SysUTF16ToNSString(contentSuggestion.publisher_name()); @@ -73,9 +72,6 @@ contentSuggestion.id().id_within_category(); suggestion.suggestionIdentifier.sectionInfo = sectionInfo; - suggestion.score = contentSuggestion.score(); - suggestion.fetchDate = contentSuggestion.fetch_date(); - if (category.IsKnownCategory(ntp_snippets::KnownCategories::READING_LIST)) { suggestion.faviconURL = contentSuggestion.reading_list_suggestion_extra()->favicon_page_url;
diff --git a/ios/chrome/browser/ntp_snippets/ios_chrome_content_suggestions_service_factory_util.cc b/ios/chrome/browser/ntp_snippets/ios_chrome_content_suggestions_service_factory_util.cc index d769bc2..25a2ec4 100644 --- a/ios/chrome/browser/ntp_snippets/ios_chrome_content_suggestions_service_factory_util.cc +++ b/ios/chrome/browser/ntp_snippets/ios_chrome_content_suggestions_service_factory_util.cc
@@ -182,8 +182,6 @@ base::Bind(&ParseJson), GetFetchEndpoint(GetChannel()), api_key, service->user_classifier()); - // This pref is also used for logging. If it is changed, change it in the - // other places. std::string pref_name = prefs::kSearchSuggestEnabled; auto provider = base::MakeUnique<RemoteSuggestionsProviderImpl>( service, prefs, GetApplicationContext()->GetApplicationLocale(),
diff --git a/ios/chrome/browser/ui/content_suggestions/BUILD.gn b/ios/chrome/browser/ui/content_suggestions/BUILD.gn index 2f8318ba..f19a502 100644 --- a/ios/chrome/browser/ui/content_suggestions/BUILD.gn +++ b/ios/chrome/browser/ui/content_suggestions/BUILD.gn
@@ -19,7 +19,6 @@ "content_suggestions_header_view_controller_delegate.h", "content_suggestions_layout.h", "content_suggestions_layout.mm", - "content_suggestions_metrics_recording.h", "content_suggestions_view_controller.h", "content_suggestions_view_controller.mm", "content_suggestions_view_controller_audience.h",
diff --git a/ios/chrome/browser/ui/content_suggestions/cells/content_suggestions_item.h b/ios/chrome/browser/ui/content_suggestions/cells/content_suggestions_item.h index f69d7428..e0168ec 100644 --- a/ios/chrome/browser/ui/content_suggestions/cells/content_suggestions_item.h +++ b/ios/chrome/browser/ui/content_suggestions/cells/content_suggestions_item.h
@@ -55,11 +55,6 @@ // Command handler for the accessibility custom actions. @property(nonatomic, weak) id<ContentSuggestionsGestureCommands> commandHandler; -// Score of the suggestions. -@property(nonatomic, assign) float score; -// Date when the suggestion has been fetched. -@property(nonatomic, assign) base::Time fetchDate; - @end #endif // IOS_CHROME_BROWSER_UI_CONTENT_SUGGESTIONS_CELLS_CONTENT_SUGGESTIONS_ITEM_H_
diff --git a/ios/chrome/browser/ui/content_suggestions/cells/content_suggestions_item.mm b/ios/chrome/browser/ui/content_suggestions/cells/content_suggestions_item.mm index a19dc8a..567c2a23 100644 --- a/ios/chrome/browser/ui/content_suggestions/cells/content_suggestions_item.mm +++ b/ios/chrome/browser/ui/content_suggestions/cells/content_suggestions_item.mm
@@ -49,9 +49,6 @@ @synthesize firstTimeWithImage = _firstTimeWithImage; @synthesize readLaterAction = _readLaterAction; @synthesize commandHandler = _commandHandler; -@synthesize score = _score; -@synthesize fetchDate = _fetchDate; -@synthesize metricsRecorded = _metricsRecorded; - (instancetype)initWithType:(NSInteger)type title:(NSString*)title
diff --git a/ios/chrome/browser/ui/content_suggestions/cells/content_suggestions_learn_more_item.mm b/ios/chrome/browser/ui/content_suggestions/cells/content_suggestions_learn_more_item.mm index 32000bf..55c25af 100644 --- a/ios/chrome/browser/ui/content_suggestions/cells/content_suggestions_learn_more_item.mm +++ b/ios/chrome/browser/ui/content_suggestions/cells/content_suggestions_learn_more_item.mm
@@ -29,7 +29,6 @@ @implementation ContentSuggestionsLearnMoreItem @synthesize suggestionIdentifier; -@synthesize metricsRecorded; - (instancetype)initWithType:(NSInteger)type { self = [super initWithType:type];
diff --git a/ios/chrome/browser/ui/content_suggestions/cells/content_suggestions_most_visited_item.mm b/ios/chrome/browser/ui/content_suggestions/cells/content_suggestions_most_visited_item.mm index b644250..9f04f3d7 100644 --- a/ios/chrome/browser/ui/content_suggestions/cells/content_suggestions_most_visited_item.mm +++ b/ios/chrome/browser/ui/content_suggestions/cells/content_suggestions_most_visited_item.mm
@@ -25,7 +25,6 @@ @synthesize URL = _URL; @synthesize source = _source; @synthesize commandHandler = _commandHandler; -@synthesize metricsRecorded = _metricsRecorded; - (instancetype)initWithType:(NSInteger)type { self = [super initWithType:type];
diff --git a/ios/chrome/browser/ui/content_suggestions/cells/content_suggestions_text_item.mm b/ios/chrome/browser/ui/content_suggestions/cells/content_suggestions_text_item.mm index e95c525..fea3722a 100644 --- a/ios/chrome/browser/ui/content_suggestions/cells/content_suggestions_text_item.mm +++ b/ios/chrome/browser/ui/content_suggestions/cells/content_suggestions_text_item.mm
@@ -18,7 +18,6 @@ @synthesize text = _text; @synthesize detailText = _detailText; @synthesize suggestionIdentifier = _suggestionIdentifier; -@synthesize metricsRecorded = _metricsRecorded; - (instancetype)initWithType:(NSInteger)type { self = [super initWithType:type];
diff --git a/ios/chrome/browser/ui/content_suggestions/cells/content_suggestions_whats_new_item.mm b/ios/chrome/browser/ui/content_suggestions/cells/content_suggestions_whats_new_item.mm index 2d4038f..3408bc7 100644 --- a/ios/chrome/browser/ui/content_suggestions/cells/content_suggestions_whats_new_item.mm +++ b/ios/chrome/browser/ui/content_suggestions/cells/content_suggestions_whats_new_item.mm
@@ -35,7 +35,6 @@ @synthesize text = _text; @synthesize icon = _icon; @synthesize suggestionIdentifier = _suggestionIdentifier; -@synthesize metricsRecorded = _metricsRecorded; - (instancetype)initWithType:(NSInteger)type { self = [super initWithType:type];
diff --git a/ios/chrome/browser/ui/content_suggestions/cells/suggested_content.h b/ios/chrome/browser/ui/content_suggestions/cells/suggested_content.h index c1f775b..118dab41 100644 --- a/ios/chrome/browser/ui/content_suggestions/cells/suggested_content.h +++ b/ios/chrome/browser/ui/content_suggestions/cells/suggested_content.h
@@ -15,10 +15,7 @@ @protocol SuggestedContent // Identifier for this content. -@property(nonatomic, strong, nullable) - ContentSuggestionIdentifier* suggestionIdentifier; -// Whether the metrics for this suggestion have been recorded. -@property(nonatomic, assign) BOOL metricsRecorded; +@property(nonatomic, strong) ContentSuggestionIdentifier* suggestionIdentifier; // The height needed by a cell configured by this item, for a |width|. - (CGFloat)cellHeightForWidth:(CGFloat)width;
diff --git a/ios/chrome/browser/ui/content_suggestions/content_suggestions_collection_updater.h b/ios/chrome/browser/ui/content_suggestions/content_suggestions_collection_updater.h index 46a5341..ac4517e0 100644 --- a/ios/chrome/browser/ui/content_suggestions/content_suggestions_collection_updater.h +++ b/ios/chrome/browser/ui/content_suggestions/content_suggestions_collection_updater.h
@@ -77,10 +77,6 @@ // header containing the fake omnibox and the logo. - (BOOL)isHeaderSection:(NSInteger)section; -// Returns whether |section| is one of the section containing ContentSuggestions -// items. -- (BOOL)isContentSuggestionsSection:(NSInteger)section; - // Updates the number of Most Visited tiles shown for the |size| on the model // only. The collection needs to be updated separately. - (void)updateMostVisitedForSize:(CGSize)size;
diff --git a/ios/chrome/browser/ui/content_suggestions/content_suggestions_collection_updater.mm b/ios/chrome/browser/ui/content_suggestions/content_suggestions_collection_updater.mm index ee9c3a15d..0752881 100644 --- a/ios/chrome/browser/ui/content_suggestions/content_suggestions_collection_updater.mm +++ b/ios/chrome/browser/ui/content_suggestions/content_suggestions_collection_updater.mm
@@ -20,7 +20,6 @@ #import "ios/chrome/browser/ui/content_suggestions/content_suggestions_collection_utils.h" #import "ios/chrome/browser/ui/content_suggestions/content_suggestions_data_sink.h" #import "ios/chrome/browser/ui/content_suggestions/content_suggestions_data_source.h" -#import "ios/chrome/browser/ui/content_suggestions/content_suggestions_metrics_recording.h" #import "ios/chrome/browser/ui/content_suggestions/content_suggestions_view_controller.h" #import "ios/chrome/browser/ui/content_suggestions/content_suggestions_view_controller_audience.h" #import "ios/chrome/browser/ui/content_suggestions/identifier/content_suggestion_identifier.h" @@ -126,7 +125,7 @@ } // Returns whether this |sectionIdentifier| comes from ContentSuggestions. -BOOL IsFromContentSuggestionsService(NSInteger sectionIdentifier) { +BOOL IsFromContentSuggestions(NSInteger sectionIdentifier) { return sectionIdentifier == SectionIdentifierArticles || sectionIdentifier == SectionIdentifierReadingList; } @@ -407,7 +406,7 @@ [model itemsInSectionWithIdentifier:sectionIdentifier].count > 0) { return @[]; } - } else if (IsFromContentSuggestionsService(sectionIdentifier)) { + } else if (IsFromContentSuggestions(sectionIdentifier)) { // If the section is a ContentSuggestions section, add the "Learn more" // items if they are not already present. if ([model hasSectionForSectionIdentifier:SectionIdentifierLearnMore] && @@ -531,12 +530,6 @@ sectionIdentifierForSection:section] == SectionIdentifierPromo; } -- (BOOL)isContentSuggestionsSection:(NSInteger)section { - return IsFromContentSuggestionsService( - [self.collectionViewController.collectionViewModel - sectionIdentifierForSection:section]); -} - - (void)updateMostVisitedForSize:(CGSize)size { self.collectionWidth = size.width; @@ -621,7 +614,7 @@ sectionInfo.title) { BOOL addHeader = YES; - if (IsFromContentSuggestionsService(sectionIdentifier)) { + if (IsFromContentSuggestions(sectionIdentifier)) { addHeader = NO; if ([self.sectionIdentifiersFromContentSuggestions @@ -690,19 +683,6 @@ cell:(ContentSuggestionsFooterCell*)cell { SectionIdentifier sectionIdentifier = SectionIdentifierForInfo(sectionInfo); - CSCollectionViewModel* model = - self.collectionViewController.collectionViewModel; - if (![model hasSectionForSectionIdentifier:sectionIdentifier]) - return; - - // The more button is the footer of the section. So its position is the number - // of items in the section. - [self.collectionViewController.metricsRecorder - onMoreButtonTappedAtPosition: - [model numberOfItemsInSection: - [model sectionForSectionIdentifier:sectionIdentifier]] - inSection:sectionInfo]; - item.loading = YES; [item configureCell:cell]; @@ -710,7 +690,8 @@ [NSMutableArray array]; NSArray<CSCollectionViewItem*>* knownSuggestions = - [model itemsInSectionWithIdentifier:sectionIdentifier]; + [self.collectionViewController.collectionViewModel + itemsInSectionWithIdentifier:sectionIdentifier]; for (CSCollectionViewItem* suggestion in knownSuggestions) { if (suggestion.type != ItemTypeEmpty) { [knownSuggestionIdentifiers addObject:suggestion.suggestionIdentifier];
diff --git a/ios/chrome/browser/ui/content_suggestions/content_suggestions_commands.h b/ios/chrome/browser/ui/content_suggestions/content_suggestions_commands.h index 3e257b2..f7f1210 100644 --- a/ios/chrome/browser/ui/content_suggestions/content_suggestions_commands.h +++ b/ios/chrome/browser/ui/content_suggestions/content_suggestions_commands.h
@@ -13,8 +13,8 @@ // Opens the Reading List. - (void)openReadingList; -// Opens the page associated with the item at |indexPath|. -- (void)openPageForItemAtIndexPath:(nonnull NSIndexPath*)indexPath; +// Opens the page associated with this |item|. +- (void)openPageForItem:(nonnull CollectionViewItem*)item; // Opens the Most Visited associated with this |item| at the |mostVisitedItem|. - (void)openMostVisitedItem:(nonnull CollectionViewItem*)item atIndex:(NSInteger)mostVisitedIndex;
diff --git a/ios/chrome/browser/ui/content_suggestions/content_suggestions_metrics_recording.h b/ios/chrome/browser/ui/content_suggestions/content_suggestions_metrics_recording.h deleted file mode 100644 index cd4e9f4..0000000 --- a/ios/chrome/browser/ui/content_suggestions/content_suggestions_metrics_recording.h +++ /dev/null
@@ -1,39 +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 IOS_CHROME_BROWSER_UI_CONTENT_SUGGESTIONS_CONTENT_SUGGESTIONS_METRICS_RECORDING_H_ -#define IOS_CHROME_BROWSER_UI_CONTENT_SUGGESTIONS_CONTENT_SUGGESTIONS_METRICS_RECORDING_H_ - -#import <UIKit/UIKIt.h> - -#include "ui/base/window_open_disposition.h" - -@class CollectionViewItem; -@class ContentSuggestionsSectionInformation; -@protocol SuggestedContent; - -// Protocol for recording metrics related to ContentSuggestions. -@protocol ContentSuggestionsMetricsRecording - -// Records the appearance of an |item| suggestion at |indexPath|. Needs the -// number of |suggestionsAbove| the item's section. -- (void)onSuggestionShown:(CollectionViewItem*)item - atIndexPath:(NSIndexPath*)indexPath - suggestionsShownAbove:(NSInteger)suggestionsAbove; - -// Records a tap on a more button in the section associated with |sectionInfo|. -// Needs the button |position| in the section. -- (void)onMoreButtonTappedAtPosition:(NSInteger)position - inSection:(ContentSuggestionsSectionInformation*) - sectionInfo; - -// Records the dismissal of a suggestion |item| at |indexPath|.Needs the number -// of |suggestionsAbove| the item's section. -- (void)onSuggestionDismissed:(CollectionViewItem<SuggestedContent>*)item - atIndexPath:(NSIndexPath*)indexPath - suggestionsShownAbove:(NSInteger)suggestionsAbove; - -@end - -#endif // IOS_CHROME_BROWSER_UI_CONTENT_SUGGESTIONS_CONTENT_SUGGESTIONS_METRICS_RECORDING_H_
diff --git a/ios/chrome/browser/ui/content_suggestions/content_suggestions_view_controller.h b/ios/chrome/browser/ui/content_suggestions/content_suggestions_view_controller.h index a9a1f5d..1c9b286 100644 --- a/ios/chrome/browser/ui/content_suggestions/content_suggestions_view_controller.h +++ b/ios/chrome/browser/ui/content_suggestions/content_suggestions_view_controller.h
@@ -14,7 +14,6 @@ @protocol ContentSuggestionsCommands; @protocol ContentSuggestionsDataSource; @protocol ContentSuggestionsHeaderSynchronizing; -@protocol ContentSuggestionsMetricsRecording; @protocol ContentSuggestionsViewControllerAudience; @protocol ContentSuggestionsViewControllerDelegate; @protocol OverscrollActionsControllerDelegate; @@ -45,8 +44,6 @@ // Delegate for the overscroll actions. @property(nonatomic, weak) id<OverscrollActionsControllerDelegate> overscrollDelegate; -@property(nonatomic, weak) id<ContentSuggestionsMetricsRecording> - metricsRecorder; - (void)setDataSource:(id<ContentSuggestionsDataSource>)dataSource; @@ -59,11 +56,6 @@ - (void)addSuggestions: (NSArray<CollectionViewItem<SuggestedContent>*>*)suggestions toSectionInfo:(ContentSuggestionsSectionInformation*)sectionInfo; -// Returns the number of suggestions displayed above this |section|. -- (NSInteger)numberOfSuggestionsAbove:(NSInteger)section; -// Returns the number of sections containing suggestions displayed above this -// |section|. -- (NSInteger)numberOfSectionsAbove:(NSInteger)section; // Returns the accessibility identifier of the collection. + (NSString*)collectionAccessibilityIdentifier;
diff --git a/ios/chrome/browser/ui/content_suggestions/content_suggestions_view_controller.mm b/ios/chrome/browser/ui/content_suggestions/content_suggestions_view_controller.mm index 84217d5..783afb3 100644 --- a/ios/chrome/browser/ui/content_suggestions/content_suggestions_view_controller.mm +++ b/ios/chrome/browser/ui/content_suggestions/content_suggestions_view_controller.mm
@@ -18,7 +18,6 @@ #import "ios/chrome/browser/ui/content_suggestions/content_suggestions_commands.h" #import "ios/chrome/browser/ui/content_suggestions/content_suggestions_header_synchronizing.h" #import "ios/chrome/browser/ui/content_suggestions/content_suggestions_layout.h" -#import "ios/chrome/browser/ui/content_suggestions/content_suggestions_metrics_recording.h" #import "ios/chrome/browser/ui/content_suggestions/content_suggestions_view_controller_audience.h" #import "ios/chrome/browser/ui/content_suggestions/content_suggestions_view_controller_delegate.h" #import "ios/chrome/browser/ui/content_suggestions/ntp_home_constant.h" @@ -63,7 +62,6 @@ @synthesize overscrollActionsController = _overscrollActionsController; @synthesize overscrollDelegate = _overscrollDelegate; @synthesize scrolledToTop = _scrolledToTop; -@synthesize metricsRecorder = _metricsRecorder; @dynamic collectionViewModel; #pragma mark - Lifecycle @@ -97,11 +95,6 @@ return; } - [self.metricsRecorder - onSuggestionDismissed:[self.collectionViewModel itemAtIndexPath:indexPath] - atIndexPath:indexPath - suggestionsShownAbove:[self numberOfSuggestionsAbove:indexPath.section]]; - [self.collectionView performBatchUpdates:^{ [self collectionView:self.collectionView willDeleteItemsAtIndexPaths:@[ indexPath ]]; @@ -169,27 +162,6 @@ }]; } -- (NSInteger)numberOfSuggestionsAbove:(NSInteger)section { - NSInteger suggestionsAbove = 0; - for (NSInteger sectionAbove = 0; sectionAbove < section; sectionAbove++) { - if ([self.collectionUpdater isContentSuggestionsSection:sectionAbove]) { - suggestionsAbove += - [self.collectionViewModel numberOfItemsInSection:sectionAbove]; - } - } - return suggestionsAbove; -} - -- (NSInteger)numberOfSectionsAbove:(NSInteger)section { - NSInteger sectionsAbove = 0; - for (NSInteger sectionAbove = 0; sectionAbove < section; sectionAbove++) { - if ([self.collectionUpdater isContentSuggestionsSection:sectionAbove]) { - sectionsAbove++; - } - } - return sectionsAbove; -} - + (NSString*)collectionAccessibilityIdentifier { return @"ContentSuggestionsCollectionIdentifier"; } @@ -284,10 +256,10 @@ switch ([self.collectionUpdater contentSuggestionTypeForItem:item]) { case ContentSuggestionTypeReadingList: base::RecordAction(base::UserMetricsAction("MobileReadingListOpen")); - [self.suggestionCommandHandler openPageForItemAtIndexPath:indexPath]; + [self.suggestionCommandHandler openPageForItem:item]; break; case ContentSuggestionTypeArticle: - [self.suggestionCommandHandler openPageForItemAtIndexPath:indexPath]; + [self.suggestionCommandHandler openPageForItem:item]; break; case ContentSuggestionTypeMostVisited: [self.suggestionCommandHandler openMostVisitedItem:item @@ -306,23 +278,6 @@ } } -- (UICollectionViewCell*)collectionView:(UICollectionView*)collectionView - cellForItemAtIndexPath:(NSIndexPath*)indexPath { - CSCollectionViewItem* item = - [self.collectionViewModel itemAtIndexPath:indexPath]; - - if (!item.metricsRecorded) { - [self.metricsRecorder - onSuggestionShown:item - atIndexPath:indexPath - suggestionsShownAbove:[self - numberOfSuggestionsAbove:indexPath.section]]; - item.metricsRecorded = YES; - } - - return [super collectionView:collectionView cellForItemAtIndexPath:indexPath]; -} - #pragma mark - UICollectionViewDelegateFlowLayout - (CGSize)collectionView:(UICollectionView*)collectionView
diff --git a/ios/chrome/browser/ui/payments/payment_request_egtest_base.mm b/ios/chrome/browser/ui/payments/payment_request_egtest_base.mm index 2ea450da..2c8315a 100644 --- a/ios/chrome/browser/ui/payments/payment_request_egtest_base.mm +++ b/ios/chrome/browser/ui/payments/payment_request_egtest_base.mm
@@ -7,12 +7,14 @@ #include <algorithm> #include <memory> +#include "base/feature_list.h" #include "base/logging.h" #include "base/memory/ptr_util.h" #include "base/strings/sys_string_conversions.h" #include "components/autofill/core/browser/autofill_profile.h" #include "components/autofill/core/browser/credit_card.h" #include "components/autofill/core/browser/personal_data_manager.h" +#include "components/payments/core/features.h" #include "ios/chrome/browser/autofill/personal_data_manager_factory.h" #include "ios/chrome/browser/payments/ios_payment_request_cache_factory.h" #import "ios/chrome/test/app/chrome_test_util.h" @@ -47,6 +49,16 @@ #pragma mark - XCTestCase ++ (void)setUp { + [super setUp]; + if (!base::FeatureList::IsEnabled(payments::features::kWebPayments)) { + // payments::features::kWebPayments feature is not enabled, + // You have to pass --enable-features=WebPayments command line argument in + // order to run this test. + DCHECK(false); + } +} + - (void)tearDown { for (const auto& profile : _profiles) { [self personalDataManager]->RemoveByGUID(profile.guid());
diff --git a/ios/chrome/browser/ui/settings/compose_email_handler_collection_view_controller_unittest.mm b/ios/chrome/browser/ui/settings/compose_email_handler_collection_view_controller_unittest.mm index 80e1ef0f..4105ea7 100644 --- a/ios/chrome/browser/ui/settings/compose_email_handler_collection_view_controller_unittest.mm +++ b/ios/chrome/browser/ui/settings/compose_email_handler_collection_view_controller_unittest.mm
@@ -19,10 +19,6 @@ #pragma mark - MailtoURLRewriter private interface for testing. -@interface MailtoURLRewriter () -- (void)addMailtoApps:(NSArray<MailtoHandler*>*)handlerApps; -@end - #pragma mark - ComposeEmailHandlerCollectionViewControllerTest class ComposeEmailHandlerCollectionViewControllerTest @@ -34,7 +30,7 @@ // CollectionViewController. CollectionViewController* InstantiateController() override { rewriter_ = [[LegacyMailtoURLRewriter alloc] init]; - [rewriter_ addMailtoApps:handlers_]; + [rewriter_ setDefaultHandlers:handlers_]; if (defaultHandlerID_) [rewriter_ setDefaultHandlerID:defaultHandlerID_]; return [[ComposeEmailHandlerCollectionViewController alloc]
diff --git a/ios/chrome/browser/ui/settings/content_settings_collection_view_controller.mm b/ios/chrome/browser/ui/settings/content_settings_collection_view_controller.mm index 13d4de7..a8a6eef 100644 --- a/ios/chrome/browser/ui/settings/content_settings_collection_view_controller.mm +++ b/ios/chrome/browser/ui/settings/content_settings_collection_view_controller.mm
@@ -108,7 +108,7 @@ [_disablePopupsSetting setObserver:self]; _mailtoURLRewriter = - [[LegacyMailtoURLRewriter alloc] initWithStandardHandlers]; + [LegacyMailtoURLRewriter mailtoURLRewriterWithStandardHandlers]; [_mailtoURLRewriter setObserver:self]; [self loadModel];
diff --git a/ios/chrome/browser/web/BUILD.gn b/ios/chrome/browser/web/BUILD.gn index 5d97c1de7..3af6b5d 100644 --- a/ios/chrome/browser/web/BUILD.gn +++ b/ios/chrome/browser/web/BUILD.gn
@@ -25,6 +25,8 @@ "navigation_manager_util.mm", "network_activity_indicator_tab_helper.h", "network_activity_indicator_tab_helper.mm", + "nullable_mailto_url_rewriter.h", + "nullable_mailto_url_rewriter.mm", "page_placeholder_tab_helper.h", "page_placeholder_tab_helper.mm", "repost_form_tab_helper.h", @@ -68,6 +70,7 @@ "mailto_handler_unittest.mm", "navigation_manager_util_unittest.mm", "network_activity_indicator_tab_helper_unittest.mm", + "nullable_mailto_url_rewriter_unittest.mm", "page_placeholder_tab_helper_unittest.mm", "repost_form_tab_helper_unittest.mm", "sad_tab_tab_helper_unittest.mm",
diff --git a/ios/chrome/browser/web/external_app_launcher.mm b/ios/chrome/browser/web/external_app_launcher.mm index 77b4b4ed..ecc8c4f 100644 --- a/ios/chrome/browser/web/external_app_launcher.mm +++ b/ios/chrome/browser/web/external_app_launcher.mm
@@ -176,7 +176,7 @@ if (base::FeatureList::IsEnabled(kMailtoUrlRewriting) && gURL.SchemeIs(url::kMailToScheme)) { MailtoURLRewriter* rewriter = - [[LegacyMailtoURLRewriter alloc] initWithStandardHandlers]; + [LegacyMailtoURLRewriter mailtoURLRewriterWithStandardHandlers]; NSString* launchURL = [rewriter rewriteMailtoURL:gURL]; if (launchURL) URL = [NSURL URLWithString:launchURL];
diff --git a/ios/chrome/browser/web/legacy_mailto_url_rewriter.mm b/ios/chrome/browser/web/legacy_mailto_url_rewriter.mm index 9ba570c..b26e6c5 100644 --- a/ios/chrome/browser/web/legacy_mailto_url_rewriter.mm +++ b/ios/chrome/browser/web/legacy_mailto_url_rewriter.mm
@@ -15,14 +15,6 @@ #error "This file requires ARC support." #endif -namespace { -// The key for NSUserDefaults to store the Mail client selected to handle -// mailto: URL scheme. If this key is not set, user has not made an explicit -// choice for default mailto: handler and system-provided Mail client app will -// be used. -NSString* const kMailtoDefaultHandlerKey = @"MailtoHandlerDefault"; -} // namespace - @interface LegacyMailtoURLRewriter () // Dictionary keyed by the App Store ID of the Mail client and the value is @@ -30,13 +22,6 @@ @property(nonatomic, strong) NSMutableDictionary<NSString*, MailtoHandler*>* handlers; -// Private method for testing to clear the default state. -+ (void)resetDefaultHandlerIDForTesting; - -// Private method to add one or more |handlerApp| objects to the list of known -// Mail client apps. -- (void)addMailtoApps:(NSArray<MailtoHandler*>*)handlerApps; - // Custom logic to handle the migration from Google Native App Launcher options // to this simplified mailto: URL only system. This must be called after // -addMailtoApp: has been called to add all the known Mail client apps. @@ -54,6 +39,22 @@ @implementation LegacyMailtoURLRewriter @synthesize handlers = _handlers; ++ (NSString*)userDefaultsKey { + // The key for NSUserDefaults to store the Mail client selected to handle + // mailto: URL scheme. If this key is not set, user has not made an explicit + // choice for default mailto: handler and system-provided Mail client app will + // be used. + return @"MailtoHandlerDefault"; +} + ++ (instancetype)mailtoURLRewriterWithStandardHandlers { + id result = [[LegacyMailtoURLRewriter alloc] init]; + [result setDefaultHandlers:@[ + [[MailtoHandlerSystemMail alloc] init], [[MailtoHandlerGmail alloc] init] + ]]; + return result; +} + - (instancetype)init { self = [super init]; if (self) { @@ -62,16 +63,6 @@ return self; } -- (instancetype)initWithStandardHandlers { - self = [self init]; - if (self) { - [self addMailtoApps:@[ - [[MailtoHandlerSystemMail alloc] init], [[MailtoHandlerGmail alloc] init] - ]]; - } - return self; -} - - (NSArray<MailtoHandler*>*)defaultHandlers { return [[_handlers allValues] sortedArrayUsingComparator:^NSComparisonResult( @@ -80,20 +71,33 @@ }]; } +- (void)setDefaultHandlers:(NSArray<MailtoHandler*>*)handlerApps { + for (MailtoHandler* app in handlerApps) { + [_handlers setObject:app forKey:[app appStoreID]]; + } + [self migrateLegacyOptions]; + [self autoDefaultToGmailIfInstalled]; +} + - (NSString*)defaultHandlerID { NSString* value = [[NSUserDefaults standardUserDefaults] - stringForKey:kMailtoDefaultHandlerKey]; + stringForKey:[[self class] userDefaultsKey]]; + // This implementation of MailtoURLRewriter always returns a non-nil mailto: + // URL handler ID. if ([_handlers[value] isAvailable]) return value; return [[self class] systemMailApp]; } - (void)setDefaultHandlerID:(NSString*)appStoreID { + // This implementation of MailtoURLRewriter does not allow the unsetting of + // a mailto: URL handler. DCHECK([appStoreID length]); NSUserDefaults* defaults = [NSUserDefaults standardUserDefaults]; - if ([appStoreID isEqual:[defaults objectForKey:kMailtoDefaultHandlerKey]]) + NSString* defaultsKey = [[self class] userDefaultsKey]; + if ([appStoreID isEqual:[defaults objectForKey:defaultsKey]]) return; - [defaults setObject:appStoreID forKey:kMailtoDefaultHandlerKey]; + [defaults setObject:appStoreID forKey:defaultsKey]; [self.observer rewriterDidChange:self]; } @@ -116,19 +120,6 @@ #pragma mark - Private -+ (void)resetDefaultHandlerIDForTesting { - [[NSUserDefaults standardUserDefaults] - removeObjectForKey:kMailtoDefaultHandlerKey]; -} - -- (void)addMailtoApps:(NSArray<MailtoHandler*>*)handlerApps { - for (MailtoHandler* app in handlerApps) { - [_handlers setObject:app forKey:[app appStoreID]]; - } - [self migrateLegacyOptions]; - [self autoDefaultToGmailIfInstalled]; -} - // // Implements the migration logic for users of previous versions of Google // Chrome which supports Google Native App Launcher. The goal is to preserve @@ -157,6 +148,7 @@ // - (void)migrateLegacyOptions { NSUserDefaults* defaults = [NSUserDefaults standardUserDefaults]; + NSString* defaultsKey = [[self class] userDefaultsKey]; // User previously had a selection made for opening mailto: links with Gmail, // upgrade will set Gmail app to be the default mailto: handler. If user had @@ -178,17 +170,16 @@ // Gmail app. MailtoHandler* gmailHandler = _handlers[kGmailAppStoreID]; if ([gmailHandler isAvailable]) - [defaults setObject:kGmailAppStoreID forKey:kMailtoDefaultHandlerKey]; + [defaults setObject:kGmailAppStoreID forKey:defaultsKey]; else - [defaults removeObjectForKey:kMailtoDefaultHandlerKey]; + [defaults removeObjectForKey:defaultsKey]; break; } case 1: // If legacy user was not using Gmail to handle mailto: links // (kAutoOpenLinksNo), consider this an explicit user choice and // migrate to use system-provided Mail app. - [defaults setObject:[[self class] systemMailApp] - forKey:kMailtoDefaultHandlerKey]; + [defaults setObject:[[self class] systemMailApp] forKey:defaultsKey]; break; default: NOTREACHED(); @@ -200,15 +191,16 @@ - (void)autoDefaultToGmailIfInstalled { NSUserDefaults* defaults = [NSUserDefaults standardUserDefaults]; + NSString* defaultsKey = [[self class] userDefaultsKey]; // If a default handler for mailto: has already been set, user had made an // explicit choice and no further changes should be done. - if ([defaults objectForKey:kMailtoDefaultHandlerKey]) + if ([defaults objectForKey:defaultsKey]) return; NSString* const kGmailAppStoreID = @"422689480"; MailtoHandler* gmailHandler = _handlers[kGmailAppStoreID]; if ([gmailHandler isAvailable]) - [defaults setObject:kGmailAppStoreID forKey:kMailtoDefaultHandlerKey]; + [defaults setObject:kGmailAppStoreID forKey:defaultsKey]; } @end
diff --git a/ios/chrome/browser/web/legacy_mailto_url_rewriter_unittest.mm b/ios/chrome/browser/web/legacy_mailto_url_rewriter_unittest.mm index b7ab354..f25a571d 100644 --- a/ios/chrome/browser/web/legacy_mailto_url_rewriter_unittest.mm +++ b/ios/chrome/browser/web/legacy_mailto_url_rewriter_unittest.mm
@@ -27,26 +27,20 @@ } // namespace -#pragma mark - LegacyMailtoURLRewriter private interfaces for testing. - -@interface LegacyMailtoURLRewriter () -+ (void)resetDefaultHandlerIDForTesting; -- (void)addMailtoApps:(NSArray<MailtoHandler*>*)handlerApps; -@end - #pragma mark - Unit Test Cases class LegacyMailtoURLRewriterTest : public PlatformTest { - protected: - void SetUp() override { - [LegacyMailtoURLRewriter resetDefaultHandlerIDForTesting]; + public: + LegacyMailtoURLRewriterTest() { + [[NSUserDefaults standardUserDefaults] + removeObjectForKey:[LegacyMailtoURLRewriter userDefaultsKey]]; } }; // Tests that a standard instance has the expected values. TEST_F(LegacyMailtoURLRewriterTest, TestStandardInstance) { LegacyMailtoURLRewriter* rewriter = - [[LegacyMailtoURLRewriter alloc] initWithStandardHandlers]; + [LegacyMailtoURLRewriter mailtoURLRewriterWithStandardHandlers]; EXPECT_TRUE(rewriter); EXPECT_GT([[rewriter defaultHandlerName] length], 0U); // ID for system Mail client app must not be an empty string. @@ -73,7 +67,7 @@ MailtoHandler* systemMailHandler = [[MailtoHandlerSystemMail alloc] init]; MailtoHandler* fakeGmailHandler = [[FakeMailtoHandlerGmailNotInstalled alloc] init]; - [rewriter addMailtoApps:@[ systemMailHandler, fakeGmailHandler ]]; + [rewriter setDefaultHandlers:@[ systemMailHandler, fakeGmailHandler ]]; // Sets the default handler to Gmail (which is not installed). This simulates // the situation when Gmail was installed and set as the default handler. // Then Gmail app is deleted from the device. @@ -87,12 +81,12 @@ TEST_F(LegacyMailtoURLRewriterTest, TestUserPreferencePersistence) { // Sets up a first LegacyMailtoURLRewriter with at least 2 MailtoHandler // objects. A faked Gmail handler that is installed must be used or - // -addMailtoApp: will just skip it. + // -setDefaultHandlers: will just skip it. LegacyMailtoURLRewriter* rewriter = [[LegacyMailtoURLRewriter alloc] init]; MailtoHandler* systemMailHandler = [[MailtoHandlerSystemMail alloc] init]; MailtoHandler* fakeGmailHandler = [[FakeMailtoHandlerGmailInstalled alloc] init]; - [rewriter addMailtoApps:@[ systemMailHandler, fakeGmailHandler ]]; + [rewriter setDefaultHandlers:@[ systemMailHandler, fakeGmailHandler ]]; // Verifies that there must be 2 registered handlers. Then find a // MailtoHandler that is not the current default and set that as the new @@ -113,7 +107,7 @@ // Create a new LegacyMailtoURLRewriter object and verify that the current // default is the |otherHandlerID| set in the previous step. LegacyMailtoURLRewriter* rewriter2 = [[LegacyMailtoURLRewriter alloc] init]; - [rewriter2 addMailtoApps:@[ systemMailHandler, fakeGmailHandler ]]; + [rewriter2 setDefaultHandlers:@[ systemMailHandler, fakeGmailHandler ]]; EXPECT_NSEQ(otherHandlerID, [rewriter2 defaultHandlerID]); } @@ -128,7 +122,7 @@ MailtoHandler* systemMailHandler = [[MailtoHandlerSystemMail alloc] init]; MailtoHandler* fakeGmailHandler = [[FakeMailtoHandlerGmailInstalled alloc] init]; - [rewriter addMailtoApps:@[ systemMailHandler, fakeGmailHandler ]]; + [rewriter setDefaultHandlers:@[ systemMailHandler, fakeGmailHandler ]]; EXPECT_NSEQ([fakeGmailHandler appStoreID], [rewriter defaultHandlerID]); [rewriter setObserver:observer]; @@ -166,7 +160,7 @@ // Sets up a LegacyMailtoURLRewriter for testing. LegacyMailtoURLRewriter* rewriter = [[LegacyMailtoURLRewriter alloc] init]; MailtoHandler* systemMailHandler = [[MailtoHandlerSystemMail alloc] init]; - [rewriter addMailtoApps:@[ systemMailHandler, fakeGmailHandler ]]; + [rewriter setDefaultHandlers:@[ systemMailHandler, fakeGmailHandler ]]; // Verify that LegacyMailtoURLRewriter will use the system Mail app. EXPECT_NSEQ([LegacyMailtoURLRewriter systemMailApp], @@ -185,7 +179,7 @@ // Sets up a LegacyMailtoURLRewriter for testing. LegacyMailtoURLRewriter* rewriter = [[LegacyMailtoURLRewriter alloc] init]; MailtoHandler* systemMailHandler = [[MailtoHandlerSystemMail alloc] init]; - [rewriter addMailtoApps:@[ systemMailHandler, fakeGmailHandler ]]; + [rewriter setDefaultHandlers:@[ systemMailHandler, fakeGmailHandler ]]; // Verify that LegacyMailtoURLRewriter will use Gmail app. EXPECT_NSEQ(kGmailAppStoreID, [rewriter defaultHandlerID]); @@ -205,7 +199,7 @@ // Sets up a LegacyMailtoURLRewriter for testing. LegacyMailtoURLRewriter* rewriter = [[LegacyMailtoURLRewriter alloc] init]; MailtoHandler* systemMailHandler = [[MailtoHandlerSystemMail alloc] init]; - [rewriter addMailtoApps:@[ systemMailHandler, fakeGmailHandler ]]; + [rewriter setDefaultHandlers:@[ systemMailHandler, fakeGmailHandler ]]; // Verify that LegacyMailtoURLRewriter will use the system Mail app. As part // of the "upgrade", the legacy key should be removed as well. @@ -227,7 +221,7 @@ // Sets up a LegacyMailtoURLRewriter for testing. LegacyMailtoURLRewriter* rewriter = [[LegacyMailtoURLRewriter alloc] init]; MailtoHandler* systemMailHandler = [[MailtoHandlerSystemMail alloc] init]; - [rewriter addMailtoApps:@[ systemMailHandler, fakeGmailHandler ]]; + [rewriter setDefaultHandlers:@[ systemMailHandler, fakeGmailHandler ]]; // Verify that LegacyMailtoURLRewriter will use Gmail app. As part of the // upgrade, the legacy key should be removed as well. @@ -248,7 +242,7 @@ // Sets up a LegacyMailtoURLRewriter for testing. LegacyMailtoURLRewriter* rewriter = [[LegacyMailtoURLRewriter alloc] init]; MailtoHandler* systemMailHandler = [[MailtoHandlerSystemMail alloc] init]; - [rewriter addMailtoApps:@[ systemMailHandler, fakeGmailHandler ]]; + [rewriter setDefaultHandlers:@[ systemMailHandler, fakeGmailHandler ]]; // Verify that LegacyMailtoURLRewriter will use Gmail app. EXPECT_NSEQ(kGmailAppStoreID, [rewriter defaultHandlerID]);
diff --git a/ios/chrome/browser/web/mailto_url_rewriter.h b/ios/chrome/browser/web/mailto_url_rewriter.h index e58c4788..16e1b94e56 100644 --- a/ios/chrome/browser/web/mailto_url_rewriter.h +++ b/ios/chrome/browser/web/mailto_url_rewriter.h
@@ -24,30 +24,33 @@ @interface MailtoURLRewriter : NSObject // The unique ID of the Mail client app that handles mailto: URL scheme. +// This has a value of nil if default has not been set. @property(nonatomic, copy) NSString* defaultHandlerID; +// Array of all the currently supported Mail client apps that claim to handle +// mailto: URL scheme through their own custom defined URL schemes. +@property(nonatomic, strong) NSArray<MailtoHandler*>* defaultHandlers; + // Observer object that will be called when |defaultHandlerID| is changed. @property(nonatomic, weak) id<MailtoURLRewriterObserver> observer; +// Returns the NSString* key to store state in NSUserDefaults. ++ (NSString*)userDefaultsKey; + // Returns the ID as a string for the system-provided Mail client app. + (NSString*)systemMailApp; -// An initializer returning an instance that has the standard set of -// MailtoHandlers initialized. Unit tests can use -init and then set up the -// different handlers. -- (instancetype)initWithStandardHandlers; +// Convenience method to return a new instance of this class initialized with +// a standard set of MailtoHandlers. ++ (instancetype)mailtoURLRewriterWithStandardHandlers; -// Returns a sorted array of all the currently supported Mail client apps that -// claim to handle mailto: URL scheme through their own custom defined URL -// schemes. -- (NSArray<MailtoHandler*>*)defaultHandlers; - -// Returns the name of the application that handles mailto: URLs. +// Returns the name of the application that handles mailto: URLs. Returns nil +// if a default has not been set. - (NSString*)defaultHandlerName; // Rewrites |URL| into a new URL that can be "opened" to launch the Mail -// client app. May return nil if |URL| is not a mailto: URL or there are no -// Mail client app available. +// client app. May return nil if |URL| is not a mailto: URL, a mail client +// app has not been selected, or there are no Mail client app available. - (NSString*)rewriteMailtoURL:(const GURL&)URL; @end
diff --git a/ios/chrome/browser/web/mailto_url_rewriter.mm b/ios/chrome/browser/web/mailto_url_rewriter.mm index 3fa669a..060ba2ea 100644 --- a/ios/chrome/browser/web/mailto_url_rewriter.mm +++ b/ios/chrome/browser/web/mailto_url_rewriter.mm
@@ -17,6 +17,7 @@ @implementation MailtoURLRewriter @synthesize observer = _observer; +@dynamic defaultHandlers; - (NSString*)defaultHandlerID { NOTREACHED(); @@ -27,15 +28,23 @@ NOTREACHED(); } ++ (NSString*)userDefaultsKey { + return nil; +} + + (NSString*)systemMailApp { // This is the App Store ID for Apple Mail app. // See https://itunes.apple.com/us/app/mail/id1108187098?mt=8 return @"1108187098"; } -- (instancetype)initWithStandardHandlers { ++ (instancetype)mailtoURLRewriterWithStandardHandlers { NOTREACHED(); - return self; + return nil; +} + +- (void)setDefaultHandlers:(NSArray<MailtoHandler*>*)defaultHandlers { + NOTREACHED(); } - (NSArray<MailtoHandler*>*)defaultHandlers {
diff --git a/ios/chrome/browser/web/nullable_mailto_url_rewriter.h b/ios/chrome/browser/web/nullable_mailto_url_rewriter.h new file mode 100644 index 0000000..9f15241b --- /dev/null +++ b/ios/chrome/browser/web/nullable_mailto_url_rewriter.h
@@ -0,0 +1,18 @@ +// 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 IOS_CHROME_BROWSER_WEB_NULLABLE_MAILTO_URL_REWRITER_H_ +#define IOS_CHROME_BROWSER_WEB_NULLABLE_MAILTO_URL_REWRITER_H_ + +#import "ios/chrome/browser/web/mailto_url_rewriter.h" + +// An object that manages the available Mail client apps. The currently selected +// Mail client to handle mailto: URL is stored in a key in NSUserDefaults. If a +// default has not been set in NSUserDefaults, nil may be returned from some of +// the public APIs of MailtoURLRewriter. +@interface NullableMailtoURLRewriter : MailtoURLRewriter + +@end + +#endif // IOS_CHROME_BROWSER_WEB_NULLABLE_MAILTO_URL_REWRITER_H_
diff --git a/ios/chrome/browser/web/nullable_mailto_url_rewriter.mm b/ios/chrome/browser/web/nullable_mailto_url_rewriter.mm new file mode 100644 index 0000000..dc4a18d3 --- /dev/null +++ b/ios/chrome/browser/web/nullable_mailto_url_rewriter.mm
@@ -0,0 +1,113 @@ +// 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. + +#import "ios/chrome/browser/web/nullable_mailto_url_rewriter.h" + +#import <UIKit/UIKit.h> + +#import "base/logging.h" +#import "ios/chrome/browser/web/mailto_handler.h" +#import "ios/chrome/browser/web/mailto_handler_gmail.h" +#import "ios/chrome/browser/web/mailto_handler_system_mail.h" + +#if !defined(__has_feature) || !__has_feature(objc_arc) +#error "This file requires ARC support." +#endif + +@interface NullableMailtoURLRewriter () + +// Dictionary keyed by the unique ID of the Mail client. The value is +// the MailtoHandler object that can rewrite a mailto: URL. +@property(nonatomic, strong) + NSMutableDictionary<NSString*, MailtoHandler*>* handlers; + +@end + +@implementation NullableMailtoURLRewriter +@synthesize handlers = _handlers; + ++ (NSString*)userDefaultsKey { + // This key in NSUserDefaults stores the default handler ID stored. + return @"UserChosenDefaultMailApp"; +} + ++ (instancetype)mailtoURLRewriterWithStandardHandlers { + id result = [[NullableMailtoURLRewriter alloc] init]; + [result setDefaultHandlers:@[ + [[MailtoHandlerSystemMail alloc] init], [[MailtoHandlerGmail alloc] init] + ]]; + return result; +} + +- (instancetype)init { + self = [super init]; + if (self) { + _handlers = [NSMutableDictionary dictionary]; + } + return self; +} + +- (NSArray<MailtoHandler*>*)defaultHandlers { + return [[_handlers allValues] + sortedArrayUsingComparator:^NSComparisonResult( + MailtoHandler* _Nonnull obj1, MailtoHandler* _Nonnull obj2) { + return [[obj1 appName] compare:[obj2 appName]]; + }]; +} + +- (void)setDefaultHandlers:(NSArray<MailtoHandler*>*)defaultHandlers { + for (MailtoHandler* app in defaultHandlers) { + [_handlers setObject:app forKey:[app appStoreID]]; + } +} + +- (NSString*)defaultHandlerID { + NSString* value = [[NSUserDefaults standardUserDefaults] + stringForKey:[[self class] userDefaultsKey]]; + if (value) { + if ([_handlers[value] isAvailable]) + return value; + return [[self class] systemMailApp]; + } + // User has not made a choice. + NSMutableArray* availableHandlers = [NSMutableArray array]; + for (MailtoHandler* handler in [_handlers allValues]) { + if ([handler isAvailable]) + [availableHandlers addObject:handler]; + } + if ([availableHandlers count] == 1) + return [[availableHandlers firstObject] appStoreID]; + return nil; +} + +- (void)setDefaultHandlerID:(NSString*)appStoreID { + DCHECK([appStoreID length]); + NSUserDefaults* defaults = [NSUserDefaults standardUserDefaults]; + NSString* defaultsKey = [[self class] userDefaultsKey]; + if ([appStoreID isEqual:[defaults objectForKey:defaultsKey]]) + return; + [defaults setObject:appStoreID forKey:defaultsKey]; + [self.observer rewriterDidChange:self]; +} + +- (NSString*)defaultHandlerName { + NSString* handlerID = [self defaultHandlerID]; + if (!handlerID) + return nil; + MailtoHandler* handler = _handlers[handlerID]; + return [handler appName]; +} + +- (NSString*)rewriteMailtoURL:(const GURL&)gURL { + NSString* value = [self defaultHandlerID]; + if ([value length]) { + MailtoHandler* handler = _handlers[value]; + if ([handler isAvailable]) { + return [handler rewriteMailtoURL:gURL]; + } + } + return nil; +} + +@end
diff --git a/ios/chrome/browser/web/nullable_mailto_url_rewriter_unittest.mm b/ios/chrome/browser/web/nullable_mailto_url_rewriter_unittest.mm new file mode 100644 index 0000000..80a1464 --- /dev/null +++ b/ios/chrome/browser/web/nullable_mailto_url_rewriter_unittest.mm
@@ -0,0 +1,113 @@ +// 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. + +#import "ios/chrome/browser/web/nullable_mailto_url_rewriter.h" + +#import "ios/chrome/browser/web/fake_mailto_handler_helpers.h" +#import "ios/chrome/browser/web/mailto_handler_system_mail.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "testing/gtest_mac.h" +#include "testing/platform_test.h" + +#if !defined(__has_feature) || !__has_feature(objc_arc) +#error "This file requires ARC support." +#endif + +class NullableMailtoURLRewriterTest : public PlatformTest { + public: + NullableMailtoURLRewriterTest() { + [[NSUserDefaults standardUserDefaults] + removeObjectForKey:[NullableMailtoURLRewriter userDefaultsKey]]; + } +}; + +// Tests that a new instance has expected properties and behaviors. +TEST_F(NullableMailtoURLRewriterTest, TestStandardInstance) { + NullableMailtoURLRewriter* rewriter = + [NullableMailtoURLRewriter mailtoURLRewriterWithStandardHandlers]; + EXPECT_TRUE(rewriter); + + NSArray<MailtoHandler*>* handlers = [rewriter defaultHandlers]; + EXPECT_GE([handlers count], 1U); + for (MailtoHandler* handler in handlers) { + ASSERT_TRUE(handler); + NSString* appStoreID = [handler appStoreID]; + NSString* expectedDefaultAppID = + [handler isAvailable] ? appStoreID : [MailtoURLRewriter systemMailApp]; + [rewriter setDefaultHandlerID:appStoreID]; + EXPECT_NSEQ(expectedDefaultAppID, [rewriter defaultHandlerID]); + } +} + +// If Gmail is not installed, rewriter defaults to system Mail app. +TEST_F(NullableMailtoURLRewriterTest, TestNoGmailInstalled) { + NullableMailtoURLRewriter* rewriter = + [[NullableMailtoURLRewriter alloc] init]; + [rewriter setDefaultHandlers:@[ + [[MailtoHandlerSystemMail alloc] init], + [[FakeMailtoHandlerGmailNotInstalled alloc] init] + ]]; + EXPECT_NSEQ([MailtoURLRewriter systemMailApp], [rewriter defaultHandlerID]); +} + +// If Gmail is installed but user has not made a choice, there is no default +// mail app. +TEST_F(NullableMailtoURLRewriterTest, TestWithGmailChoiceNotMade) { + NullableMailtoURLRewriter* rewriter = + [[NullableMailtoURLRewriter alloc] init]; + [rewriter setDefaultHandlers:@[ + [[MailtoHandlerSystemMail alloc] init], + [[FakeMailtoHandlerGmailInstalled alloc] init] + ]]; + EXPECT_FALSE([rewriter defaultHandlerID]); +} + +// If Gmail was installed and user has made a choice, then Gmail is uninstalled. +// The default returns to system Mail app. +TEST_F(NullableMailtoURLRewriterTest, TestWithGmailUninstalled) { + NullableMailtoURLRewriter* rewriter = + [[NullableMailtoURLRewriter alloc] init]; + MailtoHandler* systemMailHandler = [[MailtoHandlerSystemMail alloc] init]; + MailtoHandler* fakeGmailHandler = + [[FakeMailtoHandlerGmailInstalled alloc] init]; + [rewriter setDefaultHandlers:@[ systemMailHandler, fakeGmailHandler ]]; + [rewriter setDefaultHandlerID:[fakeGmailHandler appStoreID]]; + EXPECT_NSEQ([fakeGmailHandler appStoreID], [rewriter defaultHandlerID]); + + rewriter = [[NullableMailtoURLRewriter alloc] init]; + fakeGmailHandler = [[FakeMailtoHandlerGmailNotInstalled alloc] init]; + [rewriter setDefaultHandlers:@[ systemMailHandler, fakeGmailHandler ]]; + EXPECT_NSEQ([MailtoURLRewriter systemMailApp], [rewriter defaultHandlerID]); +} + +// If Gmail is installed but system Mail app has been chosen by user as the +// default mail handler app. Then Gmail is uninstalled. User's choice of system +// Mail app remains unchanged and will persist through a re-installation of +// Gmail. +TEST_F(NullableMailtoURLRewriterTest, + TestSystemMailAppChosenSurviveGmailUninstall) { + // Initial state of system Mail app explicitly chosen. + NullableMailtoURLRewriter* rewriter = + [[NullableMailtoURLRewriter alloc] init]; + MailtoHandler* systemMailHandler = [[MailtoHandlerSystemMail alloc] init]; + [rewriter setDefaultHandlers:@[ + systemMailHandler, [[FakeMailtoHandlerGmailInstalled alloc] init] + ]]; + [rewriter setDefaultHandlerID:[systemMailHandler appStoreID]]; + EXPECT_NSEQ([systemMailHandler appStoreID], [rewriter defaultHandlerID]); + + // Gmail is installed. + rewriter = [[NullableMailtoURLRewriter alloc] init]; + [rewriter setDefaultHandlers:@[ + systemMailHandler, [[FakeMailtoHandlerGmailNotInstalled alloc] init] + ]]; + EXPECT_NSEQ([systemMailHandler appStoreID], [rewriter defaultHandlerID]); + + // Gmail is installed again. + rewriter = [[NullableMailtoURLRewriter alloc] init]; + [rewriter setDefaultHandlers:@[ + systemMailHandler, [[FakeMailtoHandlerGmailInstalled alloc] init] + ]]; + EXPECT_NSEQ([systemMailHandler appStoreID], [rewriter defaultHandlerID]); +}
diff --git a/ios/chrome/browser/web/sad_tab_tab_helper.h b/ios/chrome/browser/web/sad_tab_tab_helper.h index 1f7cffc..93a994eb 100644 --- a/ios/chrome/browser/web/sad_tab_tab_helper.h +++ b/ios/chrome/browser/web/sad_tab_tab_helper.h
@@ -2,11 +2,12 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#import "ios/web/public/web_state/web_state_observer.h" -#import "ios/web/public/web_state/web_state_user_data.h" +#import <Foundation/Foundation.h> #include "base/macros.h" #include "base/timer/elapsed_timer.h" +#import "ios/web/public/web_state/web_state_observer.h" +#import "ios/web/public/web_state/web_state_user_data.h" // SadTabTabHelper listens to RenderProcessGone events and presents a // SadTabView view appropriately. @@ -61,6 +62,10 @@ void DidFinishNavigation(web::NavigationContext* navigation_context) override; void WebStateDestroyed() override; + // The default window of time a failure of the same URL needs to occur + // to be considered a repeat failure. + static const double kDefaultRepeatFailureInterval; + // Stores the last URL that caused a renderer crash, // used to detect repeated crashes. GURL last_failed_url_; @@ -69,21 +74,21 @@ // used to determine time window for repeated crashes. std::unique_ptr<base::ElapsedTimer> last_failed_timer_; - // Stores the interval window during which a second RenderProcessGone failure - // will be considered a repeat failure. - double repeat_failure_interval_; + // Stores the interval window in seconds during which a second + // RenderProcessGone failure will be considered a repeat failure. + double repeat_failure_interval_ = kDefaultRepeatFailureInterval; // Whether or not WebState is currently being displayed. - bool is_visible_; + bool is_visible_ = false; // true if the WebState needs to be reloaded after web state becomes visible. - bool requires_reload_on_becoming_visible_; + bool requires_reload_on_becoming_visible_ = false; // true if the WebState needs to be reloaded after the app becomes active. - bool requires_reload_on_becoming_active_; + bool requires_reload_on_becoming_active_ = false; // Observer for UIApplicationDidBecomeActiveNotification. - __strong id<NSObject> application_did_become_active_observer_; + __strong id<NSObject> application_did_become_active_observer_ = nil; DISALLOW_COPY_AND_ASSIGN(SadTabTabHelper); };
diff --git a/ios/chrome/browser/web/sad_tab_tab_helper.mm b/ios/chrome/browser/web/sad_tab_tab_helper.mm index eff6eae..d597585c 100644 --- a/ios/chrome/browser/web/sad_tab_tab_helper.mm +++ b/ios/chrome/browser/web/sad_tab_tab_helper.mm
@@ -4,8 +4,6 @@ #import "ios/chrome/browser/web/sad_tab_tab_helper.h" -#import <Foundation/Foundation.h> - #include "base/memory/ptr_util.h" #include "base/strings/sys_string_conversions.h" #include "ios/chrome/browser/chrome_url_constants.h" @@ -21,17 +19,15 @@ DEFINE_WEB_STATE_USER_DATA_KEY(SadTabTabHelper); -namespace { -// The default window of time a failure of the same URL needs to occur -// to be considered a repeat failure. -NSTimeInterval const kDefaultRepeatFailureInterval = 60.0f; +const double SadTabTabHelper::kDefaultRepeatFailureInterval = 60.0f; +namespace { // Returns true if the application is in UIApplicationStateActive state. bool IsApplicationStateActive() { return UIApplication.sharedApplication.applicationState == UIApplicationStateActive; } -} +} // namespace SadTabTabHelper::SadTabTabHelper(web::WebState* web_state) : SadTabTabHelper(web_state, kDefaultRepeatFailureInterval) {} @@ -39,10 +35,7 @@ SadTabTabHelper::SadTabTabHelper(web::WebState* web_state, double repeat_failure_interval) : web::WebStateObserver(web_state), - repeat_failure_interval_(repeat_failure_interval), - is_visible_(false), - requires_reload_on_becoming_visible_(false), - requires_reload_on_becoming_active_(false) { + repeat_failure_interval_(repeat_failure_interval) { AddApplicationDidBecomeActiveObserver(); }
diff --git a/ios/clean/chrome/browser/ui/commands/BUILD.gn b/ios/clean/chrome/browser/ui/commands/BUILD.gn index 2d6888e..a7fbd4b 100644 --- a/ios/clean/chrome/browser/ui/commands/BUILD.gn +++ b/ios/clean/chrome/browser/ui/commands/BUILD.gn
@@ -5,6 +5,7 @@ source_set("commands") { sources = [ "context_menu_commands.h", + "dialog_commands.h", "find_in_page_search_commands.h", "find_in_page_visibility_commands.h", "navigation_commands.h",
diff --git a/ios/clean/chrome/browser/ui/commands/dialog_commands.h b/ios/clean/chrome/browser/ui/commands/dialog_commands.h new file mode 100644 index 0000000..f3f0052 --- /dev/null +++ b/ios/clean/chrome/browser/ui/commands/dialog_commands.h
@@ -0,0 +1,30 @@ +// 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 IOS_CLEAN_CHROME_BROWSER_UI_COMMANDS_DIALOG_COMMANDS_H_ +#define IOS_CLEAN_CHROME_BROWSER_UI_COMMANDS_DIALOG_COMMANDS_H_ + +#import <Foundation/Foundation.h> + +@class DialogConfigurationIdentifier; + +// Convenience typedef to improve formatting. +using DialogTextFieldValues = + NSDictionary<DialogConfigurationIdentifier*, NSString*>; + +// Command protocol for dismissing DialogConsumers. +@protocol DialogDismissalCommands + +// Called to dismiss the dialog. |buttonID| is the identifier of the +// DialogButtonConfiguration corresponding with the dialog button that was +// tapped, if any. |textFieldValues| contains the user input text. The keys +// are the identifiers of the DialogTextFieldConfigurations passed to the +// consumer, and the values are the text in their corresponding text fields. +- (void) +dismissDialogWithButtonID:(nonnull DialogConfigurationIdentifier*)buttonID + textFieldValues:(nonnull DialogTextFieldValues*)textFieldValues; + +@end + +#endif // IOS_CLEAN_CHROME_BROWSER_UI_COMMANDS_DIALOG_COMMANDS_H_
diff --git a/ios/clean/chrome/browser/ui/dialogs/BUILD.gn b/ios/clean/chrome/browser/ui/dialogs/BUILD.gn new file mode 100644 index 0000000..f5f87d2c --- /dev/null +++ b/ios/clean/chrome/browser/ui/dialogs/BUILD.gn
@@ -0,0 +1,83 @@ +# 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. + +source_set("dialog_coordinator_support") { + sources = [ + "dialog_coordinator+subclassing.h", + "dialog_coordinator.h", + "dialog_coordinator.mm", + "dialog_mediator+subclassing.h", + "dialog_mediator.h", + "dialog_mediator.mm", + ] + + configs += [ "//build/config/compiler:enable_arc" ] + + deps = [ + ":dialogs_ui", + "//base", + "//ios/chrome/browser/ui/browser_list", + "//ios/chrome/browser/ui/commands", + "//ios/chrome/browser/ui/coordinators", + "//ios/clean/chrome/browser/ui/commands", + "//ios/clean/chrome/browser/ui/overlays", + ] +} + +source_set("dialogs_ui") { + sources = [ + "dialog_button_configuration.h", + "dialog_button_configuration.mm", + "dialog_button_style.h", + "dialog_configuration_identifier.h", + "dialog_configuration_identifier.mm", + "dialog_consumer.h", + "dialog_text_field_configuration.h", + "dialog_text_field_configuration.mm", + "dialog_view_controller.h", + "dialog_view_controller.mm", + ] + + configs += [ "//build/config/compiler:enable_arc" ] + + deps = [ + "//base", + "//components/strings", + "//ios/chrome/app/strings", + "//ios/clean/chrome/browser/ui/commands", + "//ui/base", + ] +} + +source_set("unit_tests") { + testonly = true + sources = [ + "dialog_button_configuration_unittest.mm", + "dialog_configuration_identifier_unittest.mm", + "dialog_coordinator_unittest.mm", + "dialog_mediator_unittest.mm", + "dialog_text_field_configuration_unittest.mm", + "dialog_view_controller_unittest.mm", + ] + + configs += [ "//build/config/compiler:enable_arc" ] + + deps = [ + ":dialog_coordinator_support", + ":dialogs_ui", + "//base", + "//base/test:test_support", + "//ios/chrome/browser/browser_state", + "//ios/chrome/browser/ui/browser_list", + "//ios/chrome/browser/ui/coordinators", + "//ios/chrome/browser/web_state_list", + "//ios/chrome/test/base", + "//ios/clean/chrome/browser/ui/dialogs/test_helpers", + "//ios/clean/chrome/browser/ui/overlays/test_helpers", + "//ios/web", + "//ios/web/public/test", + "//ios/web/public/test/fakes", + "//testing/gtest", + ] +}
diff --git a/ios/clean/chrome/browser/ui/dialogs/OWNERS b/ios/clean/chrome/browser/ui/dialogs/OWNERS new file mode 100644 index 0000000..48efb49e --- /dev/null +++ b/ios/clean/chrome/browser/ui/dialogs/OWNERS
@@ -0,0 +1 @@ +kkhorimoto@chromium.org
diff --git a/ios/clean/chrome/browser/ui/dialogs/dialog_button_configuration.h b/ios/clean/chrome/browser/ui/dialogs/dialog_button_configuration.h new file mode 100644 index 0000000..25ede32 --- /dev/null +++ b/ios/clean/chrome/browser/ui/dialogs/dialog_button_configuration.h
@@ -0,0 +1,34 @@ +// 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 IOS_CLEAN_CHROME_BROWSER_UI_DIALOGS_DIALOG_BUTTON_CONFIGURATION_H_ +#define IOS_CLEAN_CHROME_BROWSER_UI_DIALOGS_DIALOG_BUTTON_CONFIGURATION_H_ + +#import <UIKit/UIKit.h> + +@class DialogConfigurationIdentifier; +enum class DialogButtonStyle : char; + +// An object encapsulating the data necessary to set up a dialog button. +@interface DialogButtonConfiguration : NSObject + +// Factory method for item creation. |text| must be non-empty. ++ (nonnull instancetype)configWithText:(nonnull NSString*)text + style:(DialogButtonStyle)style; + +// DialogTextFieldConfigurations should be created through the factory method. +- (nonnull instancetype)init NS_UNAVAILABLE; + +// The default text to display in the text field, if any. +@property(nonatomic, readonly, copy, nonnull) NSString* text; + +// The placehodler text to display in the text field, if any. +@property(nonatomic, readonly) DialogButtonStyle style; + +// Unique identifier for this DialogButtonConfiguration. +@property(nonatomic, readonly, strong, nonnull) + DialogConfigurationIdentifier* identifier; + +@end +#endif // IOS_CLEAN_CHROME_BROWSER_UI_DIALOGS_DIALOG_BUTTON_CONFIGURATION_H_
diff --git a/ios/clean/chrome/browser/ui/dialogs/dialog_button_configuration.mm b/ios/clean/chrome/browser/ui/dialogs/dialog_button_configuration.mm new file mode 100644 index 0000000..6d5fbc8a --- /dev/null +++ b/ios/clean/chrome/browser/ui/dialogs/dialog_button_configuration.mm
@@ -0,0 +1,44 @@ +// 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. + +#import "ios/clean/chrome/browser/ui/dialogs/dialog_button_configuration.h" + +#include "base/logging.h" +#import "ios/clean/chrome/browser/ui/dialogs/dialog_configuration_identifier.h" + +#if !defined(__has_feature) || !__has_feature(objc_arc) +#error "This file requires ARC support." +#endif + +@interface DialogButtonConfiguration () + +// Initializer used by the factory method. +- (instancetype)initWithText:(NSString*)text + style:(DialogButtonStyle)style NS_DESIGNATED_INITIALIZER; + +@end + +@implementation DialogButtonConfiguration + +@synthesize text = _text; +@synthesize style = _style; +@synthesize identifier = _identifier; + +- (instancetype)initWithText:(NSString*)text style:(DialogButtonStyle)style { + DCHECK(text.length); + if ((self = [super init])) { + _text = [text copy]; + _style = style; + _identifier = [[DialogConfigurationIdentifier alloc] init]; + } + return self; +} + +#pragma mark - Public + ++ (instancetype)configWithText:(NSString*)text style:(DialogButtonStyle)style { + return [[DialogButtonConfiguration alloc] initWithText:text style:style]; +} + +@end
diff --git a/ios/clean/chrome/browser/ui/dialogs/dialog_button_configuration_unittest.mm b/ios/clean/chrome/browser/ui/dialogs/dialog_button_configuration_unittest.mm new file mode 100644 index 0000000..114acf3e --- /dev/null +++ b/ios/clean/chrome/browser/ui/dialogs/dialog_button_configuration_unittest.mm
@@ -0,0 +1,39 @@ +// 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. + +#import "ios/clean/chrome/browser/ui/dialogs/dialog_button_configuration.h" + +#import "ios/clean/chrome/browser/ui/dialogs/dialog_button_style.h" +#import "ios/clean/chrome/browser/ui/dialogs/dialog_configuration_identifier.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "testing/gtest_mac.h" + +#if !defined(__has_feature) || !__has_feature(objc_arc) +#error "This file requires ARC support." +#endif + +namespace { +// Const values from which to create configurations. +NSString* const kButtonText = @"button text"; +const DialogButtonStyle kButtonStyle = DialogButtonStyle::DESTRUCTIVE; +} + +// Tests that the values passed to the factory method are reflected in the +// returned value. +TEST(DialogButtonConfigurationTest, FactoryMethod) { + DialogButtonConfiguration* config = + [DialogButtonConfiguration configWithText:kButtonText style:kButtonStyle]; + EXPECT_NSEQ(kButtonText, config.text); + EXPECT_EQ(kButtonStyle, config.style); +} + +// Tests that two DialogButtonConfigurations created with the same values have +// unequal identifiers. +TEST(DialogButtonConfigurationTest, Identifiers) { + DialogButtonConfiguration* config1 = + [DialogButtonConfiguration configWithText:kButtonText style:kButtonStyle]; + DialogButtonConfiguration* config2 = + [DialogButtonConfiguration configWithText:kButtonText style:kButtonStyle]; + EXPECT_FALSE([config1.identifier isEqual:config2.identifier]); +}
diff --git a/ios/clean/chrome/browser/ui/dialogs/dialog_button_style.h b/ios/clean/chrome/browser/ui/dialogs/dialog_button_style.h new file mode 100644 index 0000000..4bac25c --- /dev/null +++ b/ios/clean/chrome/browser/ui/dialogs/dialog_button_style.h
@@ -0,0 +1,19 @@ +// 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 IOS_CLEAN_CHROME_BROWSER_UI_DIALOGS_DIALOG_BUTTON_STYLE_H_ +#define IOS_CLEAN_CHROME_BROWSER_UI_DIALOGS_DIALOG_BUTTON_STYLE_H_ + +// Enum type to describe the style of buttons to use. +enum class DialogButtonStyle : char { + DEFAULT, // Uses default button styling. + + CANCEL, // Uses styling to indicates that the button cancels the + // current action, and leaves things unchanged. + + DESTRUCTIVE // Uses styling that indicates that the button might change or + // delete data. +}; + +#endif // IOS_CLEAN_CHROME_BROWSER_UI_DIALOGS_DIALOG_BUTTON_STYLE_H_
diff --git a/ios/clean/chrome/browser/ui/dialogs/dialog_configuration_identifier.h b/ios/clean/chrome/browser/ui/dialogs/dialog_configuration_identifier.h new file mode 100644 index 0000000..d626b74 --- /dev/null +++ b/ios/clean/chrome/browser/ui/dialogs/dialog_configuration_identifier.h
@@ -0,0 +1,16 @@ +// 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 IOS_CLEAN_CHROME_BROWSER_UI_DIALOGS_DIALOG_CONFIGURATION_IDENTIFIER_H_ +#define IOS_CLEAN_CHROME_BROWSER_UI_DIALOGS_DIALOG_CONFIGURATION_IDENTIFIER_H_ + +#import <Foundation/Foundation.h> + +// Identifier objects used to communicate interaction events that occur from UI +// elements corresponding to dialog configuration objects passed to +// DialogConsumers. +@interface DialogConfigurationIdentifier : NSObject<NSCopying> +@end + +#endif // IOS_CLEAN_CHROME_BROWSER_UI_DIALOGS_DIALOG_CONFIGURATION_IDENTIFIER_H_
diff --git a/ios/clean/chrome/browser/ui/dialogs/dialog_configuration_identifier.mm b/ios/clean/chrome/browser/ui/dialogs/dialog_configuration_identifier.mm new file mode 100644 index 0000000..5488b1b --- /dev/null +++ b/ios/clean/chrome/browser/ui/dialogs/dialog_configuration_identifier.mm
@@ -0,0 +1,59 @@ +// 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. + +#import "ios/clean/chrome/browser/ui/dialogs/dialog_configuration_identifier.h" + +#include "base/atomic_sequence_num.h" +#import "base/mac/foundation_util.h" + +#if !defined(__has_feature) || !__has_feature(objc_arc) +#error "This file requires ARC support." +#endif + +namespace { +base::AtomicSequenceNumber g_dialog_configuration_identifier; +} // namespace + +@interface DialogConfigurationIdentifier () { + // The unique identifier for this object. + int _uniqueID; +} + +// Designated initializer that takes a unique ID. +- (instancetype)initWithUniqueID:(int)uniqueID; + +@end + +@implementation DialogConfigurationIdentifier + +- (instancetype)init { + return [self initWithUniqueID:g_dialog_configuration_identifier.GetNext()]; +} + +- (instancetype)initWithUniqueID:(int)uniqueID { + if ((self = [super init])) { + _uniqueID = uniqueID; + } + return self; +} + +#pragma mark - NSObject + +- (BOOL)isEqual:(id)object { + DialogConfigurationIdentifier* identifier = + base::mac::ObjCCast<DialogConfigurationIdentifier>(object); + return identifier && identifier->_uniqueID == _uniqueID; +} + +- (NSUInteger)hash { + return static_cast<NSUInteger>(_uniqueID); +} + +#pragma mark - NSCopying + +- (id)copyWithZone:(NSZone*)zone { + return [[[self class] allocWithZone:zone] initWithUniqueID:_uniqueID]; +} + +@end
diff --git a/ios/clean/chrome/browser/ui/dialogs/dialog_configuration_identifier_unittest.mm b/ios/clean/chrome/browser/ui/dialogs/dialog_configuration_identifier_unittest.mm new file mode 100644 index 0000000..c14726a --- /dev/null +++ b/ios/clean/chrome/browser/ui/dialogs/dialog_configuration_identifier_unittest.mm
@@ -0,0 +1,31 @@ +// 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. + +#import "ios/clean/chrome/browser/ui/dialogs/dialog_configuration_identifier.h" + +#include "testing/gtest/include/gtest/gtest.h" +#include "testing/gtest_mac.h" + +#if !defined(__has_feature) || !__has_feature(objc_arc) +#error "This file requires ARC support." +#endif + +// Tests that two DialogConfigurationIdentifiers created sequentially are not +// equal to each other. +TEST(DialogConfigurationIdentifierTest, NotEqual) { + DialogConfigurationIdentifier* ID1 = + [[DialogConfigurationIdentifier alloc] init]; + DialogConfigurationIdentifier* ID2 = + [[DialogConfigurationIdentifier alloc] init]; + EXPECT_FALSE([ID1 isEqual:ID2]); +} + +// Tests that copying a DialogConfigurationIdentifier creates one that is equal +// to the original. +TEST(DialogConfigurationIdentifierTest, EqualCopies) { + DialogConfigurationIdentifier* identifier = + [[DialogConfigurationIdentifier alloc] init]; + DialogConfigurationIdentifier* copy = [identifier copy]; + EXPECT_NSEQ(identifier, copy); +}
diff --git a/ios/clean/chrome/browser/ui/dialogs/dialog_consumer.h b/ios/clean/chrome/browser/ui/dialogs/dialog_consumer.h new file mode 100644 index 0000000..699204f --- /dev/null +++ b/ios/clean/chrome/browser/ui/dialogs/dialog_consumer.h
@@ -0,0 +1,35 @@ +// 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 IOS_CLEAN_CHROME_BROWSER_UI_DIALOGS_DIALOG_CONSUMER_H_ +#define IOS_CLEAN_CHROME_BROWSER_UI_DIALOGS_DIALOG_CONSUMER_H_ + +#import <Foundation/Foundation.h> + +@class DialogButtonConfiguration; +@class DialogTextFieldConfiguration; + +// A DialogConsumer uses data provided by this protocol to configure and run a +// dialog. A DialogConsumer can be configured multiple times before +// presentation, but once the consumer is presented, the appearance of the +// dialog will be immutable. +@protocol DialogConsumer<NSObject> + +// Sets the title of the dialog. +- (void)setDialogTitle:(nullable NSString*)title; + +// Sets the message of the dialog. +- (void)setDialogMessage:(nullable NSString*)message; + +// Sets the button items to display in the dialog. +- (void)setDialogButtonConfigurations: + (nullable NSArray<DialogButtonConfiguration*>*)buttonConfigs; + +// Sets the text field items to display in the dialog. +- (void)setDialogTextFieldConfigurations: + (nullable NSArray<DialogTextFieldConfiguration*>*)textFieldConfigs; + +@end + +#endif // IOS_CLEAN_CHROME_BROWSER_UI_DIALOGS_DIALOG_CONSUMER_H_
diff --git a/ios/clean/chrome/browser/ui/dialogs/dialog_coordinator+subclassing.h b/ios/clean/chrome/browser/ui/dialogs/dialog_coordinator+subclassing.h new file mode 100644 index 0000000..6f564e08 --- /dev/null +++ b/ios/clean/chrome/browser/ui/dialogs/dialog_coordinator+subclassing.h
@@ -0,0 +1,27 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef IOS_CLEAN_CHROME_BROWSER_UI_DIALOGS_DIALOG_COORDINATOR_SUBCLASSING_H_ +#define IOS_CLEAN_CHROME_BROWSER_UI_DIALOGS_DIALOG_COORDINATOR_SUBCLASSING_H_ + +#import <UIKit/UIKit.h> + +#import "ios/clean/chrome/browser/ui/dialogs/dialog_coordinator.h" + +@class DialogMediator; + +// Interface used to expose DialogCoordinator configuration to subclasses. +@interface DialogCoordinator (DialogCoordinatorSubclassing) + +// The type of dialog UI that should be started for this coordinator. Defaults +// to UIAlertControllerStyleAlert. +@property(nonatomic, readonly) UIAlertControllerStyle alertStyle; + +// The mediator that will be used to configure the DialogConsumer. The value is +// expected to return non-nil before DialogCoordinator's |-start| is called. +@property(nonatomic, readonly) DialogMediator* mediator; + +@end + +#endif // IOS_CLEAN_CHROME_BROWSER_UI_DIALOGS_DIALOG_COORDINATOR_SUBCLASSING_H_
diff --git a/ios/clean/chrome/browser/ui/dialogs/dialog_coordinator.h b/ios/clean/chrome/browser/ui/dialogs/dialog_coordinator.h new file mode 100644 index 0000000..1bc653e --- /dev/null +++ b/ios/clean/chrome/browser/ui/dialogs/dialog_coordinator.h
@@ -0,0 +1,18 @@ +// 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 IOS_CLEAN_CHROME_BROWSER_UI_DIALOGS_DIALOG_COORDINATOR_H_ +#define IOS_CLEAN_CHROME_BROWSER_UI_DIALOGS_DIALOG_COORDINATOR_H_ + +#import <Foundation/Foundation.h> + +#import "ios/clean/chrome/browser/ui/overlays/overlay_coordinator.h" + +// A coordinator that displays dialog UI. This class is meant to be subclassed, +// and subclasses are expected to implement the interface in the +// DialogCoordinator+Subclassing category. +@interface DialogCoordinator : OverlayCoordinator +@end + +#endif // IOS_CLEAN_CHROME_BROWSER_UI_DIALOGS_DIALOG_COORDINATOR_H_
diff --git a/ios/clean/chrome/browser/ui/dialogs/dialog_coordinator.mm b/ios/clean/chrome/browser/ui/dialogs/dialog_coordinator.mm new file mode 100644 index 0000000..8f6f996 --- /dev/null +++ b/ios/clean/chrome/browser/ui/dialogs/dialog_coordinator.mm
@@ -0,0 +1,69 @@ +// 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. + +#import "ios/clean/chrome/browser/ui/dialogs/dialog_coordinator.h" + +#include "base/logging.h" +#import "ios/chrome/browser/ui/browser_list/browser.h" +#import "ios/chrome/browser/ui/commands/command_dispatcher.h" +#import "ios/clean/chrome/browser/ui/dialogs/dialog_coordinator+subclassing.h" +#import "ios/clean/chrome/browser/ui/dialogs/dialog_mediator.h" +#import "ios/clean/chrome/browser/ui/dialogs/dialog_view_controller.h" + +#if !defined(__has_feature) || !__has_feature(objc_arc) +#error "This file requires ARC support." +#endif + +@interface DialogCoordinator () + +// The dispatcher for DialogDismissalCommands. +@property(nonatomic, readonly) id<DialogDismissalCommands> dismissalDispatcher; +// The view controller used to display this dialog. +@property(nonatomic, strong) DialogViewController* viewController; + +@end + +@implementation DialogCoordinator +@synthesize viewController = _viewController; + +#pragma mark - Accessors + +- (id<DialogDismissalCommands>)dismissalDispatcher { + return static_cast<id<DialogDismissalCommands>>(self.browser->dispatcher()); +} + +#pragma mark - BrowserCoordinator + +- (void)start { + DCHECK(self.mediator); + self.viewController = + [[DialogViewController alloc] initWithStyle:self.alertStyle + dispatcher:self.dismissalDispatcher]; + [self.mediator updateConsumer:self.viewController]; + [self.browser->dispatcher() + startDispatchingToTarget:self.mediator + forProtocol:@protocol(DialogDismissalCommands)]; + [super start]; +} + +- (void)stop { + [self.browser->dispatcher() stopDispatchingToTarget:self.mediator]; + [super stop]; +} + +@end + +@implementation DialogCoordinator (DialogCoordinatorSubclassing) + +- (UIAlertControllerStyle)alertStyle { + return UIAlertControllerStyleAlert; +} + +- (DialogMediator*)mediator { + // Implemented by subclasses. + NOTREACHED(); + return nil; +} + +@end
diff --git a/ios/clean/chrome/browser/ui/dialogs/dialog_coordinator_unittest.mm b/ios/clean/chrome/browser/ui/dialogs/dialog_coordinator_unittest.mm new file mode 100644 index 0000000..b040c6f --- /dev/null +++ b/ios/clean/chrome/browser/ui/dialogs/dialog_coordinator_unittest.mm
@@ -0,0 +1,104 @@ +// 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. + +#import "ios/clean/chrome/browser/ui/dialogs/dialog_coordinator.h" + +#import "base/mac/foundation_util.h" +#include "ios/chrome/browser/browser_state/chrome_browser_state.h" +#import "ios/chrome/browser/ui/browser_list/browser.h" +#import "ios/chrome/browser/ui/coordinators/browser_coordinator+internal.h" +#import "ios/clean/chrome/browser/ui/dialogs/dialog_button_configuration.h" +#import "ios/clean/chrome/browser/ui/dialogs/dialog_button_style.h" +#import "ios/clean/chrome/browser/ui/dialogs/dialog_coordinator+subclassing.h" +#import "ios/clean/chrome/browser/ui/dialogs/dialog_mediator+subclassing.h" +#import "ios/clean/chrome/browser/ui/dialogs/dialog_mediator.h" +#import "ios/clean/chrome/browser/ui/dialogs/dialog_text_field_configuration.h" +#import "ios/clean/chrome/browser/ui/dialogs/dialog_view_controller.h" +#import "ios/clean/chrome/browser/ui/dialogs/test_helpers/test_dialog_mediator.h" +#import "ios/clean/chrome/browser/ui/overlays/test_helpers/test_overlay_queue.h" +#include "ios/web/public/test/fakes/test_browser_state.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "testing/gtest_mac.h" +#include "testing/platform_test.h" + +#if !defined(__has_feature) || !__has_feature(objc_arc) +#error "This file requires ARC support." +#endif + +// Test version of DialogCoordinator that populates its consumer with the dummy +// values from TestDialogMediator. +@interface TestDialogCoordinator : DialogCoordinator +@property(nonatomic, readonly, strong) TestDialogMediator* testMediator; +@property(nonatomic, readwrite) UIAlertControllerStyle alertStyle; +@end + +@implementation TestDialogCoordinator +@synthesize testMediator = _testMediator; +@synthesize alertStyle = _alertStyle; + +- (instancetype)init { + if ((self = [super init])) { + _testMediator = [[TestDialogMediator alloc] init]; + _alertStyle = UIAlertControllerStyleAlert; + } + return self; +} + +@end + +@implementation TestDialogCoordinator (DialogCoordinatorSubclassing) + +- (DialogMediator*)mediator { + return self.testMediator; +} + +@end + +// A test fixture for DialogCoordinators. +class DialogCoordinatorTest : public PlatformTest { + public: + DialogCoordinatorTest() + : PlatformTest(), + coordinator_([[TestDialogCoordinator alloc] init]), + browser_(ios::ChromeBrowserState::FromBrowserState(&browser_state_)) { + // DialogConsumers require at least one button. + coordinator_.testMediator.buttonConfigs = @[ [DialogButtonConfiguration + configWithText:@"OK" + style:DialogButtonStyle::DEFAULT] ]; + // Add the coordinator to the queue. + queue_.SetBrowser(&browser_); + queue_.AddOverlay(coordinator_); + } + + ~DialogCoordinatorTest() override { queue_.CancelOverlays(); } + + void StartCoordinator() { queue_.StartNextOverlay(); } + + DialogViewController* alert_controller() { + return base::mac::ObjCCastStrict<DialogViewController>( + coordinator_.viewController); + } + + protected: + __strong TestDialogCoordinator* coordinator_; + web::TestBrowserState browser_state_; + Browser browser_; + TestOverlayQueue queue_; +}; + +// Tests that the alert style is used by default. +TEST_F(DialogCoordinatorTest, DefaultStyle) { + StartCoordinator(); + ASSERT_TRUE(alert_controller()); + EXPECT_EQ(UIAlertControllerStyleAlert, alert_controller().preferredStyle); +} + +// Tests that the action sheet style is used if specified. +TEST_F(DialogCoordinatorTest, ActionSheetStyle) { + coordinator_.alertStyle = UIAlertControllerStyleActionSheet; + StartCoordinator(); + ASSERT_TRUE(alert_controller()); + EXPECT_EQ(UIAlertControllerStyleActionSheet, + alert_controller().preferredStyle); +}
diff --git a/ios/clean/chrome/browser/ui/dialogs/dialog_mediator+subclassing.h b/ios/clean/chrome/browser/ui/dialogs/dialog_mediator+subclassing.h new file mode 100644 index 0000000..3c5cde8b --- /dev/null +++ b/ios/clean/chrome/browser/ui/dialogs/dialog_mediator+subclassing.h
@@ -0,0 +1,32 @@ +// 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 IOS_CLEAN_CHROME_BROWSER_UI_DIALOGS_DIALOG_MEDIATOR_SUBCLASSING_H_ +#define IOS_CLEAN_CHROME_BROWSER_UI_DIALOGS_DIALOG_MEDIATOR_SUBCLASSING_H_ + +#import "ios/clean/chrome/browser/ui/dialogs/dialog_mediator.h" + +#import "ios/clean/chrome/browser/ui/commands/dialog_commands.h" + +@class DialogButtonConfiguration; +@class DialogTextFieldConfiguration; + +// DialogMediator functionality exposed to subclasses. +@interface DialogMediator (DialogMediatorSubclassing) + +// The title to provide to the consumer. +- (NSString*)dialogTitle; + +// The message to provide to the consumer. +- (NSString*)dialogMessage; + +// The DialogButtonConfigurations to provide to the consumer. +- (NSArray<DialogButtonConfiguration*>*)buttonConfigs; + +// The DialogTextFieldConfigurations to provide to the consumer. +- (NSArray<DialogTextFieldConfiguration*>*)textFieldConfigs; + +@end + +#endif // IOS_CLEAN_CHROME_BROWSER_UI_DIALOGS_DIALOG_MEDIATOR_SUBCLASSING_H_
diff --git a/ios/clean/chrome/browser/ui/dialogs/dialog_mediator.h b/ios/clean/chrome/browser/ui/dialogs/dialog_mediator.h new file mode 100644 index 0000000..9eb25421 --- /dev/null +++ b/ios/clean/chrome/browser/ui/dialogs/dialog_mediator.h
@@ -0,0 +1,24 @@ +// 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 IOS_CLEAN_CHROME_BROWSER_UI_DIALOGS_DIALOG_MEDIATOR_H_ +#define IOS_CLEAN_CHROME_BROWSER_UI_DIALOGS_DIALOG_MEDIATOR_H_ + +#import <Foundation/Foundation.h> + +#import "ios/clean/chrome/browser/ui/commands/dialog_commands.h" + +@protocol DialogConsumer; + +// Class responsible for setting up a DialogConsumer. This class is meant to be +// subclassed, and subclasses are expected to implement the interface in the +// DialogMediator+Subclassing category. +@interface DialogMediator : NSObject<DialogDismissalCommands> + +// Supplies UI information to |consumer|. +- (void)updateConsumer:(id<DialogConsumer>)consumer; + +@end + +#endif // IOS_CLEAN_CHROME_BROWSER_UI_DIALOGS_DIALOG_MEDIATOR_H_
diff --git a/ios/clean/chrome/browser/ui/dialogs/dialog_mediator.mm b/ios/clean/chrome/browser/ui/dialogs/dialog_mediator.mm new file mode 100644 index 0000000..aa8585c2 --- /dev/null +++ b/ios/clean/chrome/browser/ui/dialogs/dialog_mediator.mm
@@ -0,0 +1,62 @@ +// 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. + +#import "ios/clean/chrome/browser/ui/dialogs/dialog_mediator.h" + +#include "base/logging.h" +#import "ios/clean/chrome/browser/ui/commands/dialog_commands.h" +#import "ios/clean/chrome/browser/ui/dialogs/dialog_button_configuration.h" +#import "ios/clean/chrome/browser/ui/dialogs/dialog_consumer.h" +#import "ios/clean/chrome/browser/ui/dialogs/dialog_mediator+subclassing.h" + +#if !defined(__has_feature) || !__has_feature(objc_arc) +#error "This file requires ARC support." +#endif + +@implementation DialogMediator + +#pragma mark - Public + +- (void)updateConsumer:(id<DialogConsumer>)consumer { + [consumer setDialogTitle:[self dialogTitle]]; + [consumer setDialogMessage:[self dialogMessage]]; + [consumer setDialogButtonConfigurations:[self buttonConfigs]]; + [consumer setDialogTextFieldConfigurations:[self textFieldConfigs]]; +} + +#pragma mark - DialogDismissalCommands + +- (void)dismissDialogWithButtonID:(DialogConfigurationIdentifier*)buttonID + textFieldValues:(DialogTextFieldValues*)textFieldValues { + // Implemented by subclasses. + NOTREACHED(); +} + +@end + +@implementation DialogMediator (Subclassing) + +- (NSString*)dialogTitle { + // Default is no title. + return nil; +} + +- (NSString*)dialogMessage { + // Default is no message. + return nil; +} + +- (NSArray<DialogButtonConfiguration*>*)buttonConfigs { + // Implemented by subclasses. A dialog must have a least one button for + // dismissal. + NOTREACHED(); + return nil; +} + +- (NSArray<DialogTextFieldConfiguration*>*)textFieldConfigs { + // Default is no text fields. + return nil; +} + +@end
diff --git a/ios/clean/chrome/browser/ui/dialogs/dialog_mediator_unittest.mm b/ios/clean/chrome/browser/ui/dialogs/dialog_mediator_unittest.mm new file mode 100644 index 0000000..c0ea538 --- /dev/null +++ b/ios/clean/chrome/browser/ui/dialogs/dialog_mediator_unittest.mm
@@ -0,0 +1,74 @@ +// 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. + +#import "ios/clean/chrome/browser/ui/dialogs/dialog_mediator.h" + +#import "base/mac/foundation_util.h" +#import "ios/chrome/browser/ui/coordinators/browser_coordinator+internal.h" +#import "ios/clean/chrome/browser/ui/dialogs/dialog_button_configuration.h" +#import "ios/clean/chrome/browser/ui/dialogs/dialog_button_style.h" +#import "ios/clean/chrome/browser/ui/dialogs/dialog_coordinator+subclassing.h" +#import "ios/clean/chrome/browser/ui/dialogs/dialog_mediator+subclassing.h" +#import "ios/clean/chrome/browser/ui/dialogs/dialog_mediator.h" +#import "ios/clean/chrome/browser/ui/dialogs/dialog_text_field_configuration.h" +#import "ios/clean/chrome/browser/ui/dialogs/dialog_view_controller.h" +#import "ios/clean/chrome/browser/ui/dialogs/test_helpers/dialog_test_util.h" +#import "ios/clean/chrome/browser/ui/dialogs/test_helpers/test_dialog_mediator.h" +#import "ios/clean/chrome/browser/ui/dialogs/test_helpers/test_dialog_view_controller.h" +#import "ios/clean/chrome/browser/ui/overlays/test_helpers/test_overlay_queue.h" +#include "ios/web/public/test/fakes/test_browser_state.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "testing/gtest_mac.h" +#include "testing/platform_test.h" + +#if !defined(__has_feature) || !__has_feature(objc_arc) +#error "This file requires ARC support." +#endif + +// A test fixture for DialogMediators. +class DialogMediatorTest : public PlatformTest { + public: + DialogMediatorTest() + : PlatformTest(), + title_(@"title"), + message_(@"message"), + button_config_([DialogButtonConfiguration + configWithText:@"button" + style:DialogButtonStyle::DESTRUCTIVE]), + text_field_config_([DialogTextFieldConfiguration + configWithDefaultText:@"defaultText" + placeholderText:@"placeholderText" + secure:YES]), + mediator_([[TestDialogMediator alloc] init]) { + mediator_.title = title_; + mediator_.message = message_; + mediator_.buttonConfigs = @[ button_config_ ]; + mediator_.textFieldConfigs = @[ text_field_config_ ]; + } + + protected: + __strong NSString* title_; + __strong NSString* message_; + __strong DialogButtonConfiguration* button_config_; + __strong DialogTextFieldConfiguration* text_field_config_; + __strong TestDialogMediator* mediator_; +}; + +// Tests that the DialogViewController is populated properly from the +// TestDialogMediator. +TEST_F(DialogMediatorTest, ConsumerPopulation) { + // Create the consumer and update it with |mediator_|. + TestDialogViewController* consumer = [[TestDialogViewController alloc] + initWithStyle:UIAlertControllerStyleAlert]; + [mediator_ updateConsumer:consumer]; + // Add the view to a container view to ensure that the alert UI is set up. + UIView* container = + [[UIView alloc] initWithFrame:[UIScreen mainScreen].bounds]; + consumer.view.frame = container.bounds; + [container addSubview:consumer.view]; + // Verify that the test configuration was properly translated to the alert UI. + dialogs_test_util::TestAlertSetup(consumer, title_, message_, + mediator_.buttonConfigs, + mediator_.textFieldConfigs); +}
diff --git a/ios/clean/chrome/browser/ui/dialogs/dialog_text_field_configuration.h b/ios/clean/chrome/browser/ui/dialogs/dialog_text_field_configuration.h new file mode 100644 index 0000000..d36ff20 --- /dev/null +++ b/ios/clean/chrome/browser/ui/dialogs/dialog_text_field_configuration.h
@@ -0,0 +1,39 @@ +// 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 IOS_CLEAN_CHROME_BROWSER_UI_DIALOGS_DIALOG_TEXT_FIELD_CONFIGURATION_H_ +#define IOS_CLEAN_CHROME_BROWSER_UI_DIALOGS_DIALOG_TEXT_FIELD_CONFIGURATION_H_ + +#import <Foundation/Foundation.h> + +@class DialogConfigurationIdentifier; + +// An object encapsulating the data necessary to set up a dialog's text field. +@interface DialogTextFieldConfiguration : NSObject + +// Factory method for item creation. ++ (nullable instancetype)configWithDefaultText:(nullable NSString*)defaultText + placeholderText: + (nullable NSString*)placeholderText + secure:(BOOL)secure; + +// DialogTextFieldConfigurations should be created through the factory method. +- (nullable instancetype)init NS_UNAVAILABLE; + +// The default text to display in the text field, if any. +@property(nonatomic, readonly, copy, nullable) NSString* defaultText; + +// The placehodler text to display in the text field, if any. +@property(nonatomic, readonly, copy, nullable) NSString* placeholderText; + +// Whether the text field should be secure (e.g. for password). +@property(nonatomic, readonly, getter=isSecure) BOOL secure; + +// Unique identifier for this DialogTextFieldConfiguration. +@property(nonatomic, readonly, strong, nonnull) + DialogConfigurationIdentifier* identifier; + +@end + +#endif // IOS_CLEAN_CHROME_BROWSER_UI_DIALOGS_DIALOG_TEXT_FIELD_CONFIGURATION_H_
diff --git a/ios/clean/chrome/browser/ui/dialogs/dialog_text_field_configuration.mm b/ios/clean/chrome/browser/ui/dialogs/dialog_text_field_configuration.mm new file mode 100644 index 0000000..fc15d6c --- /dev/null +++ b/ios/clean/chrome/browser/ui/dialogs/dialog_text_field_configuration.mm
@@ -0,0 +1,52 @@ +// 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. + +#import "ios/clean/chrome/browser/ui/dialogs/dialog_text_field_configuration.h" + +#include "base/logging.h" +#import "ios/clean/chrome/browser/ui/dialogs/dialog_configuration_identifier.h" + +#if !defined(__has_feature) || !__has_feature(objc_arc) +#error "This file requires ARC support." +#endif + +@interface DialogTextFieldConfiguration () + +// Initializer used by the factory method. +- (instancetype)initWithDefaultText:(NSString*)defaultText + placeholderText:(NSString*)placeholderText + secure:(BOOL)secure NS_DESIGNATED_INITIALIZER; + +@end + +@implementation DialogTextFieldConfiguration +@synthesize defaultText = _defaultText; +@synthesize placeholderText = _placeholderText; +@synthesize secure = _secure; +@synthesize identifier = _identifier; + +- (instancetype)initWithDefaultText:(NSString*)defaultText + placeholderText:(NSString*)placeholderText + secure:(BOOL)secure { + if ((self = [super init])) { + _defaultText = [defaultText copy]; + _placeholderText = [placeholderText copy]; + _secure = secure; + _identifier = [[DialogConfigurationIdentifier alloc] init]; + } + return self; +} + +#pragma mark - Public + ++ (instancetype)configWithDefaultText:(NSString*)defaultText + placeholderText:(NSString*)placeholderText + secure:(BOOL)secure { + return + [[DialogTextFieldConfiguration alloc] initWithDefaultText:defaultText + placeholderText:placeholderText + secure:secure]; +} + +@end
diff --git a/ios/clean/chrome/browser/ui/dialogs/dialog_text_field_configuration_unittest.mm b/ios/clean/chrome/browser/ui/dialogs/dialog_text_field_configuration_unittest.mm new file mode 100644 index 0000000..a39f90e --- /dev/null +++ b/ios/clean/chrome/browser/ui/dialogs/dialog_text_field_configuration_unittest.mm
@@ -0,0 +1,46 @@ +// 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. + +#import "ios/clean/chrome/browser/ui/dialogs/dialog_text_field_configuration.h" + +#import "ios/clean/chrome/browser/ui/dialogs/dialog_configuration_identifier.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "testing/gtest_mac.h" + +#if !defined(__has_feature) || !__has_feature(objc_arc) +#error "This file requires ARC support." +#endif + +namespace { +// Const values from which to create configurations. +NSString* const kDefaultText = @"default text"; +NSString* const kPlaceholderText = @"placeholder text"; +const BOOL kSecure = YES; +} + +// Tests that the values passed to the factory method are reflected in the +// returned value. +TEST(DialogTextFieldConfigurationTest, FactoryMethod) { + DialogTextFieldConfiguration* config = + [DialogTextFieldConfiguration configWithDefaultText:kDefaultText + placeholderText:kPlaceholderText + secure:kSecure]; + EXPECT_NSEQ(kDefaultText, config.defaultText); + EXPECT_NSEQ(kPlaceholderText, config.placeholderText); + EXPECT_EQ(kSecure, config.secure); +} + +// Tests that two DialogTextFieldConfigurations created with the same values +// have unequal identifiers. +TEST(DialogTextFieldConfigurationTest, Identifiers) { + DialogTextFieldConfiguration* config1 = + [DialogTextFieldConfiguration configWithDefaultText:kDefaultText + placeholderText:kPlaceholderText + secure:kSecure]; + DialogTextFieldConfiguration* config2 = + [DialogTextFieldConfiguration configWithDefaultText:kDefaultText + placeholderText:kPlaceholderText + secure:kSecure]; + EXPECT_FALSE([config1.identifier isEqual:config2.identifier]); +}
diff --git a/ios/clean/chrome/browser/ui/dialogs/dialog_view_controller.h b/ios/clean/chrome/browser/ui/dialogs/dialog_view_controller.h new file mode 100644 index 0000000..a64c678 --- /dev/null +++ b/ios/clean/chrome/browser/ui/dialogs/dialog_view_controller.h
@@ -0,0 +1,35 @@ +// 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 IOS_CLEAN_CHROME_BROWSER_UI_DIALOGS_DIALOG_VIEW_CONTROLLER_H_ +#define IOS_CLEAN_CHROME_BROWSER_UI_DIALOGS_DIALOG_VIEW_CONTROLLER_H_ + +#import <UIKit/UIKit.h> + +#import "ios/clean/chrome/browser/ui/dialogs/dialog_consumer.h" + +enum class DialogButtonStyle : char; + +@protocol DialogDismissalCommands; + +// Class used to display dialogs. It is configured through the DialogConsumer +// protocol, and is not meant to be subclassed. +@interface DialogViewController : UIAlertController<DialogConsumer> + +// Initializer for a dialog with |style| that uses |dispatcher| to manage its +// dismissal. +// NOTE: The only way to specify a UIAlertController's preferred style is via +// |+alertControllerWithTitle:message:preferredStyle:|, so this cannot be marked +// as the designated initializer since the compiler will warn about not calling +// super's designated initializer. +- (instancetype)initWithStyle:(UIAlertControllerStyle)style + dispatcher:(id<DialogDismissalCommands>)dispatcher; + +// Returns the UIAlertActionStyle used for a UIAlertAction created from a +// DialogButtonConfiguration with |style|. ++ (UIAlertActionStyle)alertStyleForDialogButtonStyle:(DialogButtonStyle)style; + +@end + +#endif // IOS_CLEAN_CHROME_BROWSER_UI_DIALOGS_DIALOG_VIEW_CONTROLLER_H_
diff --git a/ios/clean/chrome/browser/ui/dialogs/dialog_view_controller.mm b/ios/clean/chrome/browser/ui/dialogs/dialog_view_controller.mm new file mode 100644 index 0000000..d8915a8 --- /dev/null +++ b/ios/clean/chrome/browser/ui/dialogs/dialog_view_controller.mm
@@ -0,0 +1,165 @@ +// 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. + +#import "ios/clean/chrome/browser/ui/dialogs/dialog_view_controller.h" + +#include "base/logging.h" +#include "components/strings/grit/components_strings.h" +#import "ios/clean/chrome/browser/ui/commands/dialog_commands.h" +#import "ios/clean/chrome/browser/ui/dialogs/dialog_button_configuration.h" +#import "ios/clean/chrome/browser/ui/dialogs/dialog_button_style.h" +#import "ios/clean/chrome/browser/ui/dialogs/dialog_configuration_identifier.h" +#import "ios/clean/chrome/browser/ui/dialogs/dialog_text_field_configuration.h" +#include "ui/base/l10n/l10n_util.h" + +#if !defined(__has_feature) || !__has_feature(objc_arc) +#error "This file requires ARC support." +#endif + +namespace { +// Typedef the block parameter for UIAlertAction for readability. +typedef void (^AlertActionHandler)(UIAlertAction*); +} + +@interface DialogViewController () + +// The dispatcher used for dismissal; +@property(nonatomic, readonly, strong) id<DialogDismissalCommands> + dismissalDispatcher; + +// Objects provided through the DialogConsumer protocol. +@property(nonatomic, readonly, copy) + NSArray<DialogButtonConfiguration*>* buttonConfigs; +@property(nonatomic, readonly, copy) + NSArray<DialogTextFieldConfiguration*>* textFieldConfigs; + +// The strings corresponding with the text fields. +@property(nonatomic, readonly) + NSMutableDictionary<DialogConfigurationIdentifier*, NSString*>* + textFieldValues; + +// Creates an AlertActionHandler that sends a DialogDismissalCommand with |tag|. +- (AlertActionHandler)actionForButtonConfiguration: + (DialogButtonConfiguration*)buttonConfig; +// Adds buttons for each item in |buttonItems|. +- (void)addButtons; +// Adds text fields for each item in |textFieldItems|. +- (void)addTextFields; + +@end + +@implementation DialogViewController + +@synthesize dismissalDispatcher = _dismissalDispatcher; +@synthesize buttonConfigs = _buttonConfigs; +@synthesize textFieldConfigs = _textFieldConfigs; +@synthesize textFieldValues = _textFieldValues; + +- (instancetype)initWithStyle:(UIAlertControllerStyle)style + dispatcher:(id<DialogDismissalCommands>)dispatcher { + DCHECK(dispatcher); + self = [[self class] alertControllerWithTitle:nil + message:nil + preferredStyle:style]; + if (self) { + _dismissalDispatcher = dispatcher; + } + return self; +} + +#pragma mark - Accessors + +- (NSMutableDictionary<DialogConfigurationIdentifier*, NSString*>*) + textFieldValues { + // Early return if text field items haven't been supplied or the text fields + // have not been instantiated. + NSUInteger textFieldCount = self.textFieldConfigs.count; + if (!textFieldCount || textFieldCount != self.textFields.count) + return nil; + // Lazily create the array and update its contents. + if (!_textFieldValues) { + _textFieldValues = + [[NSMutableDictionary<DialogConfigurationIdentifier*, NSString*> alloc] + init]; + } + for (NSUInteger fieldIndex = 0; fieldIndex < textFieldCount; ++fieldIndex) { + _textFieldValues[self.textFieldConfigs[fieldIndex].identifier] = + self.textFields[fieldIndex].text; + } + return _textFieldValues; +} + +#pragma mark - Public + ++ (UIAlertActionStyle)alertStyleForDialogButtonStyle:(DialogButtonStyle)style { + switch (style) { + case DialogButtonStyle::DEFAULT: + return UIAlertActionStyleDefault; + case DialogButtonStyle::CANCEL: + return UIAlertActionStyleCancel; + case DialogButtonStyle::DESTRUCTIVE: + return UIAlertActionStyleDestructive; + } +} + +#pragma mark - DialogConsumer + +- (void)setDialogTitle:(nullable NSString*)title { + self.title = title; +} + +- (void)setDialogMessage:(nullable NSString*)message { + self.message = message; +} + +- (void)setDialogButtonConfigurations: + (nullable NSArray<DialogButtonConfiguration*>*)buttonConfigs { + _buttonConfigs = buttonConfigs; +} + +- (void)setDialogTextFieldConfigurations: + (nullable NSArray<DialogTextFieldConfiguration*>*)textFieldConfigs { + _textFieldConfigs = textFieldConfigs; +} + +#pragma mark - UIViewcontroller + +- (void)viewDidLoad { + DCHECK_GT(self.buttonConfigs.count, 0U); + [self addButtons]; + [self addTextFields]; +} + +#pragma mark - + +- (AlertActionHandler)actionForButtonConfiguration: + (DialogButtonConfiguration*)buttonConfig { + return ^(UIAlertAction*) { + [self.dismissalDispatcher dismissDialogWithButtonID:buttonConfig.identifier + textFieldValues:self.textFieldValues]; + }; +} + +- (void)addButtons { + for (DialogButtonConfiguration* config in self.buttonConfigs) { + AlertActionHandler handler = [self actionForButtonConfiguration:config]; + UIAlertActionStyle style = + [[self class] alertStyleForDialogButtonStyle:config.style]; + [self addAction:[UIAlertAction actionWithTitle:config.text + style:style + handler:handler]]; + } +} + +- (void)addTextFields { + for (DialogTextFieldConfiguration* config in self.textFieldConfigs) { + [self addTextFieldWithConfigurationHandler:^(UITextField* textField) { + textField.text = config.defaultText; + textField.placeholder = config.placeholderText; + textField.secureTextEntry = config.secure; + }]; + } +} + +@end
diff --git a/ios/clean/chrome/browser/ui/dialogs/dialog_view_controller_unittest.mm b/ios/clean/chrome/browser/ui/dialogs/dialog_view_controller_unittest.mm new file mode 100644 index 0000000..d9a2968f --- /dev/null +++ b/ios/clean/chrome/browser/ui/dialogs/dialog_view_controller_unittest.mm
@@ -0,0 +1,43 @@ +// 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. + +#import "ios/clean/chrome/browser/ui/dialogs/dialog_view_controller.h" + +#import "ios/clean/chrome/browser/ui/dialogs/dialog_button_configuration.h" +#import "ios/clean/chrome/browser/ui/dialogs/dialog_button_style.h" +#import "ios/clean/chrome/browser/ui/dialogs/dialog_text_field_configuration.h" +#import "ios/clean/chrome/browser/ui/dialogs/test_helpers/dialog_test_util.h" +#import "ios/clean/chrome/browser/ui/dialogs/test_helpers/test_dialog_view_controller.h" +#include "testing/gtest/include/gtest/gtest.h" + +#if !defined(__has_feature) || !__has_feature(objc_arc) +#error "This file requires ARC support." +#endif + +// Tests that the dialog is properly set up via the DialogConsumer API. +TEST(DialogViewControllerTest, VerifySetup) { + NSString* const kTitle = @"Title"; + NSString* const kMessage = @"message"; + NSArray* const kButtonConfigs = @[ [DialogButtonConfiguration + configWithText:@"OK" + style:DialogButtonStyle::DEFAULT] ]; + NSArray* const kTextFieldConfigs = + @[ [DialogTextFieldConfiguration configWithDefaultText:@"default" + placeholderText:@"placeholder" + secure:YES] ]; + TestDialogViewController* dialog = [[TestDialogViewController alloc] + initWithStyle:UIAlertControllerStyleAlert]; + [dialog setDialogTitle:kTitle]; + [dialog setDialogMessage:kMessage]; + [dialog setDialogButtonConfigurations:kButtonConfigs]; + [dialog setDialogTextFieldConfigurations:kTextFieldConfigs]; + // Add the view to a container so that the alert UI is set up. + UIView* containerView = + [[UIView alloc] initWithFrame:[UIScreen mainScreen].bounds]; + dialog.view.frame = containerView.bounds; + [containerView addSubview:dialog.view]; + // Verify setup. + dialogs_test_util::TestAlertSetup(dialog, kTitle, kMessage, kButtonConfigs, + kTextFieldConfigs); +}
diff --git a/ios/clean/chrome/browser/ui/dialogs/test_helpers/BUILD.gn b/ios/clean/chrome/browser/ui/dialogs/test_helpers/BUILD.gn new file mode 100644 index 0000000..54b99216 --- /dev/null +++ b/ios/clean/chrome/browser/ui/dialogs/test_helpers/BUILD.gn
@@ -0,0 +1,32 @@ +# 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. + +source_set("test_helpers") { + testonly = true + sources = [ + "dialog_test_util.h", + "dialog_test_util.mm", + "test_dialog_mediator.h", + "test_dialog_mediator.mm", + "test_dialog_view_controller.h", + "test_dialog_view_controller.mm", + ] + + deps = [ + "//base", + "//ios/chrome/browser/browser_state", + "//ios/chrome/browser/ui/browser_list", + "//ios/chrome/browser/ui/commands", + "//ios/chrome/browser/ui/coordinators", + "//ios/chrome/browser/web_state_list", + "//ios/clean/chrome/browser/ui/commands", + "//ios/clean/chrome/browser/ui/dialogs:dialog_coordinator_support", + "//ios/clean/chrome/browser/ui/dialogs:dialog_coordinator_support", + "//ios/clean/chrome/browser/ui/dialogs:dialogs_ui", + "//ios/web", + "//testing/gtest", + ] + + configs += [ "//build/config/compiler:enable_arc" ] +}
diff --git a/ios/clean/chrome/browser/ui/dialogs/test_helpers/dialog_test_util.h b/ios/clean/chrome/browser/ui/dialogs/test_helpers/dialog_test_util.h new file mode 100644 index 0000000..d403e1d9 --- /dev/null +++ b/ios/clean/chrome/browser/ui/dialogs/test_helpers/dialog_test_util.h
@@ -0,0 +1,31 @@ +// 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 IOS_CLEAN_CHROME_BROWSER_UI_DIALOGS_TEST_HELPERS_DIALOG_TEST_UTIL_H_ +#define IOS_CLEAN_CHROME_BROWSER_UI_DIALOGS_TEST_HELPERS_DIALOG_TEST_UTIL_H_ + +#import <Foundation/Foundation.h> + +@class DialogButtonConfiguration; +@class DialogMediator; +@class DialogTextFieldConfiguration; +@class DialogViewController; + +namespace dialogs_test_util { + +// Tests that |view_controller| is set up appropriately for the provided +// DialogConsumer configuration parameters. +void TestAlertSetup(DialogViewController* view_controller, + NSString* title, + NSString* message, + NSArray<DialogButtonConfiguration*>* buttons, + NSArray<DialogTextFieldConfiguration*>* fields); + +// Tests that |view_controller| is set up appropriately for |mediator|. +void TestAlertSetup(DialogViewController* view_controller, + DialogMediator* mediator); + +} // namespace dialogs_test_util + +#endif // IOS_CLEAN_CHROME_BROWSER_UI_DIALOGS_TEST_HELPERS_DIALOG_TEST_UTIL_H_
diff --git a/ios/clean/chrome/browser/ui/dialogs/test_helpers/dialog_test_util.mm b/ios/clean/chrome/browser/ui/dialogs/test_helpers/dialog_test_util.mm new file mode 100644 index 0000000..3af2f6fc --- /dev/null +++ b/ios/clean/chrome/browser/ui/dialogs/test_helpers/dialog_test_util.mm
@@ -0,0 +1,54 @@ +// 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. + +#import "ios/clean/chrome/browser/ui/dialogs/test_helpers/dialog_test_util.h" + +#import "ios/clean/chrome/browser/ui/dialogs/dialog_button_configuration.h" +#import "ios/clean/chrome/browser/ui/dialogs/dialog_mediator+subclassing.h" +#import "ios/clean/chrome/browser/ui/dialogs/dialog_text_field_configuration.h" +#import "ios/clean/chrome/browser/ui/dialogs/dialog_view_controller.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "testing/gtest_mac.h" + +#if !defined(__has_feature) || !__has_feature(objc_arc) +#error "This file requires ARC support." +#endif + +namespace dialogs_test_util { + +void TestAlertSetup(DialogViewController* view_controller, + NSString* title, + NSString* message, + NSArray<DialogButtonConfiguration*>* buttons, + NSArray<DialogTextFieldConfiguration*>* fields) { + EXPECT_TRUE(view_controller.viewLoaded); + EXPECT_NSEQ(title, view_controller.title); + EXPECT_NSEQ(message, view_controller.message); + ASSERT_EQ(view_controller.actions.count, buttons.count); + for (NSUInteger index = 0; index < buttons.count; ++index) { + UIAlertAction* button_action = view_controller.actions[index]; + DialogButtonConfiguration* button_config = buttons[index]; + EXPECT_NSEQ(button_config.text, button_action.title); + UIAlertActionStyle button_style = [DialogViewController + alertStyleForDialogButtonStyle:button_config.style]; + EXPECT_EQ(button_style, button_action.style); + } + ASSERT_EQ(view_controller.textFields.count, fields.count); + for (NSUInteger index = 0; index < fields.count; ++index) { + UITextField* text_field = view_controller.textFields[index]; + DialogTextFieldConfiguration* field_config = fields[index]; + EXPECT_NSEQ(field_config.defaultText, text_field.text); + EXPECT_NSEQ(field_config.placeholderText, text_field.placeholder); + EXPECT_EQ(field_config.secure, text_field.secureTextEntry); + } +} + +void TestAlertSetup(DialogViewController* view_controller, + DialogMediator* mediator) { + TestAlertSetup(view_controller, [mediator dialogTitle], + [mediator dialogMessage], [mediator buttonConfigs], + [mediator textFieldConfigs]); +} + +} // namespace dialogs_test_util
diff --git a/ios/clean/chrome/browser/ui/dialogs/test_helpers/test_dialog_mediator.h b/ios/clean/chrome/browser/ui/dialogs/test_helpers/test_dialog_mediator.h new file mode 100644 index 0000000..0d5bb32a --- /dev/null +++ b/ios/clean/chrome/browser/ui/dialogs/test_helpers/test_dialog_mediator.h
@@ -0,0 +1,22 @@ +// 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 IOS_CLEAN_CHROME_BROWSER_UI_DIALOGS_TEST_HELPERS_TEST_DIALOG_MEDIATOR_H_ +#define IOS_CLEAN_CHROME_BROWSER_UI_DIALOGS_TEST_HELPERS_TEST_DIALOG_MEDIATOR_H_ + +#import "ios/clean/chrome/browser/ui/dialogs/dialog_mediator.h" + +@class DialogButtonConfiguration; +@class DialogTextFieldConfiguration; + +// A DialogMediator subclass that can be configured via properties. +@interface TestDialogMediator : DialogMediator +@property(nonatomic, copy) NSString* title; +@property(nonatomic, copy) NSString* message; +@property(nonatomic, copy) NSArray<DialogButtonConfiguration*>* buttonConfigs; +@property(nonatomic, copy) + NSArray<DialogTextFieldConfiguration*>* textFieldConfigs; +@end + +#endif // IOS_CLEAN_CHROME_BROWSER_UI_DIALOGS_TEST_HELPERS_TEST_DIALOG_MEDIATOR_H_
diff --git a/ios/clean/chrome/browser/ui/dialogs/test_helpers/test_dialog_mediator.mm b/ios/clean/chrome/browser/ui/dialogs/test_helpers/test_dialog_mediator.mm new file mode 100644 index 0000000..871f900 --- /dev/null +++ b/ios/clean/chrome/browser/ui/dialogs/test_helpers/test_dialog_mediator.mm
@@ -0,0 +1,38 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#import "ios/clean/chrome/browser/ui/dialogs/test_helpers/test_dialog_mediator.h" + +#import "ios/clean/chrome/browser/ui/dialogs/dialog_mediator+subclassing.h" + +#if !defined(__has_feature) || !__has_feature(objc_arc) +#error "This file requires ARC support." +#endif + +@implementation TestDialogMediator +@synthesize title = _title; +@synthesize message = _message; +@synthesize buttonConfigs = _buttonConfigs; +@synthesize textFieldConfigs = _textFieldConfigs; +@end + +@implementation TestDialogMediator (DialogMediatorSubclassing) + +- (NSString*)dialogTitle { + return _title; +} + +- (NSString*)dialogMessage { + return _message; +} + +- (NSArray<DialogButtonConfiguration*>*)buttonConfigs { + return _buttonConfigs; +} + +- (NSArray<DialogTextFieldConfiguration*>*)textFieldConfigs { + return _textFieldConfigs; +} + +@end
diff --git a/ios/clean/chrome/browser/ui/dialogs/test_helpers/test_dialog_view_controller.h b/ios/clean/chrome/browser/ui/dialogs/test_helpers/test_dialog_view_controller.h new file mode 100644 index 0000000..c1ad881 --- /dev/null +++ b/ios/clean/chrome/browser/ui/dialogs/test_helpers/test_dialog_view_controller.h
@@ -0,0 +1,18 @@ +// 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 IOS_CLEAN_CHROME_BROWSER_UI_DIALOGS_TEST_HELPERS_TEST_DIALOG_VIEW_CONTROLLER_H_ +#define IOS_CLEAN_CHROME_BROWSER_UI_DIALOGS_TEST_HELPERS_TEST_DIALOG_VIEW_CONTROLLER_H_ + +#import "ios/clean/chrome/browser/ui/dialogs/dialog_view_controller.h" + +// A test version of DialogViewController that doesn't dispatch commands. +@interface TestDialogViewController : DialogViewController + +// A DialogViewController with |style|. +- (instancetype)initWithStyle:(UIAlertControllerStyle)style; + +@end + +#endif // IOS_CLEAN_CHROME_BROWSER_UI_DIALOGS_TEST_HELPERS_TEST_DIALOG_VIEW_CONTROLLER_H_
diff --git a/ios/clean/chrome/browser/ui/dialogs/test_helpers/test_dialog_view_controller.mm b/ios/clean/chrome/browser/ui/dialogs/test_helpers/test_dialog_view_controller.mm new file mode 100644 index 0000000..fb046c5 --- /dev/null +++ b/ios/clean/chrome/browser/ui/dialogs/test_helpers/test_dialog_view_controller.mm
@@ -0,0 +1,46 @@ +// 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. + +#import "ios/clean/chrome/browser/ui/dialogs/test_helpers/test_dialog_view_controller.h" + +#import "ios/clean/chrome/browser/ui/commands/dialog_commands.h" + +#if !defined(__has_feature) || !__has_feature(objc_arc) +#error "This file requires ARC support." +#endif + +// Helper object that performs no-ops for DialogDismissalCommands. +@interface DummyDialogDismissalDispatcher : NSObject<DialogDismissalCommands> +@end + +@implementation DummyDialogDismissalDispatcher +- (void) +dismissDialogWithButtonID:(nonnull DialogConfigurationIdentifier*)buttonID + textFieldValues:(nonnull DialogTextFieldValues*)textFieldValues { +} +@end + +@interface TestDialogViewController () +@property(nonatomic, readonly, strong) + DummyDialogDismissalDispatcher* dispatcher; +@end + +@implementation TestDialogViewController +@synthesize dispatcher = _dispatcher; + +- (instancetype)initWithStyle:(UIAlertControllerStyle)style { + DummyDialogDismissalDispatcher* dispatcher = + [[DummyDialogDismissalDispatcher alloc] init]; + if ((self = [super initWithStyle:style dispatcher:dispatcher])) { + _dispatcher = dispatcher; + } + return self; +} + +- (instancetype)initWithStyle:(UIAlertControllerStyle)style + dispatcher:(id<DialogDismissalCommands>)dispatcher { + return [self initWithStyle:style]; +} + +@end
diff --git a/ios/clean/chrome/browser/ui/ntp/ntp_home_coordinator.mm b/ios/clean/chrome/browser/ui/ntp/ntp_home_coordinator.mm index 29ebf7d..59e294ca 100644 --- a/ios/clean/chrome/browser/ui/ntp/ntp_home_coordinator.mm +++ b/ios/clean/chrome/browser/ui/ntp/ntp_home_coordinator.mm
@@ -15,7 +15,6 @@ #include "ios/chrome/browser/browser_state/chrome_browser_state.h" #import "ios/chrome/browser/content_suggestions/content_suggestions_alert_factory.h" #import "ios/chrome/browser/content_suggestions/content_suggestions_mediator.h" -#import "ios/chrome/browser/content_suggestions/content_suggestions_metrics_recorder.h" #import "ios/chrome/browser/content_suggestions/ntp_home_metrics.h" #include "ios/chrome/browser/favicon/ios_chrome_large_icon_cache_factory.h" #include "ios/chrome/browser/favicon/ios_chrome_large_icon_service_factory.h" @@ -68,7 +67,6 @@ @property(nonatomic, strong) ContentSuggestionsViewController* viewController; @property(nonatomic, strong) ContentSuggestionsHeaderSynchronizer* headerCollectionInteractionHandler; -@property(nonatomic, strong) ContentSuggestionsMetricsRecorder* metricsRecorder; @end @@ -81,7 +79,6 @@ @synthesize suggestionsMediator = _suggestionsMediator; @synthesize headerCollectionInteractionHandler = _headerCollectionInteractionHandler; -@synthesize metricsRecorder = _metricsRecorder; #pragma mark - BrowserCoordinator @@ -143,15 +140,11 @@ self.suggestionsMediator.headerProvider = self.headerCoordinator.headerProvider; - self.metricsRecorder = [[ContentSuggestionsMetricsRecorder alloc] init]; - self.metricsRecorder.delegate = self.suggestionsMediator; - [self.viewController setDataSource:self.suggestionsMediator]; self.viewController.suggestionCommandHandler = self; self.viewController.suggestionsDelegate = self.headerCoordinator.collectionDelegate; self.viewController.audience = self; - self.viewController.metricsRecorder = self.metricsRecorder; [self.viewController addChildViewController:self.headerCoordinator.viewController]; @@ -186,7 +179,7 @@ // TODO: implement this. } -- (void)openPageForItemAtIndexPath:(NSIndexPath*)indexPath { +- (void)openPageForItem:(CollectionViewItem*)item { // TODO: implement this. } @@ -201,14 +194,6 @@ readLaterAction:(BOOL)readLaterAction { ContentSuggestionsItem* suggestionsItem = base::mac::ObjCCastStrict<ContentSuggestionsItem>(item); - - [self.metricsRecorder - onMenuOpenedForSuggestion:suggestionsItem - atIndexPath:indexPath - suggestionsShownAbove:[self.viewController - numberOfSuggestionsAbove:indexPath - .section]]; - self.alertCoordinator = [ContentSuggestionsAlertFactory alertCoordinatorForSuggestionItem:suggestionsItem onViewController:self.viewController @@ -256,7 +241,11 @@ } - (void)addItemToReadingList:(ContentSuggestionsItem*)item { - // TODO: implement this. + base::RecordAction(base::UserMetricsAction("MobileReadingListAdd")); + ReadingListModel* readingModel = ReadingListModelFactory::GetForBrowserState( + self.browser->browser_state()); + readingModel->AddEntry(item.URL, base::SysNSStringToUTF8(item.title), + reading_list::ADDED_VIA_CURRENT_APP); } - (void)dismissSuggestion:(ContentSuggestionsItem*)item @@ -268,6 +257,7 @@ [self.viewController.collectionViewModel indexPathForItem:item]; } + // TODO(crbug.com/691979): Add metrics. [self.suggestionsMediator dismissSuggestion:item.suggestionIdentifier]; [self.viewController dismissEntryAtIndexPath:itemIndexPath]; }
diff --git a/ios/clean/chrome/browser/ui/overlays/test_helpers/test_overlay_parent_coordinator.mm b/ios/clean/chrome/browser/ui/overlays/test_helpers/test_overlay_parent_coordinator.mm index 63ae4fa..9eab14f 100644 --- a/ios/clean/chrome/browser/ui/overlays/test_helpers/test_overlay_parent_coordinator.mm +++ b/ios/clean/chrome/browser/ui/overlays/test_helpers/test_overlay_parent_coordinator.mm
@@ -15,20 +15,34 @@ #endif @interface TestOverlayParentCoordinator () +// The parent view controller's window. Necessary for UIViewControllers to +// present properly. +@property(nonatomic, readonly, strong) UIWindow* window; // The parent UIViewController. -@property(nonatomic, readonly) UIViewController* viewController; +@property(nonatomic, readonly, strong) UIViewController* viewController; @end @implementation TestOverlayParentCoordinator +@synthesize window = _window; @synthesize viewController = _viewController; - (instancetype)init { if ((self = [super init])) { + _window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds]; _viewController = [[UIViewController alloc] init]; + _window.rootViewController = _viewController; + [_window makeKeyAndVisible]; } return self; } +- (void)dealloc { + [_window removeFromSuperview]; + [_window resignKeyWindow]; +} + +#pragma mark - Public + - (OverlayCoordinator*)presentedOverlay { NSUInteger childCount = self.children.count; DCHECK_LE(childCount, 1U); @@ -38,3 +52,17 @@ } @end + +@implementation TestOverlayParentCoordinator (Internal) + +- (void)childCoordinatorDidStart:(BrowserCoordinator*)childCoordinator { + [self.viewController presentViewController:childCoordinator.viewController + animated:NO + completion:nil]; +} + +- (void)childCoordinatorWillStop:(BrowserCoordinator*)childCoordinator { + [self.viewController dismissViewControllerAnimated:NO completion:nil]; +} + +@end
diff --git a/ios/clean/chrome/browser/ui/overlays/test_helpers/test_overlay_queue.h b/ios/clean/chrome/browser/ui/overlays/test_helpers/test_overlay_queue.h index 65849b6..1b3a263 100644 --- a/ios/clean/chrome/browser/ui/overlays/test_helpers/test_overlay_queue.h +++ b/ios/clean/chrome/browser/ui/overlays/test_helpers/test_overlay_queue.h
@@ -7,6 +7,7 @@ #import "ios/clean/chrome/browser/ui/overlays/overlay_queue.h" +class Browser; @class BrowserCoordinator; @class OverlayCoordinator; @@ -18,9 +19,16 @@ void StartNextOverlay() override; void AddOverlay(OverlayCoordinator* overlay); + // Seting the Browser also sets the Browser of |parent_|, which will be passed + // along to OverlayCoordinators presented by this queue when they are started. + Browser* browser() const { return browser_; } + void SetBrowser(Browser* browser); + private: // The coordinator to use as the parent for overlays added via AddOverlay(). __strong BrowserCoordinator* parent_; + // The Browser to use, if any. + Browser* browser_; DISALLOW_COPY_AND_ASSIGN(TestOverlayQueue); };
diff --git a/ios/clean/chrome/browser/ui/overlays/test_helpers/test_overlay_queue.mm b/ios/clean/chrome/browser/ui/overlays/test_helpers/test_overlay_queue.mm index 28db4a0..80797a1 100644 --- a/ios/clean/chrome/browser/ui/overlays/test_helpers/test_overlay_queue.mm +++ b/ios/clean/chrome/browser/ui/overlays/test_helpers/test_overlay_queue.mm
@@ -23,3 +23,8 @@ void TestOverlayQueue::AddOverlay(OverlayCoordinator* overlay) { OverlayQueue::AddOverlay(overlay); } + +void TestOverlayQueue::SetBrowser(Browser* browser) { + browser_ = browser; + parent_.browser = browser; +}
diff --git a/ios/clean/chrome/test/BUILD.gn b/ios/clean/chrome/test/BUILD.gn index 9035a2d..b9cf0ca5 100644 --- a/ios/clean/chrome/test/BUILD.gn +++ b/ios/clean/chrome/test/BUILD.gn
@@ -24,6 +24,7 @@ "//ios/clean/chrome/app/steps:unit_tests", "//ios/clean/chrome/browser/ui/bookmarks:unit_tests", "//ios/clean/chrome/browser/ui/context_menu:unit_tests", + "//ios/clean/chrome/browser/ui/dialogs:unit_tests", "//ios/clean/chrome/browser/ui/find_in_page:unit_tests", "//ios/clean/chrome/browser/ui/ntp:unit_tests", "//ios/clean/chrome/browser/ui/omnibox:unit_tests",
diff --git a/ios/showcase/content_suggestions/sc_content_suggestions_item.mm b/ios/showcase/content_suggestions/sc_content_suggestions_item.mm index c2c6a7af..ab193be7 100644 --- a/ios/showcase/content_suggestions/sc_content_suggestions_item.mm +++ b/ios/showcase/content_suggestions/sc_content_suggestions_item.mm
@@ -21,7 +21,6 @@ @synthesize publisher; @synthesize publicationDate; @synthesize availableOffline; -@synthesize metricsRecorded; - (instancetype)initWithType:(NSInteger)type { self = [super initWithType:type];
diff --git a/ios/showcase/content_suggestions/sc_content_suggestions_most_visited_item.mm b/ios/showcase/content_suggestions/sc_content_suggestions_most_visited_item.mm index a457c687..3a81f63 100644 --- a/ios/showcase/content_suggestions/sc_content_suggestions_most_visited_item.mm +++ b/ios/showcase/content_suggestions/sc_content_suggestions_most_visited_item.mm
@@ -16,7 +16,6 @@ @synthesize attributes; @synthesize suggestionIdentifier; @synthesize title; -@synthesize metricsRecorded; - (instancetype)initWithType:(NSInteger)type { self = [super initWithType:type];
diff --git a/ipc/BUILD.gn b/ipc/BUILD.gn index d5294db9..42621399 100644 --- a/ipc/BUILD.gn +++ b/ipc/BUILD.gn
@@ -151,6 +151,9 @@ sources = [ "ipc.mojom", ] + public_deps = [ + "//mojo/common:read_only_buffer", + ] } mojom("test_interfaces") {
diff --git a/ipc/ipc.mojom b/ipc/ipc.mojom index f986c47..3ab4d4ef 100644 --- a/ipc/ipc.mojom +++ b/ipc/ipc.mojom
@@ -4,6 +4,8 @@ module IPC.mojom; +import "mojo/common/read_only_buffer.mojom"; + // NOTE: This MUST match the value of MSG_ROUTING_NONE in src/ipc/ipc_message.h. const int32 kRoutingIdNone = -2; @@ -31,7 +33,8 @@ SetPeerPid(int32 pid); // Transmits a classical Chrome IPC message. - Receive(array<uint8> data, array<SerializedHandle>? handles); + Receive(mojo.common.mojom.ReadOnlyBuffer data, + array<SerializedHandle>? handles); // Requests a Channel-associated interface. GetAssociatedInterface(string name, associated GenericInterface& request);
diff --git a/ipc/ipc_message_pipe_reader.cc b/ipc/ipc_message_pipe_reader.cc index cd174a25..8b22681 100644 --- a/ipc/ipc_message_pipe_reader.cc +++ b/ipc/ipc_message_pipe_reader.cc
@@ -60,15 +60,12 @@ if (result != MOJO_RESULT_OK) return false; - std::vector<uint8_t> data(message->size()); - std::copy(reinterpret_cast<const uint8_t*>(message->data()), - reinterpret_cast<const uint8_t*>(message->data()) + message->size(), - data.data()); - if (!sender_) return false; - sender_->Receive(data, std::move(handles)); + sender_->Receive(base::make_span(static_cast<const uint8_t*>(message->data()), + message->size()), + std::move(handles)); DVLOG(4) << "Send " << message->type() << ": " << message->size(); return true; @@ -88,7 +85,7 @@ } void MessagePipeReader::Receive( - const std::vector<uint8_t>& data, + base::span<const uint8_t> data, base::Optional<std::vector<mojom::SerializedHandlePtr>> handles) { Message message( data.empty() ? "" : reinterpret_cast<const char*>(data.data()),
diff --git a/ipc/ipc_message_pipe_reader.h b/ipc/ipc_message_pipe_reader.h index 3fca2bb..b2f5044 100644 --- a/ipc/ipc_message_pipe_reader.h +++ b/ipc/ipc_message_pipe_reader.h
@@ -94,7 +94,7 @@ // mojom::Channel: void SetPeerPid(int32_t peer_pid) override; void Receive( - const std::vector<uint8_t>& data, + base::span<const uint8_t> data, base::Optional<std::vector<mojom::SerializedHandlePtr>> handles) override; void GetAssociatedInterface( const std::string& name,
diff --git a/ipc/ipc_mojo_bootstrap_unittest.cc b/ipc/ipc_mojo_bootstrap_unittest.cc index c6cde3e..52e261f 100644 --- a/ipc/ipc_mojo_bootstrap_unittest.cc +++ b/ipc/ipc_mojo_bootstrap_unittest.cc
@@ -42,7 +42,7 @@ on_peer_pid_set_.Run(); } - void Receive(const std::vector<uint8_t>& data, + void Receive(base::span<const uint8_t> data, base::Optional<std::vector<IPC::mojom::SerializedHandlePtr>> handles) override {}
diff --git a/mash/catalog_viewer/catalog_viewer.cc b/mash/catalog_viewer/catalog_viewer.cc index 5439b05..3d8c406 100644 --- a/mash/catalog_viewer/catalog_viewer.cc +++ b/mash/catalog_viewer/catalog_viewer.cc
@@ -53,8 +53,7 @@ SetBorder(views::CreateEmptyBorder(gfx::Insets(kPadding))); SetBackground(views::CreateStandardPanelBackground()); - views::GridLayout* layout = new views::GridLayout(this); - SetLayoutManager(layout); + views::GridLayout* layout = views::GridLayout::CreateAndInstall(this); views::ColumnSet* columns = layout->AddColumnSet(0); columns->AddColumn(views::GridLayout::FILL, views::GridLayout::FILL, 0,
diff --git a/mash/example/window_type_launcher/window_type_launcher.cc b/mash/example/window_type_launcher/window_type_launcher.cc index 0a9f0be..6ad2aa3 100644 --- a/mash/example/window_type_launcher/window_type_launcher.cc +++ b/mash/example/window_type_launcher/window_type_launcher.cc
@@ -275,8 +275,7 @@ MdTextButton::Create(this, base::ASCIIToUTF16("Jank for (s):"))), jank_duration_field_(new views::Textfield) { SetBorder(views::CreateEmptyBorder(gfx::Insets(5))); - views::GridLayout* layout = new views::GridLayout(this); - SetLayoutManager(layout); + views::GridLayout* layout = views::GridLayout::CreateAndInstall(this); views::ColumnSet* column_set = layout->AddColumnSet(0); column_set->AddColumn(views::GridLayout::LEADING, views::GridLayout::CENTER,
diff --git a/media/base/android/android_overlay.cc b/media/base/android/android_overlay.cc index e24c39c..fb05573 100644 --- a/media/base/android/android_overlay.cc +++ b/media/base/android/android_overlay.cc
@@ -7,7 +7,12 @@ namespace media { AndroidOverlay::AndroidOverlay() : weak_factory_(this) {} -AndroidOverlay::~AndroidOverlay() {} +AndroidOverlay::~AndroidOverlay() { + // Don't permit any other callbacks once we start sending deletion cbs. + weak_factory_.InvalidateWeakPtrs(); + for (auto& cb : deletion_cbs_) + std::move(cb).Run(this); +} void AndroidOverlay::AddSurfaceDestroyedCallback( AndroidOverlayConfig::DestroyedCB cb) { @@ -33,4 +38,9 @@ } } +void AndroidOverlay::AddOverlayDeletedCallback( + AndroidOverlayConfig::DeletedCB cb) { + deletion_cbs_.push_back(std::move(cb)); +} + } // namespace media
diff --git a/media/base/android/android_overlay.h b/media/base/android/android_overlay.h index c94e963..05fef5d 100644 --- a/media/base/android/android_overlay.h +++ b/media/base/android/android_overlay.h
@@ -60,12 +60,16 @@ // These will be called in the same order that they're added. void AddSurfaceDestroyedCallback(AndroidOverlayConfig::DestroyedCB cb); + // Add a callback to notify when |this| has been deleted. + void AddOverlayDeletedCallback(AndroidOverlayConfig::DeletedCB cb); + protected: AndroidOverlay(); void RunSurfaceDestroyedCallbacks(); std::list<AndroidOverlayConfig::DestroyedCB> destruction_cbs_; + std::list<AndroidOverlayConfig::DeletedCB> deletion_cbs_; base::WeakPtrFactory<AndroidOverlay> weak_factory_;
diff --git a/media/base/android_overlay_config.h b/media/base/android_overlay_config.h index 4286114a..1759d0ef 100644 --- a/media/base/android_overlay_config.h +++ b/media/base/android_overlay_config.h
@@ -21,13 +21,25 @@ using ReadyCB = base::OnceCallback<void(AndroidOverlay*)>; // Called when overlay has failed before |ReadyCB| is called. Will not be - // called after ReadyCB. It will be the last callback for the overlay. + // called after ReadyCB. It will be the last callback for the overlay, except + // for any DeletedCB. using FailedCB = base::OnceCallback<void(AndroidOverlay*)>; - // Called when the overlay has been destroyed. This will not be called unless - // ReadyCB has been called. It will be the last callback for the overlay. + // Called when the surface has been destroyed. This will not be called unless + // ReadyCB has been called. It will be the last callback for the overlay, + // except for any DeletedCB. In response, the client is expected to quit + // using the surface and delete the overlay object. using DestroyedCB = base::OnceCallback<void(AndroidOverlay*)>; + // Called when the overlay object has been deleted. This is unrelated to + // any DestroyedCB, which informs us when the surface is no longer usable and + // that we should delete the AndroidOverlay object. DeletedCB is called when + // the AndroidOverlay object is deleted for any reason. It is guaranteed that + // the overlay pointer will still be a valid pointer, but it is not safe to + // access it. It's provided just to make it easier to tell which overlay is + // being deleted. + using DeletedCB = base::OnceCallback<void(AndroidOverlay*)>; + // Configuration used to create an overlay. AndroidOverlayConfig(); AndroidOverlayConfig(AndroidOverlayConfig&&);
diff --git a/media/filters/decoder_stream.cc b/media/filters/decoder_stream.cc index fa6cd87..cbbb590ff 100644 --- a/media/filters/decoder_stream.cc +++ b/media/filters/decoder_stream.cc
@@ -654,8 +654,11 @@ // lost frames if we were to fallback then). pending_buffers_.clear(); + const DecoderConfig& config = StreamTraits::GetDecoderConfig(stream_); + traits_.OnConfigChanged(config); + if (!config_change_observer_cb_.is_null()) - config_change_observer_cb_.Run(StreamTraits::GetDecoderConfig(stream_)); + config_change_observer_cb_.Run(config); state_ = STATE_FLUSHING_DECODER; if (!reset_cb_.is_null()) {
diff --git a/media/filters/decoder_stream_traits.cc b/media/filters/decoder_stream_traits.cc index 68b1b1a..e103ddf5 100644 --- a/media/filters/decoder_stream_traits.cc +++ b/media/filters/decoder_stream_traits.cc
@@ -84,6 +84,13 @@ audio_ts_validator_->RecordOutputDuration(buffer); } +void DecoderStreamTraits<DemuxerStream::AUDIO>::OnConfigChanged( + const DecoderConfigType& config) { + // Reset validator with the latest config. Also ensures that we do not attempt + // to match timestamps across config boundaries. + audio_ts_validator_.reset(new AudioTimestampValidator(config, media_log_)); +} + // Video decoder stream traits implementation. // static
diff --git a/media/filters/decoder_stream_traits.h b/media/filters/decoder_stream_traits.h index e84e37da..58878f4 100644 --- a/media/filters/decoder_stream_traits.h +++ b/media/filters/decoder_stream_traits.h
@@ -56,6 +56,7 @@ void OnDecode(const scoped_refptr<DecoderBuffer>& buffer); void OnDecodeDone(const scoped_refptr<OutputType>& buffer); void OnStreamReset(DemuxerStream* stream); + void OnConfigChanged(const DecoderConfigType& config); private: // Validates encoded timestamps match decoded output duration. MEDIA_LOG warns @@ -93,6 +94,7 @@ void OnDecode(const scoped_refptr<DecoderBuffer>& buffer); void OnDecodeDone(const scoped_refptr<OutputType>& buffer) {} void OnStreamReset(DemuxerStream* stream); + void OnConfigChanged(const DecoderConfigType& config) {} private: base::TimeDelta last_keyframe_timestamp_;
diff --git a/media/gpu/android_video_surface_chooser_impl.cc b/media/gpu/android_video_surface_chooser_impl.cc index d2f6ae8c..06839d1 100644 --- a/media/gpu/android_video_surface_chooser_impl.cc +++ b/media/gpu/android_video_surface_chooser_impl.cc
@@ -154,6 +154,13 @@ } void AndroidVideoSurfaceChooserImpl::SwitchToSurfaceTexture() { + // Invalidate any outstanding deletion callbacks for any overlays that we've + // provided to the client already. We assume that it will eventually drop + // them in response to the callback. Ready / failed callbacks aren't affected + // by this, since we own the overlay until those occur. We're about to + // drop |overlay_|, if we have one, which cancels them. + weak_factory_.InvalidateWeakPtrs(); + // Cancel any outstanding overlay request, in case we're switching to overlay. if (overlay_) overlay_ = nullptr; @@ -181,6 +188,11 @@ // We don't modify |client_overlay_state_| yet, since we don't call the client // back yet. + // Invalidate any outstanding callbacks. This is needed only for the deletion + // callback, since for ready/failed callbacks, we still have ownership of the + // object. If we delete the object, then callbacks are cancelled anyway. + weak_factory_.InvalidateWeakPtrs(); + AndroidOverlayConfig config; // We bind all of our callbacks with weak ptrs, since we don't know how long // the client will hold on to overlays. They could, in principle, show up @@ -206,6 +218,12 @@ // back yet. DCHECK_EQ(overlay, overlay_.get()); + // Notify the overlay that we'd like to know if it's destroyed, so that we can + // update our internal state if the client drops it without being told. + overlay_->AddOverlayDeletedCallback( + base::Bind(&AndroidVideoSurfaceChooserImpl::OnOverlayDeleted, + weak_factory_.GetWeakPtr())); + client_overlay_state_ = kUsingOverlay; use_overlay_cb_.Run(std::move(overlay_)); } @@ -225,4 +243,10 @@ SwitchToSurfaceTexture(); } +void AndroidVideoSurfaceChooserImpl::OnOverlayDeleted(AndroidOverlay* overlay) { + client_overlay_state_ = kUsingSurfaceTexture; + // We don't call SwitchToSurfaceTexture since the client dropped the overlay. + // It's already using SurfaceTexture. +} + } // namespace media
diff --git a/media/gpu/android_video_surface_chooser_impl.h b/media/gpu/android_video_surface_chooser_impl.h index 74a2979..d993473 100644 --- a/media/gpu/android_video_surface_chooser_impl.h +++ b/media/gpu/android_video_surface_chooser_impl.h
@@ -52,6 +52,7 @@ // AndroidOverlay callbacks. void OnOverlayReady(AndroidOverlay*); void OnOverlayFailed(AndroidOverlay*); + void OnOverlayDeleted(AndroidOverlay*); // Client callbacks. UseOverlayCB use_overlay_cb_;
diff --git a/media/gpu/android_video_surface_chooser_impl_unittest.cc b/media/gpu/android_video_surface_chooser_impl_unittest.cc index 69e811d0..958ba4f 100644 --- a/media/gpu/android_video_surface_chooser_impl_unittest.cc +++ b/media/gpu/android_video_surface_chooser_impl_unittest.cc
@@ -43,6 +43,11 @@ // Note that this won't clear |overlay_|, which is helpful. MOCK_METHOD0(UseSurfaceTexture, void(void)); + // Let the test have the overlay. + std::unique_ptr<AndroidOverlay> ReleaseOverlay() { + return std::move(overlay_); + } + private: std::unique_ptr<AndroidOverlay> overlay_; }; @@ -299,6 +304,34 @@ overlay_callbacks_.OverlayReady.Run(); } +TEST_F(AndroidVideoSurfaceChooserImplTest, + UpdateStateAfterDeleteRetriesOverlay) { + // Make sure that SurfaceChooser notices that we delete the overlay, and have + // switched back to SurfaceTexture mode. + + chooser_state_.is_fullscreen = true; + EXPECT_CALL(*this, MockOnOverlayCreated()); + StartChooser(FactoryFor(std::move(overlay_))); + EXPECT_CALL(client_, UseOverlay(NotNull())); + overlay_callbacks_.OverlayReady.Run(); + + // Delete the overlay. + destruction_observer_ = nullptr; + client_.ReleaseOverlay(); + + // Force chooser to choose again. We expect that it will retry the overlay, + // since the delete should have informed it that we've switched back to + // SurfaceTexture without a callback from SurfaceChooser. If it didn't know + // this, then it would think that the client is still using an overlay, and + // take no action. + + // Note that if it enforces a delay here before retrying, that might be okay + // too. For now, we assume that it doesn't. + EXPECT_CALL(*this, MockOnOverlayCreated()); + chooser_->UpdateState(base::Optional<AndroidOverlayFactoryCB>(), + chooser_state_); +} + TEST_P(AndroidVideoSurfaceChooserImplTest, OverlayIsUsedOrNotBasedOnState) { // Provide a factory, and verify that it is used when the state says that it // should be. If the overlay is used, then we also verify that it does not
diff --git a/media/gpu/surface_texture_gl_owner_unittest.cc b/media/gpu/surface_texture_gl_owner_unittest.cc index 1eee962f..4db4eea 100644 --- a/media/gpu/surface_texture_gl_owner_unittest.cc +++ b/media/gpu/surface_texture_gl_owner_unittest.cc
@@ -34,7 +34,7 @@ protected: void SetUp() override { gl::init::InitializeGLOneOffImplementation(gl::kGLImplementationEGLGLES2, - false, false, false); + false, false, false, true); surface_ = new gl::PbufferGLSurfaceEGL(gfx::Size(320, 240)); surface_->Initialize();
diff --git a/media/test/pipeline_integration_test.cc b/media/test/pipeline_integration_test.cc index 1710d02..aafb52f 100644 --- a/media/test/pipeline_integration_test.cc +++ b/media/test/pipeline_integration_test.cc
@@ -24,6 +24,7 @@ #include "media/base/media.h" #include "media/base/media_switches.h" #include "media/base/media_tracks.h" +#include "media/base/mock_media_log.h" #include "media/base/test_data_util.h" #include "media/base/timestamp_constants.h" #include "media/cdm/aes_decryptor.h" @@ -76,11 +77,12 @@ #define MAYBE_TEXT(test) test #endif -using testing::_; -using testing::AnyNumber; -using testing::AtLeast; -using testing::AtMost; -using testing::SaveArg; +using ::testing::_; +using ::testing::AnyNumber; +using ::testing::AtLeast; +using ::testing::AtMost; +using ::testing::HasSubstr; +using ::testing::SaveArg; namespace media { @@ -1202,6 +1204,41 @@ Stop(); } +TEST_F(PipelineIntegrationTest, MediaSource_AudioConfigChange_WebM) { + MockMediaSource source("bear-320x240-audio-only.webm", kAudioOnlyWebM, + kAppendWholeFile); + EXPECT_EQ(PIPELINE_OK, StartPipelineWithMediaSource(&source)); + + const int kNewSampleRate = 48000; + EXPECT_CALL(*this, + OnAudioConfigChange(::testing::Property( + &AudioDecoderConfig::samples_per_second, kNewSampleRate))) + .Times(1); + + // A higher sample rate will cause the audio buffer durations to change. This + // should not manifest as a timestamp gap in AudioTimestampValidator. + // Timestamp expectations should be reset across config changes. + EXPECT_MEDIA_LOG(Not(HasSubstr("Large timestamp gap detected"))) + .Times(AnyNumber()); + + scoped_refptr<DecoderBuffer> second_file = + ReadTestDataFile("bear-320x240-audio-only-48khz.webm"); + ASSERT_TRUE(source.AppendAtTime(base::TimeDelta::FromSeconds(kAppendTimeSec), + second_file->data(), + second_file->data_size())); + source.EndOfStream(); + + Play(); + EXPECT_TRUE(WaitUntilOnEnded()); + + EXPECT_EQ(1u, pipeline_->GetBufferedTimeRanges().size()); + EXPECT_EQ(0, pipeline_->GetBufferedTimeRanges().start(0).InMilliseconds()); + EXPECT_EQ(3773, pipeline_->GetBufferedTimeRanges().end(0).InMilliseconds()); + + source.Shutdown(); + Stop(); +} + TEST_F(PipelineIntegrationTest, MediaSource_Remove_Updates_BufferedRanges) { const char* input_filename = "bear-320x240.webm"; MockMediaSource source(input_filename, kWebM, kAppendWholeFile);
diff --git a/media/test/pipeline_integration_test_base.h b/media/test/pipeline_integration_test_base.h index 19c199b..11e62e82 100644 --- a/media/test/pipeline_integration_test_base.h +++ b/media/test/pipeline_integration_test_base.h
@@ -15,6 +15,7 @@ #include "media/audio/clockless_audio_sink.h" #include "media/audio/null_audio_sink.h" #include "media/base/demuxer.h" +#include "media/base/mock_media_log.h" #include "media/base/null_video_sink.h" #include "media/base/pipeline_impl.h" #include "media/base/pipeline_status.h" @@ -25,6 +26,8 @@ #include "media/renderers/video_renderer_impl.h" #include "testing/gmock/include/gmock/gmock.h" +using ::testing::NiceMock; + namespace media { class FakeEncryptedMedia; @@ -150,7 +153,7 @@ CreateAudioDecodersCB prepend_audio_decoders_cb); protected: - MediaLog media_log_; + NiceMock<MockMediaLog> media_log_; base::test::ScopedTaskEnvironment scoped_task_environment_; base::MD5Context md5_context_; bool hashing_enabled_;
diff --git a/mojo/common/BUILD.gn b/mojo/common/BUILD.gn index 00e4a11..cef26db 100644 --- a/mojo/common/BUILD.gn +++ b/mojo/common/BUILD.gn
@@ -30,6 +30,12 @@ js_bindings_mode = "both" } +mojom("read_only_buffer") { + sources = [ + "read_only_buffer.mojom", + ] +} + component("common_base") { output_name = "mojo_common_lib"
diff --git a/mojo/common/read_only_buffer.mojom b/mojo/common/read_only_buffer.mojom new file mode 100644 index 0000000..19d08ce4 --- /dev/null +++ b/mojo/common/read_only_buffer.mojom
@@ -0,0 +1,12 @@ +// 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 mojo.common.mojom; + +// This struct is typemapped to base::span<const uint8_t>. +// Note: At the receiving end, this base::span<const uint8_t> refers to a memory +// range within the received message. +struct ReadOnlyBuffer { + array<uint8> buffer; +};
diff --git a/mojo/common/read_only_buffer.typemap b/mojo/common/read_only_buffer.typemap new file mode 100644 index 0000000..c55a855 --- /dev/null +++ b/mojo/common/read_only_buffer.typemap
@@ -0,0 +1,8 @@ +# 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. + +mojom = "//mojo/common/read_only_buffer.mojom" +public_headers = [ "//base/containers/span.h" ] +traits_headers = [ "//mojo/common/read_only_buffer_struct_traits.h" ] +type_mappings = [ "mojo.common.mojom.ReadOnlyBuffer=base::span<const uint8_t>[copyable_pass_by_value]" ]
diff --git a/mojo/common/read_only_buffer_struct_traits.h b/mojo/common/read_only_buffer_struct_traits.h new file mode 100644 index 0000000..c35efac --- /dev/null +++ b/mojo/common/read_only_buffer_struct_traits.h
@@ -0,0 +1,35 @@ +// 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 MOJO_COMMON_READ_ONLY_BUFFER_STRUCT_TRAITS_H_ +#define MOJO_COMMON_READ_ONLY_BUFFER_STRUCT_TRAITS_H_ + +#include "base/containers/span.h" +#include "mojo/common/read_only_buffer.mojom-shared.h" + +namespace mojo { + +template <> +struct StructTraits<common::mojom::ReadOnlyBufferDataView, + base::span<const uint8_t>> { + static base::span<const uint8_t> buffer(base::span<const uint8_t> input) { + return input; + } + + static bool Read(common::mojom::ReadOnlyBufferDataView input, + base::span<const uint8_t>* out) { + ArrayDataView<uint8_t> data_view; + input.GetBufferDataView(&data_view); + + // NOTE: This output directly refers to memory owned by the message. + // Therefore, the message must stay valid while the output is passed to the + // user code. + *out = base::span<const uint8_t>(data_view.data(), data_view.size()); + return true; + } +}; + +} // namespace mojo + +#endif // MOJO_COMMON_READ_ONLY_BUFFER_STRUCT_TRAITS_H_
diff --git a/mojo/common/typemaps.gni b/mojo/common/typemaps.gni index 6890c8a6..b1d54a5a 100644 --- a/mojo/common/typemaps.gni +++ b/mojo/common/typemaps.gni
@@ -7,6 +7,7 @@ "//mojo/common/file_path.typemap", "//mojo/common/memory_allocator_dump_cross_process_uid.typemap", "//mojo/common/process_id.typemap", + "//mojo/common/read_only_buffer.typemap", "//mojo/common/string16.typemap", "//mojo/common/text_direction.typemap", "//mojo/common/time.typemap",
diff --git a/net/dns/host_resolver_impl.cc b/net/dns/host_resolver_impl.cc index 422a8e8..ec790c4b 100644 --- a/net/dns/host_resolver_impl.cc +++ b/net/dns/host_resolver_impl.cc
@@ -31,7 +31,6 @@ #include "base/callback_helpers.h" #include "base/compiler_specific.h" #include "base/debug/debugger.h" -#include "base/debug/leak_annotations.h" #include "base/debug/stack_trace.h" #include "base/macros.h" #include "base/memory/ptr_util.h" @@ -1044,13 +1043,6 @@ : resolver_(resolver), result_(false) { DCHECK(resolver.get()); - // |worker_task_runner| may posts tasks to the WorkerPool, so need this to - // avoid reporting worker pool leaks in tests. The WorkerPool doesn't have a - // flushing API, so can't do anything about them, other than using another - // task runner. - // http://crbug.com/248513 - ANNOTATE_SCOPED_MEMORY_LEAK; - worker_task_runner->PostTaskAndReply( FROM_HERE, base::Bind(&LoopbackProbeJob::DoProbe, base::Unretained(this)),
diff --git a/net/quic/core/congestion_control/bandwidth_sampler.h b/net/quic/core/congestion_control/bandwidth_sampler.h index 0550bcc9..b42e8b9 100644 --- a/net/quic/core/congestion_control/bandwidth_sampler.h +++ b/net/quic/core/congestion_control/bandwidth_sampler.h
@@ -241,8 +241,13 @@ // PacketNumberIndexedQueue. ConnectionStateOnSentPacket() : sent_time(QuicTime::Zero()), + size(0), + total_bytes_sent(0), + total_bytes_sent_at_last_acked_packet(0), last_acked_packet_sent_time(QuicTime::Zero()), - last_acked_packet_ack_time(QuicTime::Zero()) {} + last_acked_packet_ack_time(QuicTime::Zero()), + total_bytes_acked_at_the_last_acked_packet(0), + is_app_limited(false) {} }; typedef QuicLinkedHashMap<QuicPacketNumber, ConnectionStateOnSentPacket>
diff --git a/remoting/host/file_proxy_wrapper.h b/remoting/host/file_proxy_wrapper.h index a674cba..88e7f5ae 100644 --- a/remoting/host/file_proxy_wrapper.h +++ b/remoting/host/file_proxy_wrapper.h
@@ -7,6 +7,7 @@ #include "base/callback.h" #include "base/files/file_proxy.h" +#include "base/optional.h" #include "remoting/proto/file_transfer.pb.h" namespace remoting { @@ -18,10 +19,6 @@ // thread, and possibly a different process depending on the platform. class FileProxyWrapper { public: - typedef base::Callback<void(protocol::FileTransferResponse_ErrorCode)> - ErrorCallback; - typedef base::Callback<void()> SuccessCallback; - enum State { // Created, but Init() has not been called yet. kUninitialized = 0, @@ -29,7 +26,8 @@ // Init() has been called. kInitialized = 1, - // CreateFile() has been called. The file may or may not exist yet. + // CreateFile() has been called. The file may or may not exist yet, but + // this means that WriteChunk() can now be called. kFileCreated = 2, // Close() has been called. WriteChunk() can no longer be called, but not @@ -44,25 +42,29 @@ kFailed = 5, }; + // If an error occured while writing the file, State will be kFailed and the + // Optional will contain the error which occured. If the file was written to + // and closed successfully, State will be kClosed and the Optional will be + // empty. + typedef base::OnceCallback< + void(State, base::Optional<protocol::FileTransferResponse_ErrorCode>)> + StatusCallback; + // Creates a platform-specific FileProxyWrapper. static std::unique_ptr<FileProxyWrapper> Create(); FileProxyWrapper(); virtual ~FileProxyWrapper(); - // |error_callback| must not immediately destroy this FileProxyWrapper. - virtual void Init(const ErrorCallback& error_callback) = 0; - // TODO(jarhar): Remove |success_callback| from CreateFile(), and instead - // allow WriteChunk() to be called immediately after CreateFile(). This also - // means that FileTransferMessageHandler will no longer send a - // FileTransferResponse with the "READY" state. + // |status_callback| is called either when FileProxyWrapper encounters an + // error or when Close() has been called and the file has been written + // successfully. |status_callback| must not immediately destroy this + // FileProxyWrapper. + virtual void Init(StatusCallback status_callback) = 0; virtual void CreateFile(const base::FilePath& directory, - const std::string& filename, - const SuccessCallback& success_callback) = 0; + const std::string& filename) = 0; virtual void WriteChunk(std::unique_ptr<CompoundBuffer> buffer) = 0; - // TODO(jarhar): Remove |success_callback| from Close() and instead use the - // ErrorCallback sent to Init() to signify success after Close(). - virtual void Close(const SuccessCallback& success_callback) = 0; + virtual void Close() = 0; virtual void Cancel() = 0; virtual State state() = 0; };
diff --git a/remoting/host/file_proxy_wrapper_linux.cc b/remoting/host/file_proxy_wrapper_linux.cc index b4029c2..5793f32 100644 --- a/remoting/host/file_proxy_wrapper_linux.cc +++ b/remoting/host/file_proxy_wrapper_linux.cc
@@ -48,12 +48,11 @@ ~FileProxyWrapperLinux() override; // FileProxyWrapper implementation. - void Init(const ErrorCallback& error_callback) override; + void Init(StatusCallback status_callback) override; void CreateFile(const base::FilePath& directory, - const std::string& filename, - const SuccessCallback& success_callback) override; + const std::string& filename) override; void WriteChunk(std::unique_ptr<CompoundBuffer> buffer) override; - void Close(const SuccessCallback& success_callback) override; + void Close() override; void Cancel() override; State state() override; @@ -64,10 +63,8 @@ }; // Callbacks for CreateFile(). - void CreateTempFile(const SuccessCallback& success_callback, - int unique_path_number); - void CreateTempFileCallback(const SuccessCallback& success_callback, - base::File::Error error); + void CreateTempFile(int unique_path_number); + void CreateTempFileCallback(base::File::Error error); // Callbacks for WriteChunk(). void WriteFileChunk(std::unique_ptr<FileChunk> chunk); @@ -86,8 +83,9 @@ scoped_refptr<base::SequencedTaskRunner> file_task_runner_; std::unique_ptr<base::FileProxy> file_proxy_; - ErrorCallback error_callback_; + StatusCallback status_callback_; + bool temp_file_created_ = false; base::FilePath temp_filepath_; base::FilePath destination_filepath_; @@ -97,8 +95,6 @@ // empty if nothing is being written to disk right now. std::unique_ptr<FileChunk> active_file_chunk_; - SuccessCallback close_success_callback_; - base::ThreadChecker thread_checker_; base::WeakPtr<FileProxyWrapperLinux> weak_ptr_; base::WeakPtrFactory<FileProxyWrapperLinux> weak_factory_; @@ -112,11 +108,11 @@ DCHECK(thread_checker_.CalledOnValidThread()); } -void FileProxyWrapperLinux::Init(const ErrorCallback& error_callback) { +void FileProxyWrapperLinux::Init(StatusCallback status_callback) { DCHECK(thread_checker_.CalledOnValidThread()); SetState(kInitialized); - error_callback_ = error_callback; + status_callback_ = std::move(status_callback); file_task_runner_ = base::CreateSequencedTaskRunnerWithTraits( {base::MayBlock(), base::TaskPriority::BACKGROUND}); @@ -129,10 +125,8 @@ } } -void FileProxyWrapperLinux::CreateFile( - const base::FilePath& directory, - const std::string& filename, - const SuccessCallback& success_callback) { +void FileProxyWrapperLinux::CreateFile(const base::FilePath& directory, + const std::string& filename) { DCHECK(thread_checker_.CalledOnValidThread()); SetState(kFileCreated); @@ -144,34 +138,37 @@ file_task_runner_.get(), FROM_HERE, base::BindOnce(&base::GetUniquePathNumber, temp_filepath_, base::FilePath::StringType()), - base::BindOnce(&FileProxyWrapperLinux::CreateTempFile, weak_ptr_, - success_callback)); + base::BindOnce(&FileProxyWrapperLinux::CreateTempFile, weak_ptr_)); } -void FileProxyWrapperLinux::CreateTempFile( - const SuccessCallback& success_callback, - int unique_path_number) { +void FileProxyWrapperLinux::CreateTempFile(int unique_path_number) { if (unique_path_number > 0) { temp_filepath_ = temp_filepath_.InsertBeforeExtensionASCII( base::StringPrintf(" (%d)", unique_path_number)); } if (!file_proxy_->CreateOrOpen( temp_filepath_, base::File::FLAG_CREATE | base::File::FLAG_WRITE, - base::Bind(&FileProxyWrapperLinux::CreateTempFileCallback, weak_ptr_, - success_callback))) { + base::Bind(&FileProxyWrapperLinux::CreateTempFileCallback, + weak_ptr_))) { // file_proxy_ failed to post a task to file_task_runner_. CancelWithError(protocol::FileTransferResponse_ErrorCode_UNEXPECTED_ERROR); } } -void FileProxyWrapperLinux::CreateTempFileCallback( - const SuccessCallback& success_callback, - base::File::Error error) { +void FileProxyWrapperLinux::CreateTempFileCallback(base::File::Error error) { if (error) { LOG(ERROR) << "Creating the temp file failed with error: " << error; CancelWithError(FileErrorToResponseError(error)); } else { - success_callback.Run(); + temp_file_created_ = true; + // Chunks to write may have been queued while we were creating the file, + // start writing them now if there were any. + if (!file_chunks_.empty()) { + std::unique_ptr<FileChunk> chunk_to_write = + std::move(file_chunks_.front()); + file_chunks_.pop(); + WriteFileChunk(std::move(chunk_to_write)); + } } } @@ -190,7 +187,9 @@ new_file_chunk->write_offset = next_write_file_offset_; next_write_file_offset_ += new_file_chunk->data.size(); - if (active_file_chunk_) { + // If the file hasn't been created yet or there is another chunk currently + // being written, we have to queue this chunk to be written later. + if (!temp_file_created_ || active_file_chunk_) { // TODO(jarhar): When flow control enabled QUIC-based WebRTC data channels // are implemented, block the flow of incoming chunks here if // file_chunks_ has reached a maximum size. This implementation will @@ -235,11 +234,10 @@ } } -void FileProxyWrapperLinux::Close(const SuccessCallback& success_callback) { +void FileProxyWrapperLinux::Close() { DCHECK(thread_checker_.CalledOnValidThread()); SetState(kClosing); - close_success_callback_ = success_callback; if (!active_file_chunk_ && file_chunks_.empty()) { // All writes are complete, so we can finish up now. @@ -282,7 +280,9 @@ if (success) { SetState(kClosed); - std::move(close_success_callback_).Run(); + std::move(status_callback_) + .Run(state_, + base::Optional<protocol::FileTransferResponse_ErrorCode>()); } else { CancelWithError(protocol::FileTransferResponse_ErrorCode_FILE_IO_ERROR); } @@ -312,7 +312,9 @@ void FileProxyWrapperLinux::CancelWithError( protocol::FileTransferResponse_ErrorCode error) { Cancel(); - std::move(error_callback_).Run(error); + std::move(status_callback_) + .Run(state_, + base::Optional<protocol::FileTransferResponse_ErrorCode>(error)); } void FileProxyWrapperLinux::SetState(State state) {
diff --git a/remoting/host/file_proxy_wrapper_linux_unittest.cc b/remoting/host/file_proxy_wrapper_linux_unittest.cc index 038b240..a8ce17c1 100644 --- a/remoting/host/file_proxy_wrapper_linux_unittest.cc +++ b/remoting/host/file_proxy_wrapper_linux_unittest.cc
@@ -48,18 +48,18 @@ return dir_.GetPath().Append(kTestFilename); } - void ErrorCallback(protocol::FileTransferResponse_ErrorCode error); - void CreateFileCallback(); - void CloseCallback(); + void StatusCallback( + FileProxyWrapper::State state, + base::Optional<protocol::FileTransferResponse_ErrorCode> error); protected: base::test::ScopedTaskEnvironment scoped_task_environment_; base::ScopedTempDir dir_; std::unique_ptr<FileProxyWrapper> file_proxy_wrapper_; - std::unique_ptr<protocol::FileTransferResponse_ErrorCode> error_; - bool create_callback_called_; - bool close_callback_called_; + base::Optional<protocol::FileTransferResponse_ErrorCode> error_; + FileProxyWrapper::State final_state_; + bool done_callback_succeeded_; }; FileProxyWrapperLinuxTest::FileProxyWrapperLinuxTest() @@ -72,53 +72,39 @@ ASSERT_TRUE(dir_.CreateUniqueTempDir()); file_proxy_wrapper_ = FileProxyWrapper::Create(); - file_proxy_wrapper_->Init(base::Bind( - &FileProxyWrapperLinuxTest::ErrorCallback, base::Unretained(this))); + file_proxy_wrapper_->Init(base::BindOnce( + &FileProxyWrapperLinuxTest::StatusCallback, base::Unretained(this))); - error_.reset(); - create_callback_called_ = false; - close_callback_called_ = false; + error_ = base::Optional<protocol::FileTransferResponse_ErrorCode>(); + final_state_ = FileProxyWrapper::kUninitialized; + done_callback_succeeded_ = false; } void FileProxyWrapperLinuxTest::TearDown() { file_proxy_wrapper_.reset(); } -void FileProxyWrapperLinuxTest::ErrorCallback( - protocol::FileTransferResponse_ErrorCode error) { - error_.reset(new protocol::FileTransferResponse_ErrorCode(error)); -} - -void FileProxyWrapperLinuxTest::CreateFileCallback() { - create_callback_called_ = true; -} - -void FileProxyWrapperLinuxTest::CloseCallback() { - close_callback_called_ = true; +void FileProxyWrapperLinuxTest::StatusCallback( + FileProxyWrapper::State state, + base::Optional<protocol::FileTransferResponse_ErrorCode> error) { + final_state_ = state; + error_ = error; + done_callback_succeeded_ = !error_.has_value(); } // Verifies that FileProxyWrapper can write three chunks to a file without // throwing any errors. TEST_F(FileProxyWrapperLinuxTest, WriteThreeChunks) { - file_proxy_wrapper_->CreateFile( - TestDir(), kTestFilename, - base::Bind(&FileProxyWrapperLinuxTest::CreateFileCallback, - base::Unretained(this))); - - scoped_task_environment_.RunUntilIdle(); - ASSERT_FALSE(error_); - ASSERT_TRUE(create_callback_called_); - + file_proxy_wrapper_->CreateFile(TestDir(), kTestFilename); file_proxy_wrapper_->WriteChunk(ToBuffer(kTestDataOne)); file_proxy_wrapper_->WriteChunk(ToBuffer(kTestDataTwo)); file_proxy_wrapper_->WriteChunk(ToBuffer(kTestDataThree)); - - file_proxy_wrapper_->Close(base::Bind( - &FileProxyWrapperLinuxTest::CloseCallback, base::Unretained(this))); - + file_proxy_wrapper_->Close(); scoped_task_environment_.RunUntilIdle(); + ASSERT_FALSE(error_); - ASSERT_TRUE(close_callback_called_); + ASSERT_EQ(final_state_, FileProxyWrapper::kClosed); + ASSERT_TRUE(done_callback_succeeded_); std::string actual_file_data; ASSERT_TRUE(base::ReadFileToString(TestFilePath(), &actual_file_data)); @@ -127,12 +113,7 @@ // Verifies that calling Cancel() deletes any temporary or destination files. TEST_F(FileProxyWrapperLinuxTest, CancelDeletesFiles) { - file_proxy_wrapper_->CreateFile( - TestDir(), kTestFilename, - base::Bind(&FileProxyWrapperLinuxTest::CreateFileCallback, - base::Unretained(this))); - scoped_task_environment_.RunUntilIdle(); - + file_proxy_wrapper_->CreateFile(TestDir(), kTestFilename); file_proxy_wrapper_->WriteChunk(ToBuffer(kTestDataOne)); scoped_task_environment_.RunUntilIdle(); @@ -148,21 +129,18 @@ TEST_F(FileProxyWrapperLinuxTest, FileAlreadyExists) { WriteFile(TestFilePath(), kTestDataOne.data(), kTestDataOne.size()); - file_proxy_wrapper_->CreateFile( - TestDir(), kTestFilename, - base::Bind(&FileProxyWrapperLinuxTest::CreateFileCallback, - base::Unretained(this))); - scoped_task_environment_.RunUntilIdle(); - + file_proxy_wrapper_->CreateFile(TestDir(), kTestFilename); file_proxy_wrapper_->WriteChunk(ToBuffer(kTestDataTwo)); - file_proxy_wrapper_->Close(base::Bind( - &FileProxyWrapperLinuxTest::CloseCallback, base::Unretained(this))); + file_proxy_wrapper_->Close(); scoped_task_environment_.RunUntilIdle(); std::string actual_file_data; base::FilePath secondary_filepath = TestDir().Append(kTestFilenameSecondary); ASSERT_TRUE(base::ReadFileToString(secondary_filepath, &actual_file_data)); ASSERT_STREQ(kTestDataTwo.data(), actual_file_data.data()); + + ASSERT_FALSE(error_); + ASSERT_EQ(final_state_, FileProxyWrapper::kClosed); } } // namespace remoting
diff --git a/remoting/proto/file_transfer.proto b/remoting/proto/file_transfer.proto index a9bb7228..12acd48e 100644 --- a/remoting/proto/file_transfer.proto +++ b/remoting/proto/file_transfer.proto
@@ -13,9 +13,8 @@ // Next Id: 4 message FileTransferResponse { enum TransferState { - READY = 1; - IN_PROGRESS = 2; - DONE = 3; + IN_PROGRESS = 1; + DONE = 2; } optional TransferState state = 1;
diff --git a/services/ui/gpu/gpu_main.cc b/services/ui/gpu/gpu_main.cc index e11d21ea..4af3ff6 100644 --- a/services/ui/gpu/gpu_main.cc +++ b/services/ui/gpu/gpu_main.cc
@@ -173,7 +173,8 @@ DCHECK(gpu_thread_task_runner_->BelongsToCurrentThread()); gpu_command_service_ = new gpu::GpuInProcessThreadService( gpu_thread_task_runner_, gpu_service_->sync_point_manager(), - gpu_service_->mailbox_manager(), gpu_service_->share_group()); + gpu_service_->mailbox_manager(), gpu_service_->share_group(), + gpu_service_->gpu_feature_info()); compositor_thread_task_runner_->PostTask( FROM_HERE,
diff --git a/services/ui/ws/BUILD.gn b/services/ui/ws/BUILD.gn index 5d265c4..3956b3c7 100644 --- a/services/ui/ws/BUILD.gn +++ b/services/ui/ws/BUILD.gn
@@ -28,6 +28,8 @@ "cursor_state.cc", "cursor_state.h", "cursor_state_delegate.h", + "debug_utils.cc", + "debug_utils.h", "default_access_policy.cc", "default_access_policy.h", "display.cc",
diff --git a/services/ui/ws/debug_utils.cc b/services/ui/ws/debug_utils.cc new file mode 100644 index 0000000..351b416 --- /dev/null +++ b/services/ui/ws/debug_utils.cc
@@ -0,0 +1,17 @@ +// 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 "services/ui/ws/debug_utils.h" + +#include "services/ui/ws/server_window.h" + +namespace ui { +namespace ws { + +std::string DebugWindowId(const ServerWindow* window) { + return window ? window->id().ToString() : "null"; +} + +} // namespace ws +} // namespace ui
diff --git a/services/ui/ws/debug_utils.h b/services/ui/ws/debug_utils.h new file mode 100644 index 0000000..dbac1c6 --- /dev/null +++ b/services/ui/ws/debug_utils.h
@@ -0,0 +1,21 @@ +// 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 SERVICES_UI_WS_DEBUG_UTILS_H_ +#define SERVICES_UI_WS_DEBUG_UTILS_H_ + +#include <string> + +namespace ui { +namespace ws { + +class ServerWindow; + +// Returns a debug string for |window|, returns "null" if |window| is null. +std::string DebugWindowId(const ServerWindow* window); + +} // namespace ws +} // namespace ui + +#endif // SERVICES_UI_WS_DEBUG_UTILS_H_
diff --git a/services/ui/ws/display.cc b/services/ui/ws/display.cc index 377fc2c2..318d34b1 100644 --- a/services/ui/ws/display.cc +++ b/services/ui/ws/display.cc
@@ -14,6 +14,7 @@ #include "services/ui/common/types.h" #include "services/ui/display/viewport_metrics.h" #include "services/ui/public/interfaces/cursor/cursor.mojom.h" +#include "services/ui/ws/debug_utils.h" #include "services/ui/ws/display_binding.h" #include "services/ui/ws/display_manager.h" #include "services/ui/ws/focus_controller.h" @@ -138,6 +139,8 @@ } bool Display::SetFocusedWindow(ServerWindow* new_focused_window) { + DVLOG(3) << "Display::SetFocusedWindow id=" + << DebugWindowId(new_focused_window); ServerWindow* old_focused_window = focus_controller_->GetFocusedWindow(); if (old_focused_window == new_focused_window) return true;
diff --git a/services/ui/ws/event_dispatcher.cc b/services/ui/ws/event_dispatcher.cc index b6acc43..c0f26a6 100644 --- a/services/ui/ws/event_dispatcher.cc +++ b/services/ui/ws/event_dispatcher.cc
@@ -517,8 +517,8 @@ if (is_mouse_event) SetMouseCursorSourceWindow(pointer_target.window); if (!any_pointers_down) { - if (pointer_target.window) - delegate_->SetFocusedWindowFromEventDispatcher(pointer_target.window); + // Don't attempt to change focus on pointer downs. We assume client code + // will do that. ServerWindow* capture_window = pointer_target.window; if (!capture_window) { gfx::Point event_location =
diff --git a/services/ui/ws/event_dispatcher_unittest.cc b/services/ui/ws/event_dispatcher_unittest.cc index ebafea0..a107fd6 100644 --- a/services/ui/ws/event_dispatcher_unittest.cc +++ b/services/ui/ws/event_dispatcher_unittest.cc
@@ -1003,18 +1003,16 @@ } TEST_P(EventDispatcherTest, DontFocusOnSecondDown) { - std::unique_ptr<ServerWindow> child1 = CreateChildWindow(WindowId(1, 3)); - std::unique_ptr<ServerWindow> child2 = CreateChildWindow(WindowId(1, 4)); + std::unique_ptr<ServerWindow> child = CreateChildWindow(WindowId(1, 3)); root_window()->SetBounds(gfx::Rect(0, 0, 100, 100)); - child1->SetBounds(gfx::Rect(10, 10, 20, 20)); - child2->SetBounds(gfx::Rect(50, 51, 11, 12)); + child->SetBounds(gfx::Rect(10, 10, 20, 20)); TestEventDispatcherDelegate* event_dispatcher_delegate = test_event_dispatcher_delegate(); EventDispatcher* dispatcher = event_dispatcher(); - // Press on child1. First press event should change focus. + // Press on |child|. Press should not change focus. const ui::PointerEvent press_event(ui::MouseEvent( ui::ET_MOUSE_PRESSED, gfx::Point(12, 12), gfx::Point(12, 12), base::TimeTicks(), ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON)); @@ -1023,20 +1021,7 @@ std::unique_ptr<DispatchedEventDetails> details = event_dispatcher_delegate->GetAndAdvanceDispatchedEventDetails(); EXPECT_FALSE(event_dispatcher_delegate->has_queued_events()); - EXPECT_EQ(child1.get(), details->window); - EXPECT_EQ(child1.get(), - event_dispatcher_delegate->GetAndClearLastFocusedWindow()); - - // Press (with a different pointer id) on child2. Event should go to child2, - // but focus should not change. - const ui::PointerEvent touch_event(ui::TouchEvent( - ui::ET_TOUCH_PRESSED, gfx::Point(53, 54), base::TimeTicks(), - ui::PointerDetails(ui::EventPointerType::POINTER_TYPE_TOUCH, 2))); - DispatchEvent(dispatcher, touch_event, 0, - EventDispatcher::AcceleratorMatchPhase::ANY); - details = event_dispatcher_delegate->GetAndAdvanceDispatchedEventDetails(); - EXPECT_FALSE(event_dispatcher_delegate->has_queued_events()); - EXPECT_EQ(child2.get(), details->window); + EXPECT_EQ(child.get(), details->window); EXPECT_EQ(nullptr, event_dispatcher_delegate->GetAndClearLastFocusedWindow()); }
diff --git a/services/ui/ws/window_tree.cc b/services/ui/ws/window_tree.cc index dab1369..7970153 100644 --- a/services/ui/ws/window_tree.cc +++ b/services/ui/ws/window_tree.cc
@@ -15,6 +15,7 @@ #include "mojo/public/cpp/bindings/map.h" #include "services/ui/display/screen_manager.h" #include "services/ui/ws/cursor_location_manager.h" +#include "services/ui/ws/debug_utils.h" #include "services/ui/ws/default_access_policy.h" #include "services/ui/ws/display.h" #include "services/ui/ws/display_manager.h" @@ -650,6 +651,9 @@ bool WindowTree::SetFocus(const ClientWindowId& window_id) { ServerWindow* window = GetWindowByClientId(window_id); ServerWindow* currently_focused = window_server_->GetFocusedWindow(); + DVLOG(3) << "SetFocusedWindow client=" << id_ + << " client window_id=" << window_id.id + << " window=" << DebugWindowId(window); if (!currently_focused && !window) { DVLOG(1) << "SetFocus failed (no focused window to clear)"; return false;
diff --git a/services/ui/ws/window_tree_unittest.cc b/services/ui/ws/window_tree_unittest.cc index b47a93c0..b3e3bd6 100644 --- a/services/ui/ws/window_tree_unittest.cc +++ b/services/ui/ws/window_tree_unittest.cc
@@ -225,8 +225,8 @@ FirstRoot(*window_tree)->set_is_activation_parent(true); } -// Verifies focus correctly changes on pointer events. -TEST_F(WindowTreeTest, FocusOnPointer) { +// Verifies focus does not change on pointer events. +TEST_F(WindowTreeTest, DontFocusOnPointer) { const ClientWindowId embed_window_id = BuildClientWindowId(wm_tree(), 1); EXPECT_TRUE( wm_tree()->NewWindow(embed_window_id, ServerWindow::Properties())); @@ -262,54 +262,12 @@ child1->SetVisible(true); child1->SetBounds(gfx::Rect(20, 20, 20, 20)); - TestWindowTreeClient* tree1_client = last_window_tree_client(); - tree1_client->tracker()->changes()->clear(); - wm_client()->tracker()->changes()->clear(); + embed_window->set_is_activation_parent(true); - // Focus should not go to |child1| yet, since the parent still doesn't allow - // active children. + // Dispatch a pointer event to the child1, focus should be null. DispatchEventAndAckImmediately(CreatePointerDownEvent(21, 22)); Display* display1 = tree1->GetDisplay(embed_window); EXPECT_EQ(nullptr, display1->GetFocusedWindow()); - DispatchEventAndAckImmediately(CreatePointerUpEvent(21, 22)); - tree1_client->tracker()->changes()->clear(); - wm_client()->tracker()->changes()->clear(); - - embed_window->set_is_activation_parent(true); - - // Focus should go to child1. This result in notifying both the window - // manager and client client being notified. - DispatchEventAndAckImmediately(CreatePointerDownEvent(21, 22)); - EXPECT_EQ(child1, display1->GetFocusedWindow()); - ASSERT_GE(wm_client()->tracker()->changes()->size(), 1u); - EXPECT_EQ("Focused id=2,1", - ChangesToDescription1(*wm_client()->tracker()->changes())[0]); - ASSERT_GE(tree1_client->tracker()->changes()->size(), 1u); - EXPECT_EQ("Focused id=2,1", - ChangesToDescription1(*tree1_client->tracker()->changes())[0]); - - DispatchEventAndAckImmediately(CreatePointerUpEvent(21, 22)); - wm_client()->tracker()->changes()->clear(); - tree1_client->tracker()->changes()->clear(); - - // Press outside of the embedded window. Note that root cannot be focused - // (because it cannot be activated). So the focus would not move in this case. - DispatchEventAndAckImmediately(CreatePointerDownEvent(61, 22)); - EXPECT_EQ(child1, display()->GetFocusedWindow()); - - DispatchEventAndAckImmediately(CreatePointerUpEvent(21, 22)); - wm_client()->tracker()->changes()->clear(); - tree1_client->tracker()->changes()->clear(); - - // Press in the same location. Should not get a focus change event (only input - // event). - DispatchEventAndAckImmediately(CreatePointerDownEvent(61, 22)); - EXPECT_EQ(child1, display()->GetFocusedWindow()); - ASSERT_EQ(1u, wm_client()->tracker()->changes()->size()) - << SingleChangeToDescription(*wm_client()->tracker()->changes()); - EXPECT_EQ("InputEvent window=0,3 event_action=16", - ChangesToDescription1(*wm_client()->tracker()->changes())[0]); - EXPECT_TRUE(tree1_client->tracker()->changes()->empty()); } TEST_F(WindowTreeTest, BasicInputEventTarget) { @@ -322,14 +280,10 @@ // Send an event to |v1|. |embed_client| should get the event, not // |wm_client|, since |v1| lives inside an embedded window. DispatchEventAndAckImmediately(CreatePointerDownEvent(21, 22)); - ASSERT_EQ(1u, wm_client()->tracker()->changes()->size()); - EXPECT_EQ("Focused id=2,1", - ChangesToDescription1(*wm_client()->tracker()->changes())[0]); - ASSERT_EQ(2u, embed_client->tracker()->changes()->size()); - EXPECT_EQ("Focused id=2,1", - ChangesToDescription1(*embed_client->tracker()->changes())[0]); + ASSERT_EQ(0u, wm_client()->tracker()->changes()->size()); + ASSERT_EQ(1u, embed_client->tracker()->changes()->size()); EXPECT_EQ("InputEvent window=2,1 event_action=16", - ChangesToDescription1(*embed_client->tracker()->changes())[1]); + ChangesToDescription1(*embed_client->tracker()->changes())[0]); } // Verifies SetChildModalParent() works correctly. @@ -417,13 +371,11 @@ ui::PointerEvent pointer_down = CreatePointerDownEvent(25, 25); DispatchEventAndAckImmediately(pointer_down); - // Expect two changes, the first is focus, the second the pointer watcher - // event. - ASSERT_EQ(2u, wm_client()->tracker()->changes()->size()); + ASSERT_EQ(1u, wm_client()->tracker()->changes()->size()); EXPECT_EQ( "PointerWatcherEvent event_action=16 window=" + ClientWindowIdToString(ClientWindowIdForWindow(wm_tree(), window)), - ChangesToDescription1(*wm_client()->tracker()->changes())[1]); + ChangesToDescription1(*wm_client()->tracker()->changes())[0]); } // Tests that a client using a pointer watcher does not receive events that
diff --git a/services/viz/privileged/interfaces/compositing/renderer_settings.mojom b/services/viz/privileged/interfaces/compositing/renderer_settings.mojom index 7d924041..daf2009 100644 --- a/services/viz/privileged/interfaces/compositing/renderer_settings.mojom +++ b/services/viz/privileged/interfaces/compositing/renderer_settings.mojom
@@ -21,4 +21,5 @@ bool should_clear_root_render_pass; bool show_overdraw_feedback; int32 slow_down_compositing_scale_factor; + bool use_skia_renderer; };
diff --git a/services/viz/privileged/interfaces/compositing/renderer_settings_struct_traits.cc b/services/viz/privileged/interfaces/compositing/renderer_settings_struct_traits.cc index e1a0c36..83c2c36ca 100644 --- a/services/viz/privileged/interfaces/compositing/renderer_settings_struct_traits.cc +++ b/services/viz/privileged/interfaces/compositing/renderer_settings_struct_traits.cc
@@ -28,6 +28,7 @@ data.disallow_non_exact_resource_reuse(); out->slow_down_compositing_scale_factor = data.slow_down_compositing_scale_factor(); + out->use_skia_renderer = data.use_skia_renderer(); return data.ReadResourceSettings(&out->resource_settings); }
diff --git a/services/viz/privileged/interfaces/compositing/renderer_settings_struct_traits.h b/services/viz/privileged/interfaces/compositing/renderer_settings_struct_traits.h index f9ce66ae..3f7e4c9 100644 --- a/services/viz/privileged/interfaces/compositing/renderer_settings_struct_traits.h +++ b/services/viz/privileged/interfaces/compositing/renderer_settings_struct_traits.h
@@ -75,6 +75,10 @@ return input.slow_down_compositing_scale_factor; } + static bool use_skia_renderer(const viz::RendererSettings& input) { + return input.use_skia_renderer; + } + static bool Read(viz::mojom::RendererSettingsDataView data, viz::RendererSettings* out); };
diff --git a/services/viz/privileged/interfaces/struct_traits_unittest.cc b/services/viz/privileged/interfaces/struct_traits_unittest.cc index e6b826a1..f13e9be 100644 --- a/services/viz/privileged/interfaces/struct_traits_unittest.cc +++ b/services/viz/privileged/interfaces/struct_traits_unittest.cc
@@ -43,6 +43,7 @@ input.enable_color_correct_rendering = true; input.highp_threshold_min = -1; input.disallow_non_exact_resource_reuse = true; + input.use_skia_renderer = true; RendererSettings output; mojom::RendererSettings::Deserialize( @@ -72,6 +73,7 @@ EXPECT_EQ(input.highp_threshold_min, output.highp_threshold_min); EXPECT_EQ(input.disallow_non_exact_resource_reuse, output.disallow_non_exact_resource_reuse); + EXPECT_EQ(input.use_skia_renderer, output.use_skia_renderer); } } // namespace
diff --git a/testing/buildbot/chromium.memory.json b/testing/buildbot/chromium.memory.json index 587a977..a7acd2143 100644 --- a/testing/buildbot/chromium.memory.json +++ b/testing/buildbot/chromium.memory.json
@@ -747,13 +747,6 @@ }, { "swarming": { - "can_use_on_swarming_builders": true, - "shards": 10 - }, - "test": "browser_tests" - }, - { - "swarming": { "can_use_on_swarming_builders": true }, "test": "cacheinvalidation_unittests"
diff --git a/testing/buildbot/filters/mojo.fyi.mus.browser_tests.filter b/testing/buildbot/filters/mojo.fyi.mus.browser_tests.filter index 5c11698..1137157 100644 --- a/testing/buildbot/filters/mojo.fyi.mus.browser_tests.filter +++ b/testing/buildbot/filters/mojo.fyi.mus.browser_tests.filter
@@ -10,6 +10,7 @@ -ForceMaximizeOnFirstRunTest.TwoRuns # TODO: fails because of screen capture. http://crbug.com/754899. +-ChromeScreenshotGrabberBrowserTest.TakeScreenshot -LoginFeedbackTest.Basic -TabCaptureApiPixelTest.EndToEndThroughWebRTC -TabCaptureApiPixelTest.EndToEndWithoutRemoting @@ -19,6 +20,9 @@ # TODO: fix, see http://crbug.com/755272. -PDFExtensionTest.RedirectsFailInPlugin +# TODO: fix, http://crbug.com/759156. +-PolicyDisplayRotationDefault/DisplayRotationDefaultTest.RefreshSecondDisplay/2 + # TODO: fix, see http://crbug.com/755303. -StartupMetricsTest.ReportsValues @@ -52,7 +56,3 @@ -WebViewTests/WebViewSizeTest.Shim_TestResizeWebviewWithDisplayNoneResizesContent/0 -WebViewTests/WebViewSizeTest.Shim_TestResizeWebviewWithDisplayNoneResizesContent/1 -WebViewTests/WebViewTest.InterstitialPageFocusedWidget/1 - -# TODO(sky): file bugs for these. --ChromeScreenshotGrabberBrowserTest.TakeScreenshot --PolicyDisplayRotationDefault/DisplayRotationDefaultTest.RefreshSecondDisplay/2
diff --git a/testing/buildbot/filters/mojo.fyi.network_content_browsertests.filter b/testing/buildbot/filters/mojo.fyi.network_content_browsertests.filter index fa433ab..f423c9e4 100644 --- a/testing/buildbot/filters/mojo.fyi.network_content_browsertests.filter +++ b/testing/buildbot/filters/mojo.fyi.network_content_browsertests.filter
@@ -108,7 +108,6 @@ -ResourceDispatcherHostBrowserTest.PageTransitionClientRedirect -ResourceDispatcherHostBrowserTest.SniffHTMLWithNoContentType -ResourceDispatcherHostBrowserTest.SniffNoContentTypeNoData --ResourceDispatcherHostBrowserTest.SyncXMLHttpRequest -SessionHistoryTest.HistoryLength -SitePerProcessBrowserTest.CrossProcessFocusChangeFiresBlurEvents -SitePerProcessBrowserTest.CrossSiteIframeBlockedByCSPInheritedBySrcDocParent @@ -137,6 +136,12 @@ # sometimes. -SRC_ClearKey/EncryptedMediaTest.FrameSizeChangeVideo/0 -# https://crbug.com/754827 --ResourceDispatcherHostBrowserTest.SyncXMLHttpRequest_Cancelled +# Cross-origin request to file:// URL should be blocked. crbug.com/759230 -BrowserSideNavigationBrowserDisableWebSecurityTest.ValidateBaseUrlForDataUrl + +# This test validates that the renderer does not hang indefinitely in a sync +# XMLHttpRequest when ResourceDispatcherHostImpl::CancelRequestsForProcess is +# called to tear down the browser-side request state without signaling +# completion. With the network service this will be equivalent to a network +# service process crash. +-ResourceDispatcherHostBrowserTest.SyncXMLHttpRequest_Cancelled
diff --git a/testing/buildbot/filters/mus.browser_tests.filter b/testing/buildbot/filters/mus.browser_tests.filter index 48acfee..1137157 100644 --- a/testing/buildbot/filters/mus.browser_tests.filter +++ b/testing/buildbot/filters/mus.browser_tests.filter
@@ -1,390 +1,58 @@ -# Failing test --BrowserTest.FullscreenBookmarkBar --BrowserTest.DisableOptionsAndImportMenuItemsConsistently --BrowserTest.ReattachDevToolsWindow +# TODO: Investigate failure, http://crbug.com/754864. +-CastStreamingApiTestWithPixelOutput.EndToEnd -# Flaky tests -crbug.com/707195 --BrowserTest.AboutVersion --BrowserTest.CanDuplicateTab --BrowserTest.ClearPendingOnFailUnlessNT --BrowserTest.DefaultMediaDevices --BrowserTest.GetSizeForNewRenderView --BrowserTest.JavascriptAlertActivatesTab --BrowserTest.Title +# TODO: Fails because RenderWidget::DidReceiveCompositorFrameAck() is +# not called as often as it was before. http://crbug.com/754846. +-ConstrainedWebDialogBrowserTest.ContentResizeInAutoResizingDialog -# Trying to whitelist -AutofillRendererTest.DontCrashWhileAssociatingForms -AutofillRendererTest.DynamicallyAddedUnownedFormElements -AutofillRendererTest.EnsureNoFormSeenIfTooFewFields -AutofillRendererTest.IgnoreNonUserGestureTextFieldChanges -AutofillRendererTest.SendForms -BrowserDialogTest.Invoke -CastSessionBrowserTest.CreateAndDestroy -ChromeContentRendererClientSearchBoxTest.RewriteThumbnailURL -ChromeRenderFrameObserverTest.SkipCapturingSubFrames -ChromeRenderViewTest.AutoplayContentSettings -ChromeRenderViewTest.ContentSettingsAllowScripts -ChromeRenderViewTest.ContentSettingsBlockScripts -ChromeRenderViewTest.ContentSettingsInterstitialPages -ChromeRenderViewTest.ContentSettingsNoscriptTag -ChromeRenderViewTest.ContentSettingsSameDocumentNavigation -ChromeRenderViewTest.DidBlockContentType -ChromeRenderViewTest.ImagesAllowedByDefault -ChromeRenderViewTest.ImagesBlockedByDefault -ChromeRenderViewTest.JSBlockSentAfterPageLoad -ChromeRenderViewTest.PluginsTemporarilyAllowed -ChromeServiceWorkerManifestFetchTest.OtherOrigin -ChromeServiceWorkerManifestFetchTest.OtherOriginUseCredentials -ChromeServiceWorkerManifestFetchTest.SameOrigin -ChromeServiceWorkerManifestFetchTest.SameOriginUseCredentials -ChromeServiceWorkerTest.CanCloseIncognitoWindowWithServiceWorkerController -ChromeServiceWorkerTest.CanShutDownWithRegisteredServiceWorker -ChromeServiceWorkerTest.FailRegisterServiceWorkerWhenJSDisabled -ChromeServiceWorkerTest.FallbackMainResourceRequestWhenJSDisabled -CloudPolicyProtoTest.VerifyProtobufEquivalence -DeviceStatusCollectorNetworkInterfacesTest.NetworkInterfaces -DeviceStatusCollectorNetworkInterfacesTest.ReportIfPublicSession -DeviceStatusCollectorTest.ActivityCrossingMidnight -DeviceStatusCollectorTest.ActivityTimesEnabledByDefault -DeviceStatusCollectorTest.ActivityTimesKeptUntilSubmittedSuccessfully -DeviceStatusCollectorTest.ActivityTimesOff -DeviceStatusCollectorTest.AllActive -DeviceStatusCollectorTest.AllIdle -DeviceStatusCollectorTest.DevSwitchBootMode -DeviceStatusCollectorTest.MaxStoredPeriods -DeviceStatusCollectorTest.MixedStates -DeviceStatusCollectorTest.NoOsUpdateStatusByDefault -DeviceStatusCollectorTest.NoRegularUserAndroidReportingWhenDisabled -DeviceStatusCollectorTest.NoRegularUserAndroidReportingWhenNotAffiliated -DeviceStatusCollectorTest.NoSessionStatusIfNoSession -DeviceStatusCollectorTest.NoSessionStatusIfSessionReportingDisabled -DeviceStatusCollectorTest.RegularUserAndroidReporting -DeviceStatusCollectorTest.ReportOsUpdateStatus -DeviceStatusCollectorTest.ReportOsUpdateStatusUpToDate -DeviceStatusCollectorTest.ReportUsers -DeviceStatusCollectorTest.StateKeptInPref -DeviceStatusCollectorTest.StateKeptInPref -DeviceStatusCollectorTest.TestAvailableMemory -DeviceStatusCollectorTest.TestCPUSamples -DeviceStatusCollectorTest.TestCPUTemp -DeviceStatusCollectorTest.TestSoundVolume -DeviceStatusCollectorTest.TestVolumeInfo -DeviceStatusCollectorTest.Times -DeviceStatusCollectorTest.VersionInfo -DownloadInterruptReasonEnumsSynced.DownloadInterruptReasonEnumsSynced -ExtensionDetermineDownloadFilenameInternal.ExtensionDetermineDownloadFilenameInternal -FormAutocompleteTest.AjaxSucceeded_FilledFormIsInvisible -FormAutocompleteTest.AjaxSucceeded_FilledFormStillVisible -FormAutocompleteTest.AjaxSucceeded_FormlessElements -FormAutocompleteTest.AjaxSucceeded_NoFormInteractionInvisible -FormAutocompleteTest.AjaxSucceeded_NoLongerVisible -FormAutocompleteTest.AjaxSucceeded_NoLongerVisible_DifferentActionsSameData -FormAutocompleteTest.AjaxSucceeded_NoLongerVisible_NoAction -FormAutocompleteTest.AjaxSucceeded_StillVisible -FormAutocompleteTest.AutoCompleteOffFormSubmit -FormAutocompleteTest.AutoCompleteOffInputSubmit -FormAutocompleteTest.CollectFormlessElements -FormAutocompleteTest.DynamicAutoCompleteOffFormSubmit -FormAutocompleteTest.InteractedFormNoLongerFocused_FocusNoLongerOnForm -FormAutocompleteTest.InteractingInDifferentForms_FocusNoLongerOnForm -FormAutocompleteTest.NoLongerVisibleBothNoActions -FormAutocompleteTest.NormalFormSubmit -FormAutocompleteTest.SubmitEventPrevented -FormAutofillTest.ClearFormWithNode -FormAutofillTest.ClearFormWithNodeContainingSelectOne -FormAutofillTest.ClearFormWithNodeContainingSelectOneForUnownedForm -FormAutofillTest.ClearFormWithNodeForUnownedForm -FormAutofillTest.ClearOnlyAutofilledFields -FormAutofillTest.ClearOnlyAutofilledFieldsForUnownedForm -FormAutofillTest.ClearPreviewedFormWithAutofilledInitiatingNode -FormAutofillTest.ClearPreviewedFormWithAutofilledInitiatingNodeForUnownedForm -FormAutofillTest.ClearPreviewedFormWithElement -FormAutofillTest.ClearPreviewedFormWithElementForUnownedForm -FormAutofillTest.ClearPreviewedFormWithElementForUnownedForm -FormAutofillTest.ClearPreviewedFormWithNonEmptyInitiatingNode -FormAutofillTest.ClearPreviewedFormWithNonEmptyInitiatingNode -FormAutofillTest.ClearPreviewedFormWithNonEmptyInitiatingNodeForUnownedForm -FormAutofillTest.ClickElement -FormAutofillTest.DetectTextDirectionFromDirectDIRAttribute -FormAutofillTest.DetectTextDirectionFromDirectStyle -FormAutofillTest.DetectTextDirectionFromParentDIRAttribute -FormAutofillTest.DetectTextDirectionFromParentStyle -FormAutofillTest.DetectTextDirectionWhenAncestorHasInlineStyle -FormAutofillTest.DetectTextDirectionWhenParentHasBothDIRAttributeAndStyle -FormAutofillTest.DetectTextDirectionWhenStyleAndDIRAttributMixed -FormAutofillTest.ExtractForms -FormAutofillTest.ExtractFormsNoFields -FormAutofillTest.ExtractFormsSkippedForms -FormAutofillTest.ExtractFormsTooFewFields -FormAutofillTest.ExtractFormsTooFewFieldsSkipsCheckable -FormAutofillTest.ExtractMultipleForms -FormAutofillTest.FillForm -FormAutofillTest.FillFormEmptyFormNames -FormAutofillTest.FillFormEmptyFormNamesForUnownedForm -FormAutofillTest.FillFormEmptyName -FormAutofillTest.FillFormEmptyNameForUnownedForm -FormAutofillTest.FillFormForUnownedForm -FormAutofillTest.FillFormForUnownedNonASCIIForm -FormAutofillTest.FillFormForUnownedNonEnglishForm -FormAutofillTest.FillFormForUnownedUntitledForm -FormAutofillTest.FillFormIncludingNonFocusableElements -FormAutofillTest.FillFormMaxLength -FormAutofillTest.FillFormMaxLengthForUnownedForm -FormAutofillTest.FillFormNegativeMaxLength -FormAutofillTest.FillFormNegativeMaxLengthForUnownedForm -FormAutofillTest.FillFormNonEmptyField -FormAutofillTest.FillFormNonEmptyFieldForUnownedForm -FormAutofillTest.FillFormNonEmptyFieldsWithDefaultValues -FormAutofillTest.FillFormNonEmptyFieldsWithPlaceholderValues -FormAutofillTest.FindFormForInputElement -FormAutofillTest.FindFormForInputElementForUnownedForm -FormAutofillTest.FindFormForTextAreaElement -FormAutofillTest.FindFormForTextAreaElementForUnownedForm -FormAutofillTest.FormCache_ExtractNewForms -FormAutofillTest.InvalidLabels -FormAutofillTest.Labels -FormAutofillTest.LabelsInferredFromBold -FormAutofillTest.LabelsInferredFromDefinitionList -FormAutofillTest.LabelsInferredFromDefinitionListRatherThanDivTable -FormAutofillTest.LabelsInferredFromDivSiblingTable -FormAutofillTest.LabelsInferredFromDivTable -FormAutofillTest.LabelsInferredFromLabelInDivTable -FormAutofillTest.LabelsInferredFromListItem -FormAutofillTest.LabelsInferredFromParagraph -FormAutofillTest.LabelsInferredFromPreviousTD -FormAutofillTest.LabelsInferredFromTableAdjacentElements -FormAutofillTest.LabelsInferredFromTableCell -FormAutofillTest.LabelsInferredFromTableCellNested -FormAutofillTest.LabelsInferredFromTableCellTH -FormAutofillTest.LabelsInferredFromTableEmptyTDs -FormAutofillTest.LabelsInferredFromTableLabels -FormAutofillTest.LabelsInferredFromTableRow -FormAutofillTest.LabelsInferredFromTableTDInterveningElements -FormAutofillTest.LabelsInferredFromTableWithSpecialElements -FormAutofillTest.LabelsInferredFromText -FormAutofillTest.LabelsInferredPriorToImgOrBr -FormAutofillTest.LabelsInferredWithImageTags -FormAutofillTest.LabelsInferredWithSameName -FormAutofillTest.LabelsWithSpans -FormAutofillTest.MaxLengthFields -FormAutofillTest.MultipleLabelsPerElement -FormAutofillTest.OneLabelElement -FormAutofillTest.OnlyExtractNewForms -FormAutofillTest.PreviewForm -FormAutofillTest.PreviewFormForUnownedForm -FormAutofillTest.PreviewFormForUnownedNonEnglishForm -FormAutofillTest.PreviewFormForUnownedUntitledForm -FormAutofillTest.SelectOneAsText -FormAutofillTest.TextAlignOverridesDirection -FormAutofillTest.ThreePartPhone -FormAutofillTest.UnmatchedFormKeywordInQueryOnly -FormAutofillTest.UnmatchedFormNoURL -FormAutofillTest.UnmatchedFormNonASCII -FormAutofillTest.UnmatchedFormPathWithoutKeywords -FormAutofillTest.UnmatchedFormTitleWithoutKeywords -FormAutofillTest.UnownedFormElementsAndFieldSetsToFormDataControlOutsideOfFieldset -FormAutofillTest.UnownedFormElementsAndFieldSetsToFormDataFieldsets -FormAutofillTest.UnownedFormElementsAndFieldSetsToFormDataWithForm -FormAutofillTest.WebFormControlElementToClickableFormField -FormAutofillTest.WebFormControlElementToFormField -FormAutofillTest.WebFormControlElementToFormFieldAutocompleteOff -FormAutofillTest.WebFormControlElementToFormFieldAutocompletetype -FormAutofillTest.WebFormControlElementToFormFieldAutofilled -FormAutofillTest.WebFormControlElementToFormFieldInvalidType -FormAutofillTest.WebFormControlElementToFormFieldLongSelect -FormAutofillTest.WebFormControlElementToFormFieldMaxLength -FormAutofillTest.WebFormControlElementToFormFieldMonthInput -FormAutofillTest.WebFormControlElementToFormFieldSelect -FormAutofillTest.WebFormControlElementToFormFieldSelect_ExtraAttributes -FormAutofillTest.WebFormControlElementToFormFieldTextArea -FormAutofillTest.WebFormControlElementToPasswordFormField -FormAutofillTest.WebFormElementConsiderNonControlLabelableElements -FormAutofillTest.WebFormElementToFormData -FormAutofillTest.WebFormElementToFormDataAutocomplete -FormAutofillTest.WebFormElementToFormData_AutocompleteOff_OnField -FormAutofillTest.WebFormElementToFormData_AutocompleteOff_OnForm -FormAutofillTest.WebFormElementToFormData_CssClasses -FormAutofillTest.WebFormElementToFormData_IdAttributes -FormClassifierTest.SigninForm -FormClassifierTest.SigninFormWithInvisibleFieldsHTML -FormClassifierTest.SigninFormWithTextFeatureInFormTagHTML -FormClassifierTest.SignupFormWithManyCheckboxesHTML -FormClassifierTest.SignupFormWithOtherFieldsHTML -FormClassifierTest.SignupFormWithSeveralPasswordFieldsHTML -FormClassifierTest.SignupFormWithSeveralTextFields -FormClassifierTest.SignupFormWithSigninButtonHTML -FormClassifierTest.SignupFormWithSigninTextFeatureAndManyFieldsHTML -FormClassifierTest.SignupFormWithTextFeatureInInputElementHTML -FormClassifierTest.SomeFormWithoutPasswordFields -FormClassifierTest.kChangeFormWithTreePasswordFieldsHTML -InstantProcessNavigationTest.ForkForNavigationsFromInstantProcess -InstantProcessNavigationTest.ForkForNavigationsToSearchURLs -PageClickTrackerTest.PageClickTrackerClickDisabledInputDoesNotResetClickCounter -PageClickTrackerTest.PageClickTrackerDisabledInputClickedNoEvent -PageClickTrackerTest.PageClickTrackerInputClicked -PageClickTrackerTest.PageClickTrackerInputFocusedAndClicked -PageClickTrackerTest.PageClickTrackerInputRightClicked -PageClickTrackerTest.PageClickTrackerScaledTextareaClicked -PageClickTrackerTest.PageClickTrackerScaledTextareaTapped -PageClickTrackerTest.PageClickTrackerTapNearEdgeIsPageClick -PageClickTrackerTest.PageClickTrackerTextAreaClicked -PageClickTrackerTest.PageClickTrackerTextAreaFocusedAndClicked -PasswordAutofillAgentTest.AutocompletePasswordForReadonlyUsernameMatched -PasswordAutofillAgentTest.AutofillNoUsernameWhenOtherCredentialsStored -PasswordAutofillAgentTest.ClearPreviewWithAutofilledUsernameAndPassword -PasswordAutofillAgentTest.ClearPreviewWithNotAutofilledUsernameAndPassword -PasswordAutofillAgentTest.ClearPreviewWithPasswordAutofilled -PasswordAutofillAgentTest.ClearPreviewWithUsernameAutofilled -PasswordAutofillAgentTest.ClickAndSelect -PasswordAutofillAgentTest.CredentialsOnClick -PasswordAutofillAgentTest.FillOnAccountSelectOnly -PasswordAutofillAgentTest.FillOnAccountSelectOnlyCredentialsOnPasswordClick -PasswordAutofillAgentTest.FillOnAccountSelectOnlyNoCredentialsOnPasswordClick -PasswordAutofillAgentTest.FillOnAccountSelectOnlyNoUsername -PasswordAutofillAgentTest.FillOnAccountSelectOnlyReadonlyNotPreferredUsername -PasswordAutofillAgentTest.FillOnAccountSelectOnlyReadonlyUnknownUsername -PasswordAutofillAgentTest.FillOnAccountSelectOnlyReadonlyUsername -PasswordAutofillAgentTest.FillSuggestion -PasswordAutofillAgentTest.FillSuggestionIfUsernameReadOnly -PasswordAutofillAgentTest.FillSuggestionPasswordChangeForms -PasswordAutofillAgentTest.FillSuggestionPasswordChangeFormsOnlyPassword -PasswordAutofillAgentTest.FindingFieldsWithAutofillPredictions -PasswordAutofillAgentTest.FindingUsernameWithoutAutofillPredictions -PasswordAutofillAgentTest.FormFillDataMustHaveUsername -PasswordAutofillAgentTest.GestureRequiredTest -PasswordAutofillAgentTest.IgnoreNotPasswordFields -PasswordAutofillAgentTest.InPageNavigationSubmissionUsernameIsEmpty -PasswordAutofillAgentTest.InitialAutocomplete -PasswordAutofillAgentTest.InitialAutocompleteForEmptyAction -PasswordAutofillAgentTest.InitialAutocompleteForMatchingFilledField -PasswordAutofillAgentTest.InputWithNoForms -PasswordAutofillAgentTest.IsWebElementVisibleTest -PasswordAutofillAgentTest.NoAction_NoPromptForAJAXSubmitWithoutNavigationAndNewElementAppeared -PasswordAutofillAgentTest.NoAutocompleteForFilledFieldUnmatched -PasswordAutofillAgentTest.NoAutocompleteForPasswordFieldUsernames -PasswordAutofillAgentTest.NoAutocompleteForTextFieldPasswords -PasswordAutofillAgentTest.NoAutocompletePasswordForReadonlyUsernameUnmatched -PasswordAutofillAgentTest.NoCredentialsOnPasswordClick -PasswordAutofillAgentTest.NoDOMActivationTest -PasswordAutofillAgentTest.NoForm_NoPromptForAJAXSubmitWithoutNavigationAndElementsVisible -PasswordAutofillAgentTest.NoForm_NoPromptForAJAXSubmitWithoutNavigationAndNewElementAppeared -PasswordAutofillAgentTest.NoForm_PromptForAJAXSubmitWithoutNavigation -PasswordAutofillAgentTest.NoInitialAutocompleteForReadOnlyPassword -PasswordAutofillAgentTest.NoPartialMatchForPrefilledUsername -PasswordAutofillAgentTest.NoopEditingDoesNotOverwriteManuallyEditedPassword -PasswordAutofillAgentTest.NotAutofillNoUsername -PasswordAutofillAgentTest.NotShowPopupPasswordField -PasswordAutofillAgentTest.OnChangeLoggingState_Activated -PasswordAutofillAgentTest.OnChangeLoggingState_Deactivated -PasswordAutofillAgentTest.OnChangeLoggingState_NoMessage -PasswordAutofillAgentTest.PasswordAutofillTriggersOnChangeEventsOnLoad -PasswordAutofillAgentTest.PasswordAutofillTriggersOnChangeEventsWaitForUsername -PasswordAutofillAgentTest.PasswordGenerationSupersedesAutofill -PasswordAutofillAgentTest.PasswordGenerationTriggered_GeneratedPassword -PasswordAutofillAgentTest.PasswordGenerationTriggered_TypedPassword -PasswordAutofillAgentTest.PasswordNotClearedOnEdit -PasswordAutofillAgentTest.PreviewSuggestion -PasswordAutofillAgentTest.PreviewSuggestionIfUsernameReadOnly -PasswordAutofillAgentTest.PreviewSuggestionSelectionRange -PasswordAutofillAgentTest.ReadonlyPasswordFieldOnSubmit -PasswordAutofillAgentTest.RememberAutofilledUsername -PasswordAutofillAgentTest.RememberChosenUsernamePassword -PasswordAutofillAgentTest.RememberFieldPropertiesOnInPageNavigation -PasswordAutofillAgentTest.RememberFieldPropertiesOnSubmit -PasswordAutofillAgentTest.RememberLastAutofilledUsernameAndPasswordOnSubmit_ScriptChanged -PasswordAutofillAgentTest.RememberLastNonEmptyUsernameAndPasswordOnSubmit_New -PasswordAutofillAgentTest.RememberLastNonEmptyUsernameAndPasswordOnSubmit_ScriptCleared -PasswordAutofillAgentTest.RememberLastNonEmptyUsernameAndPasswordOnSubmit_UserCleared -PasswordAutofillAgentTest.RememberLastTypedAfterAutofilledUsernameAndPasswordOnSubmit_ScriptChanged -PasswordAutofillAgentTest.RememberLastTypedUsernameAndPasswordOnSubmit_ScriptChanged -PasswordAutofillAgentTest.SendPasswordFormsTest_CannotCreatePasswordForm -PasswordAutofillAgentTest.SendPasswordFormsTest_EmptyForm -PasswordAutofillAgentTest.SendPasswordFormsTest_FormWithoutPasswords -PasswordAutofillAgentTest.SendPasswordFormsTest_NonDisplayedForm -PasswordAutofillAgentTest.SendPasswordFormsTest_NonVisibleForm -PasswordAutofillAgentTest.SendPasswordFormsTest_PasswordChangeForm -PasswordAutofillAgentTest.SendPasswordFormsTest_Redirection -PasswordAutofillAgentTest.SendPasswordFormsTest_ReloadTab -PasswordAutofillAgentTest.SendPasswordFormsTest_VisibleFormWithNoUsername -PasswordAutofillAgentTest.ShowAutofillSignaturesFlag -PasswordAutofillAgentTest.ShowPopupOnAutofilledPasswordField -PasswordAutofillAgentTest.ShowPopupOnEmptyPasswordField -PasswordAutofillAgentTest.ShowSuggestionForNonUsernameFieldForms -PasswordAutofillAgentTest.SuggestMultiplePasswordFields -PasswordAutofillAgentTest.SuggestPasswordFieldSignInForm -PasswordAutofillAgentTest.SuggestWhenJavaScriptUpdatesFieldNames -PasswordAutofillAgentTest.SuggestionsOnFormContainingAmbiguousOrEmptyNames -PasswordAutofillAgentTest.SuggestionsOnPasswordFieldOfChangePasswordForm -PasswordAutofillAgentTest.SuggestionsOnUsernameFieldOfChangePasswordForm -PasswordAutofillAgentTest.UsernameChangedAfterPasswordInput_FormSubmitted -PasswordAutofillAgentTest.UsernameChangedAfterPasswordInput_InPageNavigation -PasswordAutofillAgentTest.WaitUsername -PasswordGenerationAgentTest.AccountCreationFormsDetectedTest -PasswordGenerationAgentTest.AutocompleteAttributesTest -PasswordGenerationAgentTest.BlacklistedTest -PasswordGenerationAgentTest.BlurTest -PasswordGenerationAgentTest.ChangePasswordFormDetectionTest -PasswordGenerationAgentTest.ConfirmationFieldVoteFromServer -PasswordGenerationAgentTest.DetectionTest -PasswordGenerationAgentTest.DynamicFormTest -PasswordGenerationAgentTest.EditingTest -PasswordGenerationAgentTest.FillTest -PasswordGenerationAgentTest.FormClassifierDisabled -PasswordGenerationAgentTest.FormClassifierVotesSigninForm -PasswordGenerationAgentTest.FormClassifierVotesSignupForm -PasswordGenerationAgentTest.JavascriptClearedTheField -PasswordGenerationAgentTest.ManualGenerationChangeFocusTest -PasswordGenerationAgentTest.ManualGenerationInFormTest -PasswordGenerationAgentTest.ManualGenerationNoFormTest -PasswordGenerationAgentTest.MaximumOfferSize -PasswordGenerationAgentTest.MessagesAfterAccountSignupFormFound -PasswordGenerationAgentTest.MultiplePasswordFormsTest -PasswordGenerationAgentTest.PresavingGeneratedPassword -PasswordGenerationAgentTest.RevealPassword -PhishingClassifierDelegateTest.DuplicatePageCapture -PhishingClassifierDelegateTest.IgnorePreliminaryCapture -PhishingClassifierDelegateTest.Navigation -PhishingClassifierDelegateTest.NoScorer -PhishingClassifierDelegateTest.NoScorer_Ref -PhishingClassifierDelegateTest.NoStartPhishingDetection -PhishingClassifierDelegateTest.PhishingDetectionDone -PhishingDOMFeatureExtractorTest.Continuation -PhishingDOMFeatureExtractorTest.FormFeatures -PhishingDOMFeatureExtractorTest.LinkFeatures -PhishingDOMFeatureExtractorTest.ScriptAndImageFeatures -PhishingDOMFeatureExtractorTest.SubFrames -PhishingDOMFeatureExtractorTest.SubframeRemoval -PnaclHeaderTest.TestHasPnaclHeader -PolicyCertVerifierTest.VerifyTrustedCert -PolicyCertVerifierTest.VerifyUntrustedCert -PolicyCertVerifierTest.VerifyUsingAdditionalTrustAnchor -PrerenderBrowserTestWithNaCl.PrerenderNaClPluginEnabled -ScriptContextTest.GetEffectiveDocumentURL -SiteEngagementUiBrowserTest.Basic -TabCaptureCaptureOffscreenTabTest.DetermineInitialSize -TestStatsDictionaryTest.ReportFilterStats -TestStatsDictionaryTest.ReportForEach -TestStatsDictionaryTest.ReportGetAll -TestStatsDictionaryTest.ReportGetByType -TestStatsDictionaryTest.ReportGetStats -TestStatsDictionaryTest.StatsVerifyMembers -TestStatsDictionaryTest.TestStatsDictionaryShouldKeepReportAlive -ThreatDOMDetailsTest.Everything -TranslateHelperBrowserTest.BackToTranslatablePage -TranslateHelperBrowserTest.LanguageCommonMistakesAreCorrected -TranslateHelperBrowserTest.LanguageMetaTag -TranslateHelperBrowserTest.LanguageMetaTagCase -TranslateHelperBrowserTest.MultipleDifferentTranslations -TranslateHelperBrowserTest.MultipleSimilarTranslations -TranslateHelperBrowserTest.TranslatablePage -TranslateHelperBrowserTest.TranslateFailure -TranslateHelperBrowserTest.TranslateLibNeverReady -TranslateHelperBrowserTest.TranslateSuccess -TranslateHelperBrowserTest.UndefinedSourceLang -TranslateScriptBrowserTest.CallbackGetBooleanError -TranslateScriptBrowserTest.CallbackGetNumberError1 -TranslateScriptBrowserTest.CallbackGetNumberError2 -TranslateScriptBrowserTest.ElementLoadFailure -TranslateScriptBrowserTest.ElementLoadSuccess -TranslateScriptBrowserTest.TranslateFail -TranslateScriptBrowserTest.TranslateSuccess +# TODO: reenable, fails because of http://crbug.com/753593. +-ForceMaximizeOnFirstRunTest.PRE_TwoRuns +-ForceMaximizeOnFirstRunTest.TwoRuns + +# TODO: fails because of screen capture. http://crbug.com/754899. +-ChromeScreenshotGrabberBrowserTest.TakeScreenshot +-LoginFeedbackTest.Basic +-TabCaptureApiPixelTest.EndToEndThroughWebRTC +-TabCaptureApiPixelTest.EndToEndWithoutRemoting +-TabCaptureApiPixelTest.OffscreenTabEndToEnd +-TabCaptureApiPixelTest.OffscreenTabEvilTests + +# TODO: fix, see http://crbug.com/755272. +-PDFExtensionTest.RedirectsFailInPlugin + +# TODO: fix, http://crbug.com/759156. +-PolicyDisplayRotationDefault/DisplayRotationDefaultTest.RefreshSecondDisplay/2 + +# TODO: fix, see http://crbug.com/755303. +-StartupMetricsTest.ReportsValues + +# TODO: fix, see http://crbug.com/755318. +-WebViewScrollBubbling/WebViewGuestScrollTouchTest.TestGuestGestureScrollsBubble/0 +-WebViewScrollBubbling/WebViewGuestScrollTouchTest.TestGuestGestureScrollsBubble/1 +-WebViewScrollBubbling/WebViewGuestScrollTouchTest.TestGuestGestureScrollsBubble/2 +-WebViewScrollBubbling/WebViewGuestScrollTouchTest.TestGuestGestureScrollsBubble/3 + +# TODO: fix, http://crbug.com/755328 +-NaClExtensionTest.MainFrameIsRemote +-WebViewTests/WebViewDPITest.Shim_TestAutosizeBeforeNavigation/0 +-WebViewTests/WebViewDPITest.Shim_TestAutosizeBeforeNavigation/1 +-WebViewTests/WebViewDPITest.Shim_TestAutosizeHeight/0 +-WebViewTests/WebViewDPITest.Shim_TestAutosizeHeight/1 +-WebViewTests/WebViewDPITest.Shim_TestAutosizeRemoveAttributes/0 +-WebViewTests/WebViewDPITest.Shim_TestAutosizeRemoveAttributes/1 +-WebViewTests/WebViewFocusTest.TouchFocusesEmbedder/0 +-WebViewTests/WebViewSizeTest.AutoSize/0 +-WebViewTests/WebViewSizeTest.AutoSize/1 +-WebViewTests/WebViewSizeTest.Shim_TestAutosizeBeforeNavigation/0 +-WebViewTests/WebViewSizeTest.Shim_TestAutosizeBeforeNavigation/1 +-WebViewTests/WebViewSizeTest.Shim_TestAutosizeHeight/0 +-WebViewTests/WebViewSizeTest.Shim_TestAutosizeHeight/1 +-WebViewTests/WebViewSizeTest.Shim_TestAutosizeRemoveAttributes/0 +-WebViewTests/WebViewSizeTest.Shim_TestAutosizeRemoveAttributes/1 +-WebViewTests/WebViewSizeTest.Shim_TestResizeEvents/0 +-WebViewTests/WebViewSizeTest.Shim_TestResizeEvents/1 +-WebViewTests/WebViewSizeTest.Shim_TestResizeWebviewResizesContent/0 +-WebViewTests/WebViewSizeTest.Shim_TestResizeWebviewResizesContent/1 +-WebViewTests/WebViewSizeTest.Shim_TestResizeWebviewWithDisplayNoneResizesContent/0 +-WebViewTests/WebViewSizeTest.Shim_TestResizeWebviewWithDisplayNoneResizesContent/1 +-WebViewTests/WebViewTest.InterstitialPageFocusedWidget/1
diff --git a/testing/variations/fieldtrial_testing_config.json b/testing/variations/fieldtrial_testing_config.json index f18e0ee..6582acc 100644 --- a/testing/variations/fieldtrial_testing_config.json +++ b/testing/variations/fieldtrial_testing_config.json
@@ -672,7 +672,7 @@ ] } ], - "DataReductionProxyPreviewsBlacklistTransition": [ + "DataReductionProxyPreviewsBlackListTransition": [ { "platforms": [ "android" @@ -3283,6 +3283,21 @@ ] } ], + "UpdateMenuItem": [ + { + "platforms": [ + "android" + ], + "experiments": [ + { + "name": "StorageRequired500", + "params": { + "min_required_storage_for_update_mb": "500" + } + } + ] + } + ], "V8AsmJSToWasm": [ { "platforms": [
diff --git a/third_party/WebKit/LayoutTests/FlagExpectations/enable-slimming-paint-v2 b/third_party/WebKit/LayoutTests/FlagExpectations/enable-slimming-paint-v2 index cf7db01..b46d0815 100644 --- a/third_party/WebKit/LayoutTests/FlagExpectations/enable-slimming-paint-v2 +++ b/third_party/WebKit/LayoutTests/FlagExpectations/enable-slimming-paint-v2
@@ -1542,6 +1542,7 @@ Bug(none) svg/custom/marker-zero-length-linecaps.svg [ Failure ] # Text failures due to layerization differences +Bug(none) compositing/overflow/no-excessive-clip-parent-if-parent-escaped.html [ Failure ] Bug(none) compositing/overflow/scroller-with-border-radius.html [ Failure ] Bug(none) compositing/will-change/will-change-contents-suppresses-compositing.html [ Failure Crash ] Bug(none) css3/blending/mix-blend-mode-2nd-stacking-context-composited.html [ Failure ]
diff --git a/third_party/WebKit/LayoutTests/NeverFixTests b/third_party/WebKit/LayoutTests/NeverFixTests index e2b460d0..04df249 100644 --- a/third_party/WebKit/LayoutTests/NeverFixTests +++ b/third_party/WebKit/LayoutTests/NeverFixTests
@@ -195,6 +195,29 @@ [ Mac ] external/wpt/html/dom/elements/requirements-relating-to-bidirectional-algorithm-formatting-characters/dir-isolation-009b.html [ WontFix ] [ Mac ] external/wpt/html/dom/elements/requirements-relating-to-bidirectional-algorithm-formatting-characters/dir-isolation-009c.html [ WontFix ] +# Failing css-transforms-2 web platform tests. +crbug.com/753080 external/wpt/css/css-transforms-2/backface-visibility-hidden-001.html [ WontFix ] +crbug.com/753080 external/wpt/css/css-transforms-2/rotate_x_45deg.html [ WontFix ] +crbug.com/753080 external/wpt/css/css-transforms-2/rotate_y_45deg.html [ WontFix ] +crbug.com/753080 external/wpt/css/css-transforms-2/transform-3d-rotateY-stair-below-001.xht [ WontFix ] +crbug.com/753080 external/wpt/css/css-transforms-2/transform3d-image-scale-001.html [ WontFix ] +crbug.com/753080 external/wpt/css/css-transforms-2/transform3d-image-scale-002.html [ WontFix ] +crbug.com/753080 external/wpt/css/css-transforms-2/transform3d-matrix3d-001.html [ WontFix ] +crbug.com/753080 external/wpt/css/css-transforms-2/transform3d-perspective-003.html [ WontFix ] +crbug.com/753080 external/wpt/css/css-transforms-2/transform3d-perspective-004.html [ WontFix ] +crbug.com/753080 external/wpt/css/css-transforms-2/transform3d-perspective-005.html [ WontFix ] +crbug.com/753080 external/wpt/css/css-transforms-2/transform3d-preserve3d-010.html [ WontFix ] +crbug.com/753080 external/wpt/css/css-transforms-2/transform3d-preserve3d-013.html [ WontFix ] +crbug.com/753080 external/wpt/css/css-transforms-2/transform3d-rotatex-perspective-003.html [ WontFix ] +crbug.com/753080 external/wpt/css/css-transforms-2/transform3d-scale-005.html [ WontFix ] +crbug.com/753080 external/wpt/css/css-transforms-2/transform3d-scale-006.html [ WontFix ] +crbug.com/753080 external/wpt/css/css-transforms-2/transform3d-scale-007.html [ WontFix ] +crbug.com/753080 external/wpt/css/css-transforms-2/transform3d-sorting-002.html [ WontFix ] +crbug.com/753080 external/wpt/css/css-transforms-2/transform3d-sorting-004.html [ WontFix ] +crbug.com/753080 external/wpt/css/css-transforms-2/transform3d-sorting-006.html [ WontFix ] +crbug.com/753080 external/wpt/css/css-transforms-2/ttwf-css-3d-polygon-cycle-mismatch.html [ WontFix ] +crbug.com/753080 external/wpt/css/css-transforms-2/ttwf-css-3d-polygon-cycle.html [ WontFix ] + # We could fix this test for us and upstream it if the test shell user agent # would let us differentiate test_shell and WebKit DumpTreeNode. crbug.com/7482 [ Win Mac ] http/tests/misc/timer-vs-loading.html [ WontFix ] @@ -249,7 +272,6 @@ # WPT subdirectories without owners. external/wpt/accelerometer [ WontFix ] external/wpt/assumptions [ WontFix ] -external/wpt/css/css-transforms-2 [ WontFix ] external/wpt/css-values [ WontFix ] external/wpt/css/css-values-3 [ WontFix ] external/wpt/gyroscope [ WontFix ]
diff --git a/third_party/WebKit/LayoutTests/compositing/overflow/clip-parent-across-transform-boundary-expected.html b/third_party/WebKit/LayoutTests/compositing/overflow/clip-parent-across-transform-boundary-expected.html new file mode 100644 index 0000000..13372c93 --- /dev/null +++ b/third_party/WebKit/LayoutTests/compositing/overflow/clip-parent-across-transform-boundary-expected.html
@@ -0,0 +1,3 @@ +<!DOCTYPE html> +<div style="margin-left:100px; margin-top:100px; width:100px; height:100px; background:green;"></div> +This test succeeds if a non-rotated 100x100 green box is shown above.
diff --git a/third_party/WebKit/LayoutTests/compositing/overflow/clip-parent-across-transform-boundary.html b/third_party/WebKit/LayoutTests/compositing/overflow/clip-parent-across-transform-boundary.html new file mode 100644 index 0000000..7ef81b7 --- /dev/null +++ b/third_party/WebKit/LayoutTests/compositing/overflow/clip-parent-across-transform-boundary.html
@@ -0,0 +1,9 @@ +<!DOCTYPE html> +<div style="overflow:hidden; margin-left:100px; margin-top:100px; width:100px; height:100px;"> + <div style="position:relative; left:-50px; top:-50px; width:200px; height:200px; transform:rotate(30deg);"> + <div style="will-change:opacity; overflow:hidden;"> + <div style="position:absolute; width:200px; height:200px; background:green;"></div> + </div> + </div> +</div> +This test succeeds if a non-rotated 100x100 green box is shown above.
diff --git a/third_party/WebKit/LayoutTests/compositing/overflow/escape-clip-and-paint-before-clip-parent-expected.html b/third_party/WebKit/LayoutTests/compositing/overflow/escape-clip-and-paint-before-clip-parent-expected.html new file mode 100644 index 0000000..8cf0a8f --- /dev/null +++ b/third_party/WebKit/LayoutTests/compositing/overflow/escape-clip-and-paint-before-clip-parent-expected.html
@@ -0,0 +1,4 @@ +<!DOCTYPE html> +This test succeeds if a box escapes clip from a clipped stacking context +correctly while its clipping container paints after it. +<div style="width:200px; height:200px; background:blue;"></div>
diff --git a/third_party/WebKit/LayoutTests/compositing/overflow/escape-clip-and-paint-before-clip-parent.html b/third_party/WebKit/LayoutTests/compositing/overflow/escape-clip-and-paint-before-clip-parent.html new file mode 100644 index 0000000..ce5e73c --- /dev/null +++ b/third_party/WebKit/LayoutTests/compositing/overflow/escape-clip-and-paint-before-clip-parent.html
@@ -0,0 +1,11 @@ +<!DOCTYPE html> +This test succeeds if a box escapes clip from a clipped stacking context +correctly while its clipping container paints after it. +<div style="overflow:hidden; width:100px; height:100px;"> + <div style="will-change:opacity;"> + <div style="position:absolute; overflow:hidden; width:200px; height:200px; background:blue;"> + <div style="position:relative; z-index:-1; width:250px; height:250px; background:green; will-change:opacity;"> + </div> + </div> + </div> +</div>
diff --git a/third_party/WebKit/LayoutTests/compositing/overflow/fixed-pos-escape-clip-and-apply-noncomposited-clip-expected.html b/third_party/WebKit/LayoutTests/compositing/overflow/fixed-pos-escape-clip-and-apply-noncomposited-clip-expected.html new file mode 100644 index 0000000..ab20a0f2 --- /dev/null +++ b/third_party/WebKit/LayoutTests/compositing/overflow/fixed-pos-escape-clip-and-apply-noncomposited-clip-expected.html
@@ -0,0 +1,4 @@ +<!DOCTYPE html> +This test verifies fixed-pos element escapes clip correctly, and then apply clip from its true clipping container even if the container is not composited. +It succeeds if a 100x100 green box is shown below. +<div style="width:100px; height:100px; background:green;"></div>
diff --git a/third_party/WebKit/LayoutTests/compositing/overflow/fixed-pos-escape-clip-and-apply-noncomposited-clip.html b/third_party/WebKit/LayoutTests/compositing/overflow/fixed-pos-escape-clip-and-apply-noncomposited-clip.html new file mode 100644 index 0000000..ab98daf2 --- /dev/null +++ b/third_party/WebKit/LayoutTests/compositing/overflow/fixed-pos-escape-clip-and-apply-noncomposited-clip.html
@@ -0,0 +1,14 @@ +<!DOCTYPE html> +This test verifies fixed-pos element escapes clip correctly, and then apply clip from its true clipping container even if the container is not composited. +It succeeds if a 100x100 green box is shown below. +<div style="overflow:hidden; width:100px; height:100px;"> + <div style="transform:translateX(0); width:100px; height:100px;"> + <div style="position:relative; width:10px; height:10px; overflow:hidden;"> + <div style="will-change:transform;">Promote</div> + <div style="position:absolute; z-index:0; top:0; left:0; width:300px; height:200px;"> + <div style="position:fixed; left:0; top:0; width:200px; height:200px; background:green;"> + </div> + </div> + </div> + </div> +</div>
diff --git a/third_party/WebKit/LayoutTests/compositing/overflow/fixed-pos-escape-clip-having-non-stacking-context-clipping-container-expected.html b/third_party/WebKit/LayoutTests/compositing/overflow/fixed-pos-escape-clip-having-non-stacking-context-clipping-container-expected.html new file mode 100644 index 0000000..430063a --- /dev/null +++ b/third_party/WebKit/LayoutTests/compositing/overflow/fixed-pos-escape-clip-having-non-stacking-context-clipping-container-expected.html
@@ -0,0 +1,4 @@ +<!DOCTYPE html> +This test verifies fixed-pos element escapes clip correctly, even if its true clipping container is not a stacking context. +It succeeds if a 50x50 green box is shown below. +<div style="width:50px; height:50px; background:green;"></div>
diff --git a/third_party/WebKit/LayoutTests/compositing/overflow/fixed-pos-escape-clip-having-non-stacking-context-clipping-container.html b/third_party/WebKit/LayoutTests/compositing/overflow/fixed-pos-escape-clip-having-non-stacking-context-clipping-container.html new file mode 100644 index 0000000..0ac0617 --- /dev/null +++ b/third_party/WebKit/LayoutTests/compositing/overflow/fixed-pos-escape-clip-having-non-stacking-context-clipping-container.html
@@ -0,0 +1,14 @@ +<!DOCTYPE html> +This test verifies fixed-pos element escapes clip correctly, even if its true clipping container is not a stacking context. +It succeeds if a 50x50 green box is shown below. +<div style="overflow:hidden; width:100px; height:100px;"> + <div style="transform:translateX(0); width:100px; height:100px;"> + <div style="position:relative; width:10px; height:10px; overflow:hidden;"> + <div style="will-change:transform;">Promote</div> + <div style="position:absolute; z-index:0; top:0; left:0; width:300px; height:200px;"> + <div style="position:fixed; left:0; top:0; width:50px; height:50px; background:green;"> + </div> + </div> + </div> + </div> +</div>
diff --git a/third_party/WebKit/LayoutTests/compositing/overflow/no-excessive-clip-parent-if-parent-escaped-expected.png b/third_party/WebKit/LayoutTests/compositing/overflow/no-excessive-clip-parent-if-parent-escaped-expected.png new file mode 100644 index 0000000..cba76e35 --- /dev/null +++ b/third_party/WebKit/LayoutTests/compositing/overflow/no-excessive-clip-parent-if-parent-escaped-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/compositing/overflow/no-excessive-clip-parent-if-parent-escaped-expected.txt b/third_party/WebKit/LayoutTests/compositing/overflow/no-excessive-clip-parent-if-parent-escaped-expected.txt new file mode 100644 index 0000000..85819e0 --- /dev/null +++ b/third_party/WebKit/LayoutTests/compositing/overflow/no-excessive-clip-parent-if-parent-escaped-expected.txt
@@ -0,0 +1,31 @@ +{ + "layers": [ + { + "name": "LayoutView #document", + "bounds": [800, 600], + "contentsOpaque": true, + "drawsContent": true + }, + { + "name": "LayoutBlockFlow DIV", + "position": [8, 8], + "bounds": [100, 100], + "contentsOpaque": true, + "drawsContent": true, + "backgroundColor": "#FF0000" + }, + { + "name": "Child Containment Layer", + "bounds": [100, 100] + }, + { + "name": "LayoutBlockFlow (positioned) DIV", + "bounds": [50, 50], + "contentsOpaque": true, + "drawsContent": true, + "backgroundColor": "#008000", + "hasClipParent": true + } + ] +} +
diff --git a/third_party/WebKit/LayoutTests/compositing/overflow/no-excessive-clip-parent-if-parent-escaped.html b/third_party/WebKit/LayoutTests/compositing/overflow/no-excessive-clip-parent-if-parent-escaped.html new file mode 100644 index 0000000..c34abf3 --- /dev/null +++ b/third_party/WebKit/LayoutTests/compositing/overflow/no-excessive-clip-parent-if-parent-escaped.html
@@ -0,0 +1,13 @@ +<!DOCTYPE html> +<div style="width:100px; height:100px; will-change:opacity; overflow:hidden; background:red;"> + <div style="position:absolute; z-index:0; width:50px; height:50px; background:green;"> + <div style="position:absolute; width:25px; height:25px; background:blue;"></div> + </div> +</div><!-- This test suceeds if the innermost element does not set clip parent, and is not composited unnecessarily. --> +<script> +if (window.testRunner) { + testRunner.dumpAsTextWithPixelResults(); + testRunner.setCustomTextOutput( + internals.layerTreeAsText(document, internals.LAYER_TREE_INCLUDES_CLIP_AND_SCROLL_PARENTS)); +} +</script>
diff --git a/third_party/WebKit/LayoutTests/compositing/overflow/scroll-parent-absolute-with-backdrop-filter-expected.txt b/third_party/WebKit/LayoutTests/compositing/overflow/scroll-parent-absolute-with-backdrop-filter-expected.txt index fe0f1835..9a67aee 100644 --- a/third_party/WebKit/LayoutTests/compositing/overflow/scroll-parent-absolute-with-backdrop-filter-expected.txt +++ b/third_party/WebKit/LayoutTests/compositing/overflow/scroll-parent-absolute-with-backdrop-filter-expected.txt
@@ -37,7 +37,8 @@ "contentsOpaque": true, "drawsContent": true, "backfaceVisibility": "hidden", - "backgroundColor": "#FFFF00" + "backgroundColor": "#FFFF00", + "hasClipParent": true }, { "name": "LayoutBlockFlow (relative positioned) DIV id='tall'",
diff --git a/third_party/WebKit/LayoutTests/paint/invalidation/filters/effect-reference-repaint-composite-1-expected.txt b/third_party/WebKit/LayoutTests/paint/invalidation/filters/effect-reference-repaint-composite-1-expected.txt index 064c879..a6098d03 100644 --- a/third_party/WebKit/LayoutTests/paint/invalidation/filters/effect-reference-repaint-composite-1-expected.txt +++ b/third_party/WebKit/LayoutTests/paint/invalidation/filters/effect-reference-repaint-composite-1-expected.txt
@@ -8,7 +8,7 @@ "paintInvalidations": [ { "object": "LayoutBlockFlow DIV class='box'", - "rect": [128, 120, 200, 200], + "rect": [128, 120, 100, 100], "reason": "style change" } ]
diff --git a/third_party/WebKit/LayoutTests/paint/invalidation/filters/effect-reference-repaint-composite-5-expected.txt b/third_party/WebKit/LayoutTests/paint/invalidation/filters/effect-reference-repaint-composite-5-expected.txt index 064c879..a6098d03 100644 --- a/third_party/WebKit/LayoutTests/paint/invalidation/filters/effect-reference-repaint-composite-5-expected.txt +++ b/third_party/WebKit/LayoutTests/paint/invalidation/filters/effect-reference-repaint-composite-5-expected.txt
@@ -8,7 +8,7 @@ "paintInvalidations": [ { "object": "LayoutBlockFlow DIV class='box'", - "rect": [128, 120, 200, 200], + "rect": [128, 120, 100, 100], "reason": "style change" } ]
diff --git a/third_party/WebKit/LayoutTests/virtual/prefer_compositing_to_lcd_text/compositing/overflow/scroll-parent-absolute-with-backdrop-filter-expected.txt b/third_party/WebKit/LayoutTests/virtual/prefer_compositing_to_lcd_text/compositing/overflow/scroll-parent-absolute-with-backdrop-filter-expected.txt index fe0f1835..9a67aee 100644 --- a/third_party/WebKit/LayoutTests/virtual/prefer_compositing_to_lcd_text/compositing/overflow/scroll-parent-absolute-with-backdrop-filter-expected.txt +++ b/third_party/WebKit/LayoutTests/virtual/prefer_compositing_to_lcd_text/compositing/overflow/scroll-parent-absolute-with-backdrop-filter-expected.txt
@@ -37,7 +37,8 @@ "contentsOpaque": true, "drawsContent": true, "backfaceVisibility": "hidden", - "backgroundColor": "#FFFF00" + "backgroundColor": "#FFFF00", + "hasClipParent": true }, { "name": "LayoutBlockFlow (relative positioned) DIV id='tall'",
diff --git a/third_party/WebKit/Source/core/layout/MinMaxSize.cpp b/third_party/WebKit/Source/core/layout/MinMaxSize.cpp index 326ebda4..44ea9d5 100644 --- a/third_party/WebKit/Source/core/layout/MinMaxSize.cpp +++ b/third_party/WebKit/Source/core/layout/MinMaxSize.cpp
@@ -4,6 +4,8 @@ #include "core/layout/MinMaxSize.h" +#include <algorithm> + namespace blink { LayoutUnit MinMaxSize::ShrinkToFit(LayoutUnit available_size) const {
diff --git a/third_party/WebKit/Source/core/layout/ng/geometry/ng_margin_strut.cc b/third_party/WebKit/Source/core/layout/ng/geometry/ng_margin_strut.cc index d7bb1168..7c06eb5f 100644 --- a/third_party/WebKit/Source/core/layout/ng/geometry/ng_margin_strut.cc +++ b/third_party/WebKit/Source/core/layout/ng/geometry/ng_margin_strut.cc
@@ -4,6 +4,8 @@ #include "core/layout/ng/geometry/ng_margin_strut.h" +#include <algorithm> + namespace blink { LayoutUnit NGMarginStrut::Sum() const {
diff --git a/third_party/WebKit/Source/core/layout/ng/geometry/ng_physical_location.cc b/third_party/WebKit/Source/core/layout/ng/geometry/ng_physical_location.cc index d2ceada..c6d17e0 100644 --- a/third_party/WebKit/Source/core/layout/ng/geometry/ng_physical_location.cc +++ b/third_party/WebKit/Source/core/layout/ng/geometry/ng_physical_location.cc
@@ -4,6 +4,7 @@ #include "core/layout/ng/geometry/ng_physical_location.h" +#include <ostream> #include "platform/wtf/text/WTFString.h" namespace blink { @@ -16,4 +17,8 @@ return String::Format("%dx%d", left.ToInt(), top.ToInt()); } +std::ostream& operator<<(std::ostream& os, const NGPhysicalLocation& value) { + return os << value.ToString(); +} + } // namespace blink
diff --git a/third_party/WebKit/Source/core/layout/ng/geometry/ng_physical_location.h b/third_party/WebKit/Source/core/layout/ng/geometry/ng_physical_location.h index 2b40c5e8..b72dbd3 100644 --- a/third_party/WebKit/Source/core/layout/ng/geometry/ng_physical_location.h +++ b/third_party/WebKit/Source/core/layout/ng/geometry/ng_physical_location.h
@@ -24,10 +24,7 @@ String ToString() const; }; -CORE_EXPORT inline std::ostream& operator<<(std::ostream& os, - const NGPhysicalLocation& value) { - return os << value.ToString(); -} +CORE_EXPORT std::ostream& operator<<(std::ostream&, const NGPhysicalLocation&); } // namespace blink
diff --git a/third_party/WebKit/Source/core/layout/ng/ng_block_break_token.h b/third_party/WebKit/Source/core/layout/ng/ng_block_break_token.h index 72323d5..5469037 100644 --- a/third_party/WebKit/Source/core/layout/ng/ng_block_break_token.h +++ b/third_party/WebKit/Source/core/layout/ng/ng_block_break_token.h
@@ -8,6 +8,8 @@ #include "core/CoreExport.h" #include "core/layout/ng/ng_break_token.h" #include "platform/LayoutUnit.h" +#include "platform/wtf/RefPtr.h" +#include "platform/wtf/Vector.h" namespace blink {
diff --git a/third_party/WebKit/Source/core/layout/ng/ng_layout_opportunity_tree_node.cc b/third_party/WebKit/Source/core/layout/ng/ng_layout_opportunity_tree_node.cc index e7e9534..19524ffa 100644 --- a/third_party/WebKit/Source/core/layout/ng/ng_layout_opportunity_tree_node.cc +++ b/third_party/WebKit/Source/core/layout/ng/ng_layout_opportunity_tree_node.cc
@@ -4,6 +4,8 @@ #include "core/layout/ng/ng_layout_opportunity_tree_node.h" +#include "platform/wtf/text/WTFString.h" + namespace blink { NGLayoutOpportunityTreeNode::NGLayoutOpportunityTreeNode( @@ -28,4 +30,14 @@ : "null"); } +std::ostream& operator<<(std::ostream& stream, + const NGLayoutOpportunityTreeNode& value) { + return stream << value.ToString(); +} + +std::ostream& operator<<(std::ostream& out, + const NGLayoutOpportunityTreeNode* value) { + return out << (value ? value->ToString() : "(null)"); +} + } // namespace blink
diff --git a/third_party/WebKit/Source/core/layout/ng/ng_layout_opportunity_tree_node.h b/third_party/WebKit/Source/core/layout/ng/ng_layout_opportunity_tree_node.h index e0e4caed..028cc7e 100644 --- a/third_party/WebKit/Source/core/layout/ng/ng_layout_opportunity_tree_node.h +++ b/third_party/WebKit/Source/core/layout/ng/ng_layout_opportunity_tree_node.h
@@ -8,6 +8,7 @@ #include "core/layout/ng/geometry/ng_bfc_rect.h" #include "core/layout/ng/geometry/ng_edge.h" #include "core/layout/ng/ng_exclusion.h" +#include "platform/wtf/Vector.h" namespace blink { @@ -53,15 +54,8 @@ String ToString() const; }; -inline std::ostream& operator<<(std::ostream& stream, - const NGLayoutOpportunityTreeNode& value) { - return stream << value.ToString(); -} - -inline std::ostream& operator<<(std::ostream& out, - const NGLayoutOpportunityTreeNode* value) { - return out << (value ? value->ToString() : "(null)"); -} +std::ostream& operator<<(std::ostream&, const NGLayoutOpportunityTreeNode&); +std::ostream& operator<<(std::ostream&, const NGLayoutOpportunityTreeNode*); } // namespace blink #endif // NGLayoutOpportunityTreeNode_h
diff --git a/third_party/WebKit/Source/core/layout/ng/ng_length_utils.cc b/third_party/WebKit/Source/core/layout/ng/ng_length_utils.cc index 982b6b7..055b12b 100644 --- a/third_party/WebKit/Source/core/layout/ng/ng_length_utils.cc +++ b/third_party/WebKit/Source/core/layout/ng/ng_length_utils.cc
@@ -4,6 +4,7 @@ #include "core/layout/ng/ng_length_utils.h" +#include <algorithm> #include "core/layout/LayoutBox.h" #include "core/layout/ng/ng_constraint_space.h" #include "core/layout/ng/ng_constraint_space_builder.h" @@ -527,4 +528,21 @@ FromPlatformWritingMode(style->GetWritingMode()), style->Direction()); } +NGLogicalSize CalculateContentBoxSize( + const NGLogicalSize border_box_size, + const NGBoxStrut& border_scrollbar_padding) { + NGLogicalSize size = border_box_size; + size.inline_size -= border_scrollbar_padding.InlineSum(); + size.inline_size = std::max(size.inline_size, LayoutUnit()); + + // Our calculated block-axis size may still be indefinite. If so, just leave + // the size as NGSizeIndefinite instead of subtracting borders and padding. + if (size.block_size != NGSizeIndefinite) { + size.block_size -= border_scrollbar_padding.BlockSum(); + size.block_size = std::max(size.block_size, LayoutUnit()); + } + + return size; +} + } // namespace blink
diff --git a/third_party/WebKit/Source/core/layout/ng/ng_length_utils.h b/third_party/WebKit/Source/core/layout/ng/ng_length_utils.h index 9ee822f..637e2fa 100644 --- a/third_party/WebKit/Source/core/layout/ng/ng_length_utils.h +++ b/third_party/WebKit/Source/core/layout/ng/ng_length_utils.h
@@ -150,22 +150,9 @@ ComputeBlockSizeForFragment(constraint_space, style, block_content_size)); } -inline NGLogicalSize CalculateContentBoxSize( +NGLogicalSize CalculateContentBoxSize( const NGLogicalSize border_box_size, - const NGBoxStrut& border_scrollbar_padding) { - NGLogicalSize size = border_box_size; - size.inline_size -= border_scrollbar_padding.InlineSum(); - size.inline_size = std::max(size.inline_size, LayoutUnit()); - - // Our calculated block-axis size may still be indefinite. If so, just leave - // the size as NGSizeIndefinite instead of subtracting borders and padding. - if (size.block_size != NGSizeIndefinite) { - size.block_size -= border_scrollbar_padding.BlockSum(); - size.block_size = std::max(size.block_size, LayoutUnit()); - } - - return size; -} + const NGBoxStrut& border_scrollbar_padding); } // namespace blink
diff --git a/third_party/WebKit/Source/core/paint/BoxPaintInvalidator.cpp b/third_party/WebKit/Source/core/paint/BoxPaintInvalidator.cpp index 3307695..f5e59f8 100644 --- a/third_party/WebKit/Source/core/paint/BoxPaintInvalidator.cpp +++ b/third_party/WebKit/Source/core/paint/BoxPaintInvalidator.cpp
@@ -197,47 +197,41 @@ return false; } -void BoxPaintInvalidator::InvalidateScrollingContentsBackgroundIfNeeded() { - bool paints_onto_scrolling_contents_layer = - BackgroundPaintsOntoScrollingContentsLayer(); - if (!paints_onto_scrolling_contents_layer && - !BackgroundGeometryDependsOnLayoutOverflowRect()) - return; +BoxPaintInvalidator::BackgroundInvalidationType +BoxPaintInvalidator::ComputeBackgroundInvalidation() { + if (box_.BackgroundChangedSinceLastPaintInvalidation()) + return BackgroundInvalidationType::kFull; + + if (!BackgroundGeometryDependsOnLayoutOverflowRect()) + return BackgroundInvalidationType::kNone; const LayoutRect& old_layout_overflow = box_.PreviousLayoutOverflowRect(); LayoutRect new_layout_overflow = box_.LayoutOverflowRect(); - bool should_fully_invalidate_on_scrolling_contents_layer = false; - if (box_.BackgroundChangedSinceLastPaintInvalidation()) { - if (!paints_onto_scrolling_contents_layer) { - // The box should have been set needing full invalidation on style change. - DCHECK(box_.ShouldDoFullPaintInvalidation()); - return; - } - should_fully_invalidate_on_scrolling_contents_layer = true; - } else { - // Check change of layout overflow for full or incremental invalidation. - if (new_layout_overflow == old_layout_overflow) - return; - bool should_fully_invalidate = - ShouldFullyInvalidateBackgroundOnLayoutOverflowChange( - old_layout_overflow, new_layout_overflow); - if (!paints_onto_scrolling_contents_layer) { - if (should_fully_invalidate) { - box_.GetMutableForPainting() - .SetShouldDoFullPaintInvalidationWithoutGeometryChange( - PaintInvalidationReason::kBackground); - } - return; - } - should_fully_invalidate_on_scrolling_contents_layer = - should_fully_invalidate; + if (new_layout_overflow == old_layout_overflow) + return BackgroundInvalidationType::kNone; + + // Layout overflow changed; decide full vs. incremental invalidation. + if (ShouldFullyInvalidateBackgroundOnLayoutOverflowChange( + old_layout_overflow, new_layout_overflow)) { + return BackgroundInvalidationType::kFull; } + return BackgroundInvalidationType::kIncremental; +} + +void BoxPaintInvalidator::InvalidateScrollingContentsBackground( + BackgroundInvalidationType backgroundInvalidationType) { + if (!BackgroundPaintsOntoScrollingContentsLayer()) + return; + if (backgroundInvalidationType == BackgroundInvalidationType::kNone) + return; // TODO(crbug.com/732611): Implement raster invalidation of background on // scrolling contents layer for SPv2. if (!RuntimeEnabledFeatures::SlimmingPaintV2Enabled()) { - if (should_fully_invalidate_on_scrolling_contents_layer) { + const LayoutRect& old_layout_overflow = box_.PreviousLayoutOverflowRect(); + LayoutRect new_layout_overflow = box_.LayoutOverflowRect(); + if (backgroundInvalidationType == BackgroundInvalidationType::kFull) { ObjectPaintInvalidatorWithContext(box_, context_) .FullyInvalidatePaint( PaintInvalidationReason::kBackgroundOnScrollingContentsLayer, @@ -258,7 +252,15 @@ } PaintInvalidationReason BoxPaintInvalidator::InvalidatePaint() { - InvalidateScrollingContentsBackgroundIfNeeded(); + BackgroundInvalidationType backgroundInvalidationType = + ComputeBackgroundInvalidation(); + if (backgroundInvalidationType == BackgroundInvalidationType::kFull && + !BackgroundPaintsOntoScrollingContentsLayer()) { + box_.GetMutableForPainting() + .SetShouldDoFullPaintInvalidationWithoutGeometryChange( + PaintInvalidationReason::kBackground); + } + InvalidateScrollingContentsBackground(backgroundInvalidationType); PaintInvalidationReason reason = ComputePaintInvalidationReason(); if (reason == PaintInvalidationReason::kIncremental) {
diff --git a/third_party/WebKit/Source/core/paint/BoxPaintInvalidator.h b/third_party/WebKit/Source/core/paint/BoxPaintInvalidator.h index 26f4e8e2..f5092fc 100644 --- a/third_party/WebKit/Source/core/paint/BoxPaintInvalidator.h +++ b/third_party/WebKit/Source/core/paint/BoxPaintInvalidator.h
@@ -35,7 +35,10 @@ bool ShouldFullyInvalidateBackgroundOnLayoutOverflowChange( const LayoutRect& old_layout_overflow, const LayoutRect& new_layout_overflow); - void InvalidateScrollingContentsBackgroundIfNeeded(); + + enum BackgroundInvalidationType { kNone = 0, kIncremental, kFull }; + BackgroundInvalidationType ComputeBackgroundInvalidation(); + void InvalidateScrollingContentsBackground(BackgroundInvalidationType); PaintInvalidationReason ComputePaintInvalidationReason();
diff --git a/third_party/WebKit/Source/core/paint/ClipRect.cpp b/third_party/WebKit/Source/core/paint/ClipRect.cpp index bb0bd63..5b09a9b 100644 --- a/third_party/WebKit/Source/core/paint/ClipRect.cpp +++ b/third_party/WebKit/Source/core/paint/ClipRect.cpp
@@ -27,6 +27,7 @@ #include "core/paint/ClipRect.h" #include "core/layout/HitTestLocation.h" +#include "platform/wtf/text/WTFString.h" namespace blink { @@ -34,4 +35,8 @@ return hit_test_location.Intersects(rect_); } +String ClipRect::ToString() const { + return rect_.ToString(); +} + } // namespace blink
diff --git a/third_party/WebKit/Source/core/paint/ClipRect.h b/third_party/WebKit/Source/core/paint/ClipRect.h index 8f1a258..fd14dff6 100644 --- a/third_party/WebKit/Source/core/paint/ClipRect.h +++ b/third_party/WebKit/Source/core/paint/ClipRect.h
@@ -78,7 +78,7 @@ bool IsEmpty() const { return rect_.IsEmpty(); } bool Intersects(const HitTestLocation&) const; - String ToString() const { return rect_.ToString(); } + String ToString() const; private: LayoutRect rect_;
diff --git a/third_party/WebKit/Source/core/paint/compositing/CompositedLayerMapping.cpp b/third_party/WebKit/Source/core/paint/compositing/CompositedLayerMapping.cpp index dae870d..8c222c0 100644 --- a/third_party/WebKit/Source/core/paint/compositing/CompositedLayerMapping.cpp +++ b/third_party/WebKit/Source/core/paint/compositing/CompositedLayerMapping.cpp
@@ -508,16 +508,14 @@ } bool CompositedLayerMapping::AncestorRoundedCornersWillClip( - const FloatRect& child_rect_in_nearest_clipping_space, - const PaintLayer* nearest_clipping_ancestor, - const PaintLayer* compositing_ancestor) { - // Find all clips up to the ancestor compositing container to correctly - // handle nested clips. + const FloatRect& bounds_in_ancestor_space, + const PaintLayer* clip_inheritance_ancestor) { + // Collect all border-radius clips between us and the state we inherited. LayoutPoint zero_offset; Vector<FloatRoundedRect> rounded_rect_clips; LayerClipRecorder::CollectRoundedRectClips( - *nearest_clipping_ancestor, compositing_ancestor, zero_offset, true, - LayerClipRecorder::kIncludeSelfForBorderRadius, rounded_rect_clips); + owning_layer_, clip_inheritance_ancestor, zero_offset, true, + LayerClipRecorder::kDoNotIncludeSelfForBorderRadius, rounded_rect_clips); for (auto clip_rect : rounded_rect_clips) { FloatRect inner_clip_rect = clip_rect.RadiusCenterRect(); @@ -527,8 +525,8 @@ // entirely outside the rectangular border (ignoring rounded corners) so // is also unaffected by the rounded corners. In both cases the existing // rectangular clip is adequate and the mask is unnecessary. - if (!inner_clip_rect.Contains(child_rect_in_nearest_clipping_space) && - child_rect_in_nearest_clipping_space.Intersects(clip_rect.Rect())) { + if (!inner_clip_rect.Contains(bounds_in_ancestor_space) && + bounds_in_ancestor_space.Intersects(clip_rect.Rect())) { return true; } } @@ -546,45 +544,30 @@ if (!owning_layer_.Parent()) return; - const PaintLayer* compositing_ancestor = - owning_layer_.EnclosingLayerWithCompositedLayerMapping(kExcludeSelf); - if (!compositing_ancestor) - return; - - const LayoutBoxModelObject* clipping_container = - owning_layer_.ClippingContainer(); - if (!clipping_container) - return; - - if (clipping_container->EnclosingLayer() == scroll_parent) - return; - - if (compositing_ancestor->GetLayoutObject().IsDescendantOf( - clipping_container)) - return; - - // We ignore overflow clip here; we want composited overflow content to - // behave as if it lives in an unclipped universe so it can prepaint, etc. - // This means that we need to check if we are actually clipped before - // setting up m_ancestorClippingLayer otherwise - // updateAncestorClippingLayerGeometry will fail as the clip rect will be - // infinite. + // Compute the clips below the layer we inherit clip state from. + // Ignore the clips of the inherited layer, because they are already a part + // of the inherited state. // FIXME: this should use cached clip rects, but this sometimes give // inaccurate results (and trips the ASSERTS in PaintLayerClipper). - ClipRectsContext clip_rects_context(compositing_ancestor, kUncachedClipRects, + const PaintLayer* clip_inheritance_ancestor = ClipInheritanceAncestor( + owning_layer_.EnclosingLayerWithCompositedLayerMapping(kExcludeSelf)); + ClipRectsContext clip_rects_context(clip_inheritance_ancestor, + kUncachedClipRects, kIgnorePlatformOverlayScrollbarSize); clip_rects_context.SetIgnoreOverflowClip(); ClipRect clip_rect; owning_layer_.Clipper(PaintLayer::kDoNotUseGeometryMapper) .CalculateBackgroundClipRect(clip_rects_context, clip_rect); - IntRect parent_clip_rect = PixelSnappedIntRect(clip_rect.Rect()); - owning_layer_is_clipped = parent_clip_rect != LayoutRect::InfiniteIntRect(); + if (clip_rect.Rect() == LayoutRect(LayoutRect::InfiniteIntRect())) + return; + + owning_layer_is_clipped = true; // TODO(schenney): CSS clips are not applied to composited children, and // should be via mask or by compositing the parent too. // https://bugs.chromium.org/p/chromium/issues/detail?id=615870 - if (!(owning_layer_is_clipped && clip_rect.HasRadius())) + if (!clip_rect.HasRadius()) return; // If there are any rounded corners we must use a mask in the presence of @@ -595,24 +578,48 @@ return; } + // TODO(crbug.com/756265): Our composited in-flow descendants expect to + // inherit our clip, and we shouldn't omit rounded clip if some descendants + // could be clipped by it. FloatRect bounds_in_ancestor_space = GetLayoutObject() .LocalToAncestorQuad(FloatRect(composited_bounds_), - &compositing_ancestor->GetLayoutObject(), + &clip_inheritance_ancestor->GetLayoutObject(), kUseTransforms) .BoundingBox(); owning_layer_is_masked = AncestorRoundedCornersWillClip( - bounds_in_ancestor_space, clipping_container->Layer(), - compositing_ancestor); + bounds_in_ancestor_space, clip_inheritance_ancestor); } -const PaintLayer* CompositedLayerMapping::ScrollParent() { +const PaintLayer* CompositedLayerMapping::ScrollParent() const { const PaintLayer* scroll_parent = owning_layer_.ScrollParent(); if (scroll_parent && !scroll_parent->NeedsCompositedScrolling()) return nullptr; return scroll_parent; } +const PaintLayer* CompositedLayerMapping::CompositedClipParent() const { + const PaintLayer* clip_parent = owning_layer_.ClipParent(); + return clip_parent ? clip_parent->EnclosingLayerWithCompositedLayerMapping( + kIncludeSelf) + : nullptr; +} + +const PaintLayer* CompositedLayerMapping::ClipInheritanceAncestor( + const PaintLayer* compositing_container) const { + // Determine the clip state we are going to inherit. + // There are three sources a layer inherits its clip state from, in the + // order of priority (see cc/trees/property_tree_builder.cc): + // 1. Clip parent + // 2. Scroll parent + // 3. Parent layer + if (const PaintLayer* clip_parent = CompositedClipParent()) + return clip_parent; + if (const PaintLayer* scroll_parent = ScrollParent()) + return scroll_parent; + return compositing_container; +} + bool CompositedLayerMapping::UpdateGraphicsLayerConfiguration() { DCHECK_EQ(owning_layer_.Compositor()->Lifecycle().GetState(), DocumentLifecycle::kInCompositingUpdate); @@ -1271,28 +1278,65 @@ if (!compositing_container || !ancestor_clipping_layer_) return; - ClipRectsContext clip_rects_context(compositing_container, + const PaintLayer* clip_inheritance_ancestor = + ClipInheritanceAncestor(compositing_container); + ClipRectsContext clip_rects_context(clip_inheritance_ancestor, kPaintingClipRectsIgnoringOverflowClip, kIgnorePlatformOverlayScrollbarSize); + // Note: kPaintingClipRectsIgnoringOverflowClip implies SetIgnoreOverflowClip. - ClipRect parent_clip_rect; + ClipRect clip_rect; owning_layer_.Clipper(PaintLayer::kDoNotUseGeometryMapper) - .CalculateBackgroundClipRect(clip_rects_context, parent_clip_rect); + .CalculateBackgroundClipRect(clip_rects_context, clip_rect); + DCHECK(clip_rect.Rect() != LayoutRect(LayoutRect::InfiniteIntRect())); - IntRect snapped_parent_clip_rect( - PixelSnappedIntRect(parent_clip_rect.Rect())); + // The accumulated clip rect is in the space of clip_inheritance_ancestor. + // It needs to be converted to the space of our compositing container because + // our layer position is based on that. + LayoutRect clip_rect_in_compositing_container_space = clip_rect.Rect(); + // The following two branches are doing exact the same conversion, but + // ConvertToLayerCoords can only handle descendant-to-ancestor conversion. + // Inversion needs to be done manually if clip_inheritance_container is not + // a descendant of compositing_container. + if (clip_inheritance_ancestor == compositing_container) { + // No needs to convert. + } else if (clip_inheritance_ancestor == ScrollParent()) { + // Having a scroll parent implies that the inherited clip is a sibling to + // us in paint order, thus our compositing container must be an ancestor + // of the scroll parent. + // See CompositingInputsUpdater::UpdateRecursive(). + DCHECK(clip_inheritance_ancestor->GetLayoutObject().IsDescendantOf( + &compositing_container->GetLayoutObject())); + clip_inheritance_ancestor->ConvertToLayerCoords( + compositing_container, clip_rect_in_compositing_container_space); + } else { + // Inherits from clip parent. The clip parent is set only when we need to + // escape some clip that was applied to our compositing container. As such, + // the clip parent must be some ancestor of our compositing container. + DCHECK(compositing_container->GetLayoutObject().IsDescendantOf( + &clip_inheritance_ancestor->GetLayoutObject())); + LayoutPoint compositing_container_origin_in_clip_ancestor_space; + compositing_container->ConvertToLayerCoords( + clip_inheritance_ancestor, + compositing_container_origin_in_clip_ancestor_space); + clip_rect_in_compositing_container_space.MoveBy( + -compositing_container_origin_in_clip_ancestor_space); + } + clip_rect_in_compositing_container_space.Move( + compositing_container->SubpixelAccumulation()); - DCHECK(snapped_parent_clip_rect != LayoutRect::InfiniteIntRect()); + IntRect snapped_clip_rect = + PixelSnappedIntRect(clip_rect_in_compositing_container_space); + ancestor_clipping_layer_->SetPosition(FloatPoint( - snapped_parent_clip_rect.Location() - graphics_layer_parent_location)); - ancestor_clipping_layer_->SetSize(FloatSize(snapped_parent_clip_rect.Size())); + snapped_clip_rect.Location() - graphics_layer_parent_location)); + ancestor_clipping_layer_->SetSize(FloatSize(snapped_clip_rect.Size())); // backgroundRect is relative to compositingContainer, so subtract // snappedOffsetFromCompositedAncestor.X/snappedOffsetFromCompositedAncestor.Y // to get back to local coords. ancestor_clipping_layer_->SetOffsetFromLayoutObject( - snapped_parent_clip_rect.Location() - - snapped_offset_from_composited_ancestor); + snapped_clip_rect.Location() - snapped_offset_from_composited_ancestor); if (ancestor_clipping_mask_layer_) { ancestor_clipping_mask_layer_->SetOffsetFromLayoutObject( @@ -1303,7 +1347,7 @@ // The primary layer is then parented in, and positioned relative to this // clipping layer. - graphics_layer_parent_location = snapped_parent_clip_rect.Location(); + graphics_layer_parent_location = snapped_clip_rect.Location(); } void CompositedLayerMapping::UpdateOverflowControlsHostLayerGeometry( @@ -2461,11 +2505,7 @@ } void CompositedLayerMapping::UpdateClipParent(const PaintLayer* scroll_parent) { - const PaintLayer* clip_parent = owning_layer_.ClipParent(); - if (clip_parent) { - clip_parent = - clip_parent->EnclosingLayerWithCompositedLayerMapping(kIncludeSelf); - } + const PaintLayer* clip_parent = CompositedClipParent(); if (ScrollingCoordinator* scrolling_coordinator = owning_layer_.GetScrollingCoordinator()) {
diff --git a/third_party/WebKit/Source/core/paint/compositing/CompositedLayerMapping.h b/third_party/WebKit/Source/core/paint/compositing/CompositedLayerMapping.h index 4243f90..d92ab80f 100644 --- a/third_party/WebKit/Source/core/paint/compositing/CompositedLayerMapping.h +++ b/third_party/WebKit/Source/core/paint/compositing/CompositedLayerMapping.h
@@ -494,35 +494,37 @@ const GraphicsLayerPaintInfo&, const Vector<GraphicsLayerPaintInfo>& layers); - // Conservatively check that a sequence of border-radius clips do not clip - // this layer. The rectangle to check for clipping is the child's layer - // bound in the nearest clipping ancestor's space. The - // nearest_clipping_ancestor is the place where we need to start the search - // for border radius clips. The compositing_ancestor is the nearest - // compositing ancestor layer and we can stop checking clips at that layer - // because higher layer clips will be applied elsewhere. - // This is a fast approximate test. Depending on the shape of the child and - // the size of the clips, this method may return true when in fact - // the child is not clipped. We accept the approximation because most border - // radii are small and the outcome is used to reduce the number of layers, - // not influence correctness. + // Conservatively check whether there exists any border-radius clip that + // must be applied by an ancestor clipping mask layer. There are two inputs + // to this function: the bounds of contents that are going to be clipped + // by ancestor clipping layer, and the compositing ancestor which we are + // going to inherit clip state from. + // The function works by collecting all border-radius clips between the + // current layer and the inherited clip, i.e. those are the clips that are + // going to be applied by the ancestor clipping mask layer. A fast + // approximation test is used to determine whether the contents exceed + // the bounds of any of the clips. The function may return false positive + // (apply mask layer when not strictly needed), but never false negative, + // as its purpose is only for optimization. bool AncestorRoundedCornersWillClip( - const FloatRect& child_rect_in_nearest_clipping_space, - const PaintLayer* nearest_clipping_ancestor, - const PaintLayer* compositing_ancestor); + const FloatRect& bounds_in_ancestor_space, + const PaintLayer* clip_inheritance_ancestor); - // Return true in |owningLayerIsClipped| iff |m_owningLayer|'s compositing - // ancestor is not a descendant (inclusive) of the clipping container for - // |m_owningLayer|. Return true in |owningLayerIsMasked| iff - // |owningLayerIsClipped| is true and |m_owningLayer|'s compositing ancestor - // is not a descendant (inclusive) of a container that applies a mask for - // |m_owningLayer|. + // Return true in |owningLayerIsClipped| iff there is any clip in between + // the current layer and the inherited clip state. The inherited clip state + // is determined by the interoperation between compositing container, clip + // parent, and scroll parent. + // Return true in |owningLayerIsMasked| iff |owningLayerIsClipped| is true + // and any of the clip needs to be applied as a painted mask. void OwningLayerClippedOrMaskedByLayerNotAboveCompositedAncestor( const PaintLayer* scroll_parent, bool& owning_layer_is_clipped, bool& owning_layer_is_masked); - const PaintLayer* ScrollParent(); + const PaintLayer* ScrollParent() const; + const PaintLayer* CompositedClipParent() const; + const PaintLayer* ClipInheritanceAncestor( + const PaintLayer* compositing_container) const; // Clear the groupedMapping entry on the layer at the given index, only if // that layer does not appear earlier in the set of layers for this object.
diff --git a/third_party/WebKit/Source/core/paint/compositing/CompositingInputsUpdater.cpp b/third_party/WebKit/Source/core/paint/compositing/CompositingInputsUpdater.cpp index b9e592fd..dd517c3d 100644 --- a/third_party/WebKit/Source/core/paint/compositing/CompositingInputsUpdater.cpp +++ b/third_party/WebKit/Source/core/paint/compositing/CompositingInputsUpdater.cpp
@@ -71,29 +71,17 @@ return nullptr; } -static bool HasClippedStackingAncestor(const PaintLayer* layer, - const PaintLayer* clipping_layer) { - if (layer == clipping_layer) +static bool NeedsToEscapeClipInheritedFromCompositingContainer( + const PaintLayer* layer, + const LayoutObject& desired_clip) { + const PaintLayer* compositing_parent = layer->CompositingContainer(); + DCHECK(compositing_parent); + const LayoutObject* inherited_clip = &compositing_parent->GetLayoutObject(); + if (!inherited_clip->HasClipRelatedProperty()) + inherited_clip = compositing_parent->ClippingContainer(); + if (!inherited_clip) return false; - bool found_intervening_clip = false; - const LayoutObject& clipping_layout_object = - clipping_layer->GetLayoutObject(); - for (const PaintLayer* current = layer->CompositingContainer(); current; - current = current->CompositingContainer()) { - if (current == clipping_layer) - return found_intervening_clip; - - if (current->GetLayoutObject().HasClipRelatedProperty() && - !clipping_layout_object.IsDescendantOf(¤t->GetLayoutObject())) - found_intervening_clip = true; - - if (const LayoutObject* container = current->ClippingContainer()) { - if (&clipping_layout_object != container && - !clipping_layout_object.IsDescendantOf(container)) - found_intervening_clip = true; - } - } - return false; + return !desired_clip.IsDescendantOf(inherited_clip); } void CompositingInputsUpdater::UpdateRecursive(PaintLayer* layer, @@ -200,6 +188,8 @@ layer_is_fixed_position ? layer : parent->NearestFixedPositionLayer(); if (info.has_ancestor_with_clip_related_property) { + // This is the ancestor that is |layer|'s containing block, or has a + // CSS clip, which ever is the closest. const PaintLayer* parent_layer_on_clipping_container_chain = FindParentLayerOnClippingContainerChain(layer); const bool parent_has_clip_related_property = @@ -210,22 +200,26 @@ ? &parent_layer_on_clipping_container_chain->GetLayoutObject() : parent_layer_on_clipping_container_chain->ClippingContainer(); - const PaintLayer* clipping_layer = - properties.clipping_container - ? properties.clipping_container->EnclosingLayer() - : layer->Compositor()->RootLayer(); - if (!layer->SubtreeIsInvisible()) { - if (layer->GetLayoutObject().IsOutOfFlowPositioned()) { - if (HasClippedStackingAncestor(layer, clipping_layer)) - properties.clip_parent = clipping_layer; - } else { - if (clipping_layer && clipping_layer->CompositingContainer() == - layer->CompositingContainer()) { - // If the clipping container of |layer| is a sibling in the - // stacking tree, and it escapes a stacking ancestor clip, - // this layer should escape that clip also. - properties.clip_parent = clipping_layer->ClipParent(); + if (layer->GetLayoutObject().IsOutOfFlowPositioned() && + NeedsToEscapeClipInheritedFromCompositingContainer( + layer, parent_layer_on_clipping_container_chain + ->GetLayoutObject())) { + properties.clip_parent = parent_layer_on_clipping_container_chain; + } else if (parent_layer_on_clipping_container_chain + ->CompositingContainer() == + layer->CompositingContainer()) { + // If the clipping container of |layer| is a sibling in the + // stacking tree, and it escapes a stacking ancestor clip, + // this layer should escape that clip also. + if (parent_layer_on_clipping_container_chain->ClipParent()) { + // It may be attempting to inherit the clip state of + // parent_layer_on_clipping_container_chain directly, but our + // paint order can be before the clipping parent due to negative + // z-index. Our compositor implementation currently only allow + // inheriting clip from layer that paints before us. + properties.clip_parent = + parent_layer_on_clipping_container_chain->ClipParent(); } } }
diff --git a/third_party/WebKit/Source/platform/BUILD.gn b/third_party/WebKit/Source/platform/BUILD.gn index b0f9b72..2a96530 100644 --- a/third_party/WebKit/Source/platform/BUILD.gn +++ b/third_party/WebKit/Source/platform/BUILD.gn
@@ -1809,6 +1809,7 @@ "graphics/compositing/ContentLayerClientImplTest.cpp", "graphics/compositing/PaintArtifactCompositorTest.cpp", "graphics/compositing/PaintChunksToCcLayerTest.cpp", + "graphics/filters/FECompositeTest.cpp", "graphics/filters/ImageFilterBuilderTest.cpp", "graphics/gpu/DrawingBufferTest.cpp", "graphics/gpu/SharedGpuContextTest.cpp",
diff --git a/third_party/WebKit/Source/platform/CalculationValue.h b/third_party/WebKit/Source/platform/CalculationValue.h index c641246..57c7d77 100644 --- a/third_party/WebKit/Source/platform/CalculationValue.h +++ b/third_party/WebKit/Source/platform/CalculationValue.h
@@ -33,7 +33,9 @@ #include "platform/Length.h" #include "platform/LengthFunctions.h" +#include "platform/wtf/PassRefPtr.h" #include "platform/wtf/RefCounted.h" +#include "platform/wtf/RefPtr.h" namespace blink {
diff --git a/third_party/WebKit/Source/platform/LayoutUnit.cpp b/third_party/WebKit/Source/platform/LayoutUnit.cpp index 3c29ca72..70e752d 100644 --- a/third_party/WebKit/Source/platform/LayoutUnit.cpp +++ b/third_party/WebKit/Source/platform/LayoutUnit.cpp
@@ -4,6 +4,7 @@ #include "platform/LayoutUnit.h" +#include <ostream> #include "platform/wtf/text/WTFString.h" namespace blink { @@ -20,4 +21,8 @@ return String::Number(ToDouble()); } +std::ostream& operator<<(std::ostream& stream, const LayoutUnit& value) { + return stream << value.ToString(); +} + } // namespace blink
diff --git a/third_party/WebKit/Source/platform/LayoutUnit.h b/third_party/WebKit/Source/platform/LayoutUnit.h index 344e5f6..50d0677 100644 --- a/third_party/WebKit/Source/platform/LayoutUnit.h +++ b/third_party/WebKit/Source/platform/LayoutUnit.h
@@ -31,17 +31,14 @@ #ifndef LayoutUnit_h #define LayoutUnit_h -#include <limits.h> -#include <math.h> -#include <stdlib.h> -#include <algorithm> +#include <iosfwd> #include <limits> #include "base/numerics/safe_conversions.h" #include "platform/PlatformExport.h" #include "platform/wtf/Allocator.h" #include "platform/wtf/Assertions.h" +#include "platform/wtf/Forward.h" #include "platform/wtf/SaturatedArithmetic.h" -#include "platform/wtf/text/WTFString.h" namespace blink { @@ -166,11 +163,11 @@ } LayoutUnit ClampNegativeToZero() const { - return std::max(*this, LayoutUnit()); + return value_ < 0 ? LayoutUnit() : *this; } LayoutUnit ClampPositiveToZero() const { - return std::min(*this, LayoutUnit()); + return value_ > 0 ? LayoutUnit() : *this; } LayoutUnit Fraction() const { @@ -707,9 +704,7 @@ return value.ToInt() == value; } -inline std::ostream& operator<<(std::ostream& stream, const LayoutUnit& value) { - return stream << value.ToString(); -} +PLATFORM_EXPORT std::ostream& operator<<(std::ostream&, const LayoutUnit&); } // namespace blink
diff --git a/third_party/WebKit/Source/platform/Length.cpp b/third_party/WebKit/Source/platform/Length.cpp index be1ca46..35b8c24c 100644 --- a/third_party/WebKit/Source/platform/Length.cpp +++ b/third_party/WebKit/Source/platform/Length.cpp
@@ -27,6 +27,7 @@ #include "platform/CalculationValue.h" #include "platform/animation/AnimationUtilities.h" +#include "platform/wtf/HashMap.h" namespace blink {
diff --git a/third_party/WebKit/Source/platform/graphics/filters/FEComposite.cpp b/third_party/WebKit/Source/platform/graphics/filters/FEComposite.cpp index b034c46..e85fd49 100644 --- a/third_party/WebKit/Source/platform/graphics/filters/FEComposite.cpp +++ b/third_party/WebKit/Source/platform/graphics/filters/FEComposite.cpp
@@ -113,42 +113,51 @@ } FloatRect FEComposite::MapInputs(const FloatRect& rect) const { - FloatRect input1_rect = InputEffect(1)->MapRect(rect); + FloatRect i1 = InputEffect(0)->MapRect(rect); + FloatRect i2 = InputEffect(1)->MapRect(rect); switch (type_) { case FECOMPOSITE_OPERATOR_IN: // 'in' has output only in the intersection of both inputs. - return Intersection(input1_rect, InputEffect(0)->MapRect(input1_rect)); + return Intersection(i1, i2); case FECOMPOSITE_OPERATOR_ATOP: // 'atop' has output only in the extents of the second input. - return input1_rect; + return i2; case FECOMPOSITE_OPERATOR_ARITHMETIC: // result(i1,i2) = k1*i1*i2 + k2*i1 + k3*i2 + k4 // // (The below is not a complete breakdown of cases.) // - // Arithmetic with non-zero k4 may influence the complete filter primitive - // region. [k4 > 0 => result(0,0) = k4 => result(a,b) >= k4] + // Arithmetic with positive k4 may influence the complete filter primitive + // region. [k4 > 0 => result(0,0) = k4 => result(i1,i2) >= k4] + // Fall through to use union. If this effect clips to bounds, + // ApplyBounds() will return AbsoluteBounds() regardless of the return + // value of this function because AffectsTransparentPixels() is true. if (K4() > 0) - return rect; - // Additionally, if k2 = 0, input 0 can only appear where input 1 also - // appears. [k2 = k4 = 0 => result(a,b) = k1*a*b + k3*b = (k1*a + k3)*b] - // Hence for k2 > 0, both inputs can still appear. (Except if k3 = 0.) - if (K2() <= 0) { - // If k3 > 0, output can be produced wherever input 1 is - // non-transparent. - if (K3() > 0) - return input1_rect; - // If just k1 is positive, output will only be produce where both - // inputs are non-transparent. Use intersection. - // [k1 >= 0 and k2 = k3 = k4 = 0 => result(a,b) = k1 * a * b] - return Intersection(input1_rect, InputEffect(0)->MapRect(input1_rect)); - } - // else fall through to use union + break; + // If both K2 or K3 are positive, both i1 and i2 appear. Fall through to + // use union. + if (K2() > 0 && K3() > 0) + break; + // If k2 > 0, output can be produced whenever i1 is non-transparent. + // [k3 = k4 = 0 => result(i1,i2) = k1*i1*i2 + k2*i1 = (k1*i2 + k2)*i1] + if (K2() > 0) + return i1; + // If k3 > 0, output can be produced whenever i2 is non-transparent. + // [k2 = k4 = 0 => result(i1,i2) = k1*i1*i2 + k3*i2 = (k1*i1 + k3)*i2] + if (K3() > 0) + return i2; + // If just k1 is positive, output will only be produce where both inputs + // are non-transparent. Use intersection. + // [k1 > 0 and k2 = k3 = k4 = 0 => result(i1,i2) = k1*i1*i2] + if (K1() > 0) + return Intersection(i1, i2); + // [k1 = k2 = k3 = k4 = 0 => result(i1,i2) = 0] + return FloatRect(); default: break; } // Take the union of both input effects. - return UnionRect(input1_rect, InputEffect(0)->MapRect(rect)); + return UnionRect(i1, i2); } SkBlendMode ToBlendMode(CompositeOperationType mode) {
diff --git a/third_party/WebKit/Source/platform/graphics/filters/FECompositeTest.cpp b/third_party/WebKit/Source/platform/graphics/filters/FECompositeTest.cpp new file mode 100644 index 0000000..dcea59d --- /dev/null +++ b/third_party/WebKit/Source/platform/graphics/filters/FECompositeTest.cpp
@@ -0,0 +1,133 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "platform/graphics/filters/FEComposite.h" + +#include "platform/graphics/filters/FEOffset.h" +#include "platform/graphics/filters/Filter.h" +#include "platform/graphics/filters/SourceGraphic.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace blink { + +class FECompositeTest : public ::testing::Test { + protected: + FEComposite* CreateComposite(CompositeOperationType type, + float k1 = 0, + float k2 = 0, + float k3 = 0, + float k4 = 0) { + // Use big filter region to avoid it from affecting FEComposite's MapRect + // results. + FloatRect filter_region(-10000, -10000, 20000, 20000); + auto* filter = + Filter::Create(FloatRect(), filter_region, 1, Filter::kUserSpace); + + // Input 1 of composite has a fixed output rect. + auto* source_graphic1 = SourceGraphic::Create(filter); + source_graphic1->SetClipsToBounds(false); + source_graphic1->SetSourceRect(kInput1Rect); + + // Input 2 of composite will pass composite->MapRect()'s parameter as its + // output. + auto* source_graphic2 = SourceGraphic::Create(filter); + source_graphic2->SetClipsToBounds(false); + + // Composite input 1 and input 2. + auto* composite = FEComposite::Create(filter, type, k1, k2, k3, k4); + composite->SetClipsToBounds(false); + composite->InputEffects().push_back(source_graphic1); + composite->InputEffects().push_back(source_graphic2); + return composite; + } + + const IntRect kInput1Rect = {50, -50, 100, 100}; +}; + +#define EXPECT_INTERSECTION(c) \ + do { \ + EXPECT_TRUE(c->MapRect(FloatRect()).IsEmpty()); \ + EXPECT_TRUE(c->MapRect(FloatRect(0, 0, 50, 50)).IsEmpty()); \ + EXPECT_EQ(FloatRect(50, 0, 100, 50), \ + c->MapRect(FloatRect(0, 0, 200, 200))); \ + } while (false) + +#define EXPECT_INPUT1(c) \ + do { \ + EXPECT_EQ(FloatRect(kInput1Rect), c->MapRect(FloatRect())); \ + EXPECT_EQ(FloatRect(kInput1Rect), c->MapRect(FloatRect(0, 0, 50, 50))); \ + EXPECT_EQ(FloatRect(kInput1Rect), c->MapRect(FloatRect(0, 0, 200, 200))); \ + } while (false) + +#define EXPECT_INPUT2(c) \ + do { \ + EXPECT_TRUE(c->MapRect(FloatRect()).IsEmpty()); \ + EXPECT_EQ(FloatRect(0, 0, 50, 50), c->MapRect(FloatRect(0, 0, 50, 50))); \ + EXPECT_EQ(FloatRect(0, 0, 200, 200), \ + c->MapRect(FloatRect(0, 0, 200, 200))); \ + } while (false) + +#define EXPECT_UNION(c) \ + do { \ + EXPECT_EQ(FloatRect(kInput1Rect), c->MapRect(FloatRect())); \ + EXPECT_EQ(FloatRect(0, -50, 150, 100), \ + c->MapRect(FloatRect(0, 0, 50, 50))); \ + EXPECT_EQ(FloatRect(0, -50, 200, 250), \ + c->MapRect(FloatRect(0, 0, 200, 200))); \ + } while (false) + +#define EXPECT_EMPTY(c) \ + do { \ + EXPECT_TRUE(c->MapRect(FloatRect()).IsEmpty()); \ + EXPECT_TRUE(c->MapRect(FloatRect(0, 0, 50, 50)).IsEmpty()); \ + EXPECT_TRUE(c->MapRect(FloatRect(0, 0, 200, 200)).IsEmpty()); \ + } while (false) + +TEST_F(FECompositeTest, MapRectIn) { + EXPECT_INTERSECTION(CreateComposite(FECOMPOSITE_OPERATOR_IN)); +} + +TEST_F(FECompositeTest, MapRectATop) { + EXPECT_INPUT2(CreateComposite(FECOMPOSITE_OPERATOR_ATOP)); +} + +TEST_F(FECompositeTest, MapRectOtherOperators) { + EXPECT_UNION(CreateComposite(FECOMPOSITE_OPERATOR_OVER)); + EXPECT_UNION(CreateComposite(FECOMPOSITE_OPERATOR_OUT)); + EXPECT_UNION(CreateComposite(FECOMPOSITE_OPERATOR_XOR)); + EXPECT_UNION(CreateComposite(FECOMPOSITE_OPERATOR_LIGHTER)); +} + +TEST_F(FECompositeTest, MapRectArithmetic) { + EXPECT_EMPTY(CreateComposite(FECOMPOSITE_OPERATOR_ARITHMETIC, 0, 0, 0, 0)); + EXPECT_UNION(CreateComposite(FECOMPOSITE_OPERATOR_ARITHMETIC, 0, 0, 0, 1)); + EXPECT_INPUT2(CreateComposite(FECOMPOSITE_OPERATOR_ARITHMETIC, 0, 0, 1, 0)); + EXPECT_UNION(CreateComposite(FECOMPOSITE_OPERATOR_ARITHMETIC, 0, 0, 1, 1)); + EXPECT_INPUT1(CreateComposite(FECOMPOSITE_OPERATOR_ARITHMETIC, 0, 1, 0, 0)); + EXPECT_UNION(CreateComposite(FECOMPOSITE_OPERATOR_ARITHMETIC, 0, 1, 0, 1)); + EXPECT_UNION(CreateComposite(FECOMPOSITE_OPERATOR_ARITHMETIC, 0, 1, 1, 0)); + EXPECT_UNION(CreateComposite(FECOMPOSITE_OPERATOR_ARITHMETIC, 0, 1, 1, 1)); + EXPECT_INTERSECTION( + CreateComposite(FECOMPOSITE_OPERATOR_ARITHMETIC, 1, 0, 0, 0)); + EXPECT_UNION(CreateComposite(FECOMPOSITE_OPERATOR_ARITHMETIC, 1, 0, 0, 1)); + EXPECT_INPUT2(CreateComposite(FECOMPOSITE_OPERATOR_ARITHMETIC, 1, 0, 1, 0)); + EXPECT_UNION(CreateComposite(FECOMPOSITE_OPERATOR_ARITHMETIC, 1, 0, 1, 1)); + EXPECT_INPUT1(CreateComposite(FECOMPOSITE_OPERATOR_ARITHMETIC, 1, 1, 0, 0)); + EXPECT_UNION(CreateComposite(FECOMPOSITE_OPERATOR_ARITHMETIC, 1, 1, 0, 1)); + EXPECT_UNION(CreateComposite(FECOMPOSITE_OPERATOR_ARITHMETIC, 1, 1, 1, 0)); + EXPECT_UNION(CreateComposite(FECOMPOSITE_OPERATOR_ARITHMETIC, 1, 1, 1, 1)); +} + +TEST_F(FECompositeTest, MapRectArithmeticK4Clipped) { + // Arithmetic operator with positive K4 will always affect the whole primitive + // subregion. + auto* c = CreateComposite(FECOMPOSITE_OPERATOR_ARITHMETIC, 1, 1, 1, 1); + c->SetClipsToBounds(true); + FloatRect bounds(222, 333, 444, 555); + c->SetFilterPrimitiveSubregion(bounds); + EXPECT_EQ(bounds, c->MapRect(FloatRect())); + EXPECT_EQ(bounds, c->MapRect(FloatRect(100, 200, 300, 400))); +} + +} // namespace blink
diff --git a/third_party/WebKit/Source/platform/loader/BUILD.gn b/third_party/WebKit/Source/platform/loader/BUILD.gn index b7c1c22..3d9deaa 100644 --- a/third_party/WebKit/Source/platform/loader/BUILD.gn +++ b/third_party/WebKit/Source/platform/loader/BUILD.gn
@@ -97,6 +97,8 @@ "//components/link_header_util:link_header_util", "//mojo/public/cpp/system:system", "//storage/public/interfaces:interfaces_blink__generator", + "//third_party/WebKit/Source/platform:make_platform_generated", + "//third_party/WebKit/public:mojo_bindings_blink", ] public_deps = [
diff --git a/third_party/WebKit/Source/platform/testing/GeometryPrinters.cpp b/third_party/WebKit/Source/platform/testing/GeometryPrinters.cpp index 64c44d88..92f3efc 100644 --- a/third_party/WebKit/Source/platform/testing/GeometryPrinters.cpp +++ b/third_party/WebKit/Source/platform/testing/GeometryPrinters.cpp
@@ -19,6 +19,7 @@ #include "platform/geometry/LayoutPoint.h" #include "platform/geometry/LayoutRect.h" #include "platform/geometry/LayoutSize.h" +#include "platform/wtf/text/WTFString.h" #include <ostream> // NOLINT namespace blink {
diff --git a/third_party/WebKit/Tools/Scripts/webkitpy/common/net/git_cl.py b/third_party/WebKit/Tools/Scripts/webkitpy/common/net/git_cl.py index 729d1d6..bebdc8c 100644 --- a/third_party/WebKit/Tools/Scripts/webkitpy/common/net/git_cl.py +++ b/third_party/WebKit/Tools/Scripts/webkitpy/common/net/git_cl.py
@@ -82,7 +82,7 @@ self._host.sleep(poll_delay_seconds) try_results = self.try_job_results() _log.debug('Fetched try results: %s', try_results) - if self.all_finished(try_results): + if try_results and self.all_finished(try_results): self._host.print_('All jobs finished.') return try_results self._host.print_('Waiting. %d seconds passed.' % (self._host.time() - start))
diff --git a/third_party/WebKit/Tools/Scripts/webkitpy/common/net/git_cl_unittest.py b/third_party/WebKit/Tools/Scripts/webkitpy/common/net/git_cl_unittest.py index f5b81c9..2343bf794 100644 --- a/third_party/WebKit/Tools/Scripts/webkitpy/common/net/git_cl_unittest.py +++ b/third_party/WebKit/Tools/Scripts/webkitpy/common/net/git_cl_unittest.py
@@ -72,6 +72,22 @@ 'Waiting. 6600 seconds passed.\n' 'Timed out waiting for try results.\n') + def test_wait_for_try_jobs_no_results_not_considered_finished(self): + host = MockHost() + git_cl = GitCL(host) + git_cl.fetch_raw_try_job_results = lambda: [] + git_cl.wait_for_try_jobs() + self.assertEqual( + host.stdout.getvalue(), + 'Waiting for try jobs (timeout: 7200 seconds).\n' + 'Waiting. 600 seconds passed.\n' + 'Waiting. 1800 seconds passed.\n' + 'Waiting. 3000 seconds passed.\n' + 'Waiting. 4200 seconds passed.\n' + 'Waiting. 5400 seconds passed.\n' + 'Waiting. 6600 seconds passed.\n' + 'Timed out waiting for try results.\n') + def test_wait_for_try_jobs_done(self): host = MockHost() git_cl = GitCL(host)
diff --git a/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/run_webkit_tests.py b/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/run_webkit_tests.py index 981faf04..6a4788f 100644 --- a/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/run_webkit_tests.py +++ b/third_party/WebKit/Tools/Scripts/webkitpy/layout_tests/run_webkit_tests.py
@@ -480,7 +480,10 @@ help='If specified, upload results json files to this appengine server.'), ])) - option_parser = optparse.OptionParser() + option_parser = optparse.OptionParser( + prog='run-webkit-tests', + usage='%prog [options] [tests]', + description='Runs Blink layout tests as described in docs/testing/layout_tests.md') for group_name, group_options in option_group_definitions: option_group = optparse.OptionGroup(option_parser, group_name)
diff --git a/third_party/WebKit/public/BUILD.gn b/third_party/WebKit/public/BUILD.gn index 49bb879..a06e301 100644 --- a/third_party/WebKit/public/BUILD.gn +++ b/third_party/WebKit/public/BUILD.gn
@@ -768,6 +768,7 @@ "//content/common:mojo_bindings_blink", "//chrome/common:mojo_bindings_blink", "//third_party/WebKit/Source/platform", + "//third_party/WebKit/Source/platform/loader", ] sources = [ "platform/display_mode.mojom",
diff --git a/tools/mb/mb_config.pyl b/tools/mb/mb_config.pyl index 38dda18..33b1c42 100644 --- a/tools/mb/mb_config.pyl +++ b/tools/mb/mb_config.pyl
@@ -520,6 +520,7 @@ 'tryserver.chromium.angle': { 'android_angle_rel_ng': 'gpu_tests_deqp_android_release_trybot_arm64', + 'android_angle_deqp_rel_ng': 'gpu_tests_deqp_android_release_trybot_arm64', 'linux_angle_chromeos_rel_ng': 'gpu_fyi_tests_chromeos_cros_release_trybot', 'linux_angle_dbg_ng': 'gpu_fyi_tests_debug_trybot', 'linux_angle_deqp_rel_ng': 'gpu_fyi_tests_release_trybot',
diff --git a/tools/metrics/histograms/histograms.xml b/tools/metrics/histograms/histograms.xml index 8ee4c35..0bd5fca 100644 --- a/tools/metrics/histograms/histograms.xml +++ b/tools/metrics/histograms/histograms.xml
@@ -24543,13 +24543,23 @@ </summary> </histogram> +<histogram name="GoogleUpdate.InfoBar.DeviceFreeSpace" units="MB"> + <owner>shaktisahu@chromium.org</owner> + <summary> + (Android-only) The amount of internal memory storage that is free on the + file system and available to the applications when the InfoBar or update + menu item is shown. + </summary> +</histogram> + <histogram name="GoogleUpdate.InfoBar.InternalStorageSizeAvailable" units="MB"> <owner>yfriedman@chromium.org</owner> <owner>dfalcantara@chromium.org</owner> <owner>khushalsagar@chromium.org</owner> <summary> (Android-only) The amount of internal memory storage available on the user's - device when the InfoBar or update menu item is shown. + device when the InfoBar or update menu item is shown. Deprecating soon in + M64. </summary> </histogram>
diff --git a/ui/app_list/views/suggestions_container_view.cc b/ui/app_list/views/suggestions_container_view.cc index ee04a7a..c86ac21 100644 --- a/ui/app_list/views/suggestions_container_view.cc +++ b/ui/app_list/views/suggestions_container_view.cc
@@ -133,7 +133,8 @@ void SuggestionsContainerView::CreateAppsGrid(int apps_num) { DCHECK(search_result_tile_views_.empty()); - views::GridLayout* tiles_layout_manager = new views::GridLayout(this); + views::GridLayout* tiles_layout_manager = + views::GridLayout::CreateAndInstall(this); SetLayoutManager(tiles_layout_manager); views::ColumnSet* column_set = tiles_layout_manager->AddColumnSet(0);
diff --git a/ui/aura/mus/window_tree_client.cc b/ui/aura/mus/window_tree_client.cc index 3edbc80d..99d5a7f9 100644 --- a/ui/aura/mus/window_tree_client.cc +++ b/ui/aura/mus/window_tree_client.cc
@@ -154,21 +154,6 @@ return ui::GetScaleFactorForNativeView(window); } -void ConvertEventLocationToDip(int64_t display_id, ui::LocatedEvent* event) { - display::Screen* screen = display::Screen::GetScreen(); - display::Display display; - if (!screen->GetDisplayWithDisplayId(display_id, &display) || - display.device_scale_factor() == 1.f) { - return; - } - const gfx::Point host_location = - gfx::ConvertPointToDIP(display.device_scale_factor(), event->location()); - event->set_location(host_location); - const gfx::Point root_location = gfx::ConvertPointToDIP( - display.device_scale_factor(), event->root_location()); - event->set_root_location(root_location); -} - // Create and return a MouseEvent or TouchEvent from |event| if |event| is a // PointerEvent, otherwise return the copy of |event|. std::unique_ptr<ui::Event> MapEvent(const ui::Event& event) { @@ -390,6 +375,63 @@ return windows_.count(window_mus->server_id()) > 0; } +void WindowTreeClient::ConvertPointerEventLocationToDip( + int64_t display_id, + WindowMus* window, + ui::LocatedEvent* event) const { + // PointerEvents shouldn't have the target set. + DCHECK(!event->target()); + if (window_manager_delegate_) { + ConvertPointerEventLocationToDipInWindowManager(display_id, window, event); + return; + } + display::Screen* screen = display::Screen::GetScreen(); + display::Display display; + // TODO(sky): this needs to take into account the ui display scale. + // http://crbug.com/758399. + if (!screen->GetDisplayWithDisplayId(display_id, &display) || + display.device_scale_factor() == 1.f) { + return; + } + const gfx::Point root_location = gfx::ConvertPointToDIP( + display.device_scale_factor(), event->root_location()); + event->set_root_location(root_location); + if (window) { + const gfx::Point host_location = gfx::ConvertPointToDIP( + display.device_scale_factor(), event->location()); + event->set_location(host_location); + } else { + // When there is no window force the root and location to be the same. They + // may differ it |window| was valid at the time of the event, but was since + // deleted. + event->set_location(root_location); + } +} + +void WindowTreeClient::ConvertPointerEventLocationToDipInWindowManager( + int64_t display_id, + WindowMus* window, + ui::LocatedEvent* event) const { + const WindowTreeHostMus* window_tree_host = + GetWindowTreeHostForDisplayId(display_id); + if (!window_tree_host) + return; + + ui::Event::DispatcherApi dispatcher_api(event); + if (window) { + dispatcher_api.set_target(window->GetWindow()); + } else { + // UpdateForRootTransform() in the case of no target uses |location_|. + // |location_| may be relative to a window that wasn't found. To ensure we + // convert from the root, reset |location_| to |root_location_|. + event->set_location_f(event->root_location_f()); + } + event->UpdateForRootTransform( + window_tree_host->GetInverseRootTransform(), + window_tree_host->GetInverseRootTransformForLocalEventCoordinates()); + dispatcher_api.set_target(nullptr); +} + InFlightChange* WindowTreeClient::GetOldestInFlightChangeMatching( const InFlightChange& change) { for (const auto& pair : in_flight_map_) { @@ -481,6 +523,20 @@ window->SetPropertyFromServer(pair.first, &pair.second); } +const WindowTreeHostMus* WindowTreeClient::GetWindowTreeHostForDisplayId( + int64_t display_id) const { + if (!window_manager_delegate_) + return nullptr; + + for (WindowMus* window : roots_) { + WindowTreeHostMus* window_tree_host = + static_cast<WindowTreeHostMus*>(window->GetWindow()->GetHost()); + if (window_tree_host->display_id() == display_id) + return window_tree_host; + } + return nullptr; +} + std::unique_ptr<WindowTreeHostMus> WindowTreeClient::CreateWindowTreeHost( WindowMusType window_mus_type, const ui::mojom::WindowData& window_data, @@ -1366,7 +1422,8 @@ if (matches_pointer_watcher && has_pointer_watcher_) { DCHECK(event->IsPointerEvent()); std::unique_ptr<ui::Event> event_in_dip(ui::Event::Clone(*event)); - ConvertEventLocationToDip(display_id, event_in_dip->AsLocatedEvent()); + ConvertPointerEventLocationToDip(display_id, window, + event_in_dip->AsLocatedEvent()); delegate_->OnPointerEventObserved(*event_in_dip->AsPointerEvent(), window ? window->GetWindow() : nullptr); } @@ -1435,8 +1492,9 @@ if (!has_pointer_watcher_) return; - ConvertEventLocationToDip(display_id, event->AsLocatedEvent()); WindowMus* target_window = GetWindowByServerId(window_id); + ConvertPointerEventLocationToDip(display_id, target_window, + event->AsLocatedEvent()); delegate_->OnPointerEventObserved( *event->AsPointerEvent(), target_window ? target_window->GetWindow() : nullptr);
diff --git a/ui/aura/mus/window_tree_client.h b/ui/aura/mus/window_tree_client.h index c343903c..a81fa20 100644 --- a/ui/aura/mus/window_tree_client.h +++ b/ui/aura/mus/window_tree_client.h
@@ -207,6 +207,22 @@ bool IsWindowKnown(aura::Window* window); + // Updates the coordinates of |event| to be in DIPs. |window| is the source + // of the event, and may be null. A null |window| means either there is no + // local window the event is targeted at *or* |window| was valid at the time + // the event was generated at the server but was deleted locally before the + // event was received. + void ConvertPointerEventLocationToDip(int64_t display_id, + WindowMus* window, + ui::LocatedEvent* event) const; + + // Variant of ConvertPointerEventLocationToDip() that is used when in + // the window-manager. + void ConvertPointerEventLocationToDipInWindowManager( + int64_t display_id, + WindowMus* window, + ui::LocatedEvent* event) const; + // Returns the oldest InFlightChange that matches |change|. InFlightChange* GetOldestInFlightChangeMatching(const InFlightChange& change); @@ -236,6 +252,9 @@ WindowMus* window, const ui::mojom::WindowData& window_data); + const WindowTreeHostMus* GetWindowTreeHostForDisplayId( + int64_t display_id) const; + // Creates a new WindowTreeHostMus. std::unique_ptr<WindowTreeHostMus> CreateWindowTreeHost( WindowMusType window_mus_type,
diff --git a/ui/aura/mus/window_tree_client_unittest.cc b/ui/aura/mus/window_tree_client_unittest.cc index 4e12e26..85f2444 100644 --- a/ui/aura/mus/window_tree_client_unittest.cc +++ b/ui/aura/mus/window_tree_client_unittest.cc
@@ -52,6 +52,7 @@ #include "ui/events/test/test_event_handler.h" #include "ui/gfx/geometry/dip_util.h" #include "ui/gfx/geometry/rect.h" +#include "ui/gfx/transform.h" namespace aura { @@ -718,9 +719,9 @@ : test_window_tree_(test_window_tree) {} ~InputEventBasicTestWindowDelegate() override {} - bool got_move() const { return got_move_; } - bool got_press() const { return got_press_; } - bool got_release() const { return got_release_; } + int move_count() const { return move_count_; } + int press_count() const { return press_count_; } + int release_count() const { return release_count_; } bool was_acked() const { return was_acked_; } const gfx::Point& last_event_location() const { return last_event_location_; } void set_event_id(uint32_t event_id) { event_id_ = event_id; } @@ -735,11 +736,11 @@ void OnMouseEvent(ui::MouseEvent* event) override { was_acked_ = test_window_tree_->WasEventAcked(event_id_); if (event->type() == ui::ET_MOUSE_MOVED) - got_move_ = true; + ++move_count_; else if (event->type() == ui::ET_MOUSE_PRESSED) - got_press_ = true; + ++press_count_; else if (event->type() == ui::ET_MOUSE_RELEASED) - got_release_ = true; + ++release_count_; last_event_location_ = event->location(); last_mouse_event_had_native_event_ = event->HasNativeEvent(); if (event->HasNativeEvent()) { @@ -752,18 +753,18 @@ void OnTouchEvent(ui::TouchEvent* event) override { was_acked_ = test_window_tree_->WasEventAcked(event_id_); if (event->type() == ui::ET_TOUCH_PRESSED) - got_press_ = true; + ++press_count_; else if (event->type() == ui::ET_TOUCH_RELEASED) - got_release_ = true; + ++release_count_; last_event_location_ = event->location(); event->SetHandled(); } void reset() { was_acked_ = false; - got_move_ = false; - got_press_ = false; - got_release_ = false; + move_count_ = 0; + press_count_ = 0; + release_count_ = 0; last_event_location_ = gfx::Point(); event_id_ = 0; } @@ -771,9 +772,9 @@ private: TestWindowTree* test_window_tree_; bool was_acked_ = false; - bool got_move_ = false; - bool got_press_ = false; - bool got_release_ = false; + int move_count_ = 0; + int press_count_ = 0; + int release_count_ = false; gfx::Point last_event_location_; uint32_t event_id_ = 0; bool last_mouse_event_had_native_event_ = false; @@ -788,7 +789,7 @@ : target_window_(target_window) {} ~InputEventBasicTestEventHandler() override {} - bool got_move() const { return got_move_; } + int move_count() const { return move_count_; } const gfx::Point& last_event_location() const { return last_event_location_; } void set_event_id(uint32_t event_id) { event_id_ = event_id; } @@ -796,21 +797,21 @@ void OnMouseEvent(ui::MouseEvent* event) override { if (event->target() == target_window_) { if (event->type() == ui::ET_MOUSE_MOVED) - got_move_ = true; + ++move_count_; last_event_location_ = event->location(); event->SetHandled(); } } void reset() { - got_move_ = false; + move_count_ = 0; last_event_location_ = gfx::Point(); event_id_ = 0; } private: Window* target_window_ = nullptr; - bool got_move_ = false; + int move_count_ = 0; gfx::Point last_event_location_; uint32_t event_id_ = 0; @@ -835,7 +836,7 @@ top_level->AddChild(&child); child.SetBounds(gfx::Rect(10, 10, 100, 100)); child.Show(); - EXPECT_FALSE(window_delegate.got_move()); + EXPECT_EQ(0, window_delegate.move_count()); EXPECT_FALSE(window_delegate.was_acked()); const gfx::Point event_location_in_child(2, 3); const uint32_t event_id = 1; @@ -849,7 +850,7 @@ EXPECT_TRUE(window_tree()->WasEventAcked(event_id)); EXPECT_EQ(ui::mojom::EventResult::HANDLED, window_tree()->GetEventResult(event_id)); - EXPECT_TRUE(window_delegate.got_move()); + EXPECT_EQ(1, window_delegate.move_count()); EXPECT_FALSE(window_delegate.was_acked()); EXPECT_EQ(event_location_in_child, window_delegate.last_event_location()); } @@ -870,7 +871,7 @@ top_level->AddChild(&child); child.SetBounds(gfx::Rect(10, 10, 100, 100)); child.Show(); - EXPECT_FALSE(window_delegate.got_move()); + EXPECT_EQ(0, window_delegate.move_count()); const gfx::Point event_location(2, 3); const uint32_t event_id = 1; window_delegate.set_event_id(event_id); @@ -884,7 +885,7 @@ EXPECT_TRUE(window_tree()->WasEventAcked(event_id)); EXPECT_EQ(ui::mojom::EventResult::HANDLED, window_tree()->GetEventResult(event_id)); - EXPECT_TRUE(window_delegate.got_move()); + EXPECT_EQ(1, window_delegate.move_count()); EXPECT_EQ(event_location, window_delegate.last_event_location()); } @@ -912,8 +913,8 @@ child2.SetBounds(gfx::Rect(20, 30, 100, 100)); child2.Show(); - EXPECT_FALSE(window_delegate1.got_move()); - EXPECT_FALSE(window_delegate2.got_move()); + EXPECT_EQ(0, window_delegate1.move_count()); + EXPECT_EQ(0, window_delegate2.move_count()); // child1 has a targeter set and event_location is (50, 60), child2 // should get the event even though mus-ws wants to send to child1. @@ -930,8 +931,8 @@ EXPECT_TRUE(window_tree()->WasEventAcked(event_id)); EXPECT_EQ(ui::mojom::EventResult::HANDLED, window_tree()->GetEventResult(event_id)); - EXPECT_FALSE(window_delegate1.got_move()); - EXPECT_TRUE(window_delegate2.got_move()); + EXPECT_EQ(0, window_delegate1.move_count()); + EXPECT_EQ(1, window_delegate2.move_count()); EXPECT_EQ(gfx::Point(30, 30), window_delegate2.last_event_location()); window_delegate1.reset(); window_delegate2.reset(); @@ -951,8 +952,8 @@ EXPECT_TRUE(window_tree()->WasEventAcked(event_id)); EXPECT_EQ(ui::mojom::EventResult::HANDLED, window_tree()->GetEventResult(event_id)); - EXPECT_TRUE(window_delegate1.got_move()); - EXPECT_FALSE(window_delegate2.got_move()); + EXPECT_EQ(1, window_delegate1.move_count()); + EXPECT_EQ(0, window_delegate2.move_count()); EXPECT_EQ(gfx::Point(50, 60), window_delegate1.last_event_location()); } @@ -980,8 +981,8 @@ child2.SetBounds(gfx::Rect(20, 30, 100, 100)); child2.Show(); - EXPECT_FALSE(window_delegate1.got_move()); - EXPECT_FALSE(window_delegate2.got_move()); + EXPECT_EQ(0, window_delegate1.move_count()); + EXPECT_EQ(0, window_delegate2.move_count()); // child1 has a custom targeter set which would always return itself as the // target window therefore event should go to child1 unlike @@ -999,8 +1000,8 @@ EXPECT_TRUE(window_tree()->WasEventAcked(event_id)); EXPECT_EQ(ui::mojom::EventResult::HANDLED, window_tree()->GetEventResult(event_id)); - EXPECT_TRUE(window_delegate1.got_move()); - EXPECT_FALSE(window_delegate2.got_move()); + EXPECT_EQ(1, window_delegate1.move_count()); + EXPECT_EQ(0, window_delegate2.move_count()); EXPECT_EQ(gfx::Point(50, 60), window_delegate1.last_event_location()); window_delegate1.reset(); window_delegate2.reset(); @@ -1016,8 +1017,8 @@ EXPECT_TRUE(window_tree()->WasEventAcked(event_id)); EXPECT_EQ(ui::mojom::EventResult::HANDLED, window_tree()->GetEventResult(event_id)); - EXPECT_TRUE(window_delegate1.got_move()); - EXPECT_FALSE(window_delegate2.got_move()); + EXPECT_EQ(1, window_delegate1.move_count()); + EXPECT_EQ(0, window_delegate2.move_count()); EXPECT_EQ(gfx::Point(70, 90), window_delegate1.last_event_location()); } @@ -1050,8 +1051,8 @@ child2->SetBounds(gfx::Rect(20, 30, 100, 100)); child2->Show(); - EXPECT_FALSE(window_delegate1->got_move()); - EXPECT_FALSE(window_delegate2->got_move()); + EXPECT_EQ(0, window_delegate1->move_count()); + EXPECT_EQ(0, window_delegate2->move_count()); // child1 has a custom targeter set which would always return itself as the // target window therefore event should go to child1. @@ -1068,8 +1069,8 @@ EXPECT_TRUE(window_tree()->WasEventAcked(event_id)); EXPECT_EQ(ui::mojom::EventResult::HANDLED, window_tree()->GetEventResult(event_id)); - EXPECT_TRUE(window_delegate1->got_move()); - EXPECT_FALSE(window_delegate2->got_move()); + EXPECT_EQ(1, window_delegate1->move_count()); + EXPECT_EQ(0, window_delegate2->move_count()); EXPECT_EQ(gfx::Point(50, 60), window_delegate1->last_event_location()); window_delegate1->reset(); window_delegate2->reset(); @@ -1089,8 +1090,8 @@ EXPECT_TRUE(window_tree()->WasEventAcked(event_id)); EXPECT_EQ(ui::mojom::EventResult::HANDLED, window_tree()->GetEventResult(event_id)); - EXPECT_FALSE(window_delegate1->got_move()); - EXPECT_TRUE(window_delegate2->got_move()); + EXPECT_EQ(0, window_delegate1->move_count()); + EXPECT_EQ(1, window_delegate2->move_count()); EXPECT_EQ(gfx::Point(30, 30), window_delegate2->last_event_location()); child2.reset(); child1.reset(); @@ -1117,8 +1118,8 @@ child.SetBounds(gfx::Rect(10, 10, 100, 100)); child.Show(); - EXPECT_FALSE(root_handler.got_move()); - EXPECT_FALSE(child_delegate.got_move()); + EXPECT_EQ(0, root_handler.move_count()); + EXPECT_EQ(0, child_delegate.move_count()); const gfx::Point event_location_in_child(20, 30); const uint32_t event_id = 1; @@ -1134,9 +1135,9 @@ EXPECT_TRUE(window_tree()->WasEventAcked(event_id)); EXPECT_EQ(ui::mojom::EventResult::HANDLED, window_tree()->GetEventResult(event_id)); - EXPECT_TRUE(root_handler.got_move()); + EXPECT_EQ(1, root_handler.move_count()); EXPECT_EQ(gfx::Point(20, 30), root_handler.last_event_location()); - EXPECT_FALSE(child_delegate.got_move()); + EXPECT_EQ(0, child_delegate.move_count()); EXPECT_EQ(gfx::Point(), child_delegate.last_event_location()); } @@ -1158,7 +1159,7 @@ child.SetBounds(gfx::Rect(10, 10, 100, 100)); child.Show(); - EXPECT_FALSE(window_delegate.got_press()); + EXPECT_EQ(0, window_delegate.press_count()); EXPECT_FALSE(env->IsMouseButtonDown()); EXPECT_FALSE(env->mouse_button_flags()); EXPECT_EQ(gfx::Point(), env->last_mouse_location()); @@ -1177,7 +1178,7 @@ EXPECT_TRUE(window_tree()->WasEventAcked(event_id)); EXPECT_EQ(ui::mojom::EventResult::HANDLED, window_tree()->GetEventResult(event_id)); - EXPECT_TRUE(window_delegate.got_press()); + EXPECT_EQ(1, window_delegate.press_count()); EXPECT_TRUE(env->IsMouseButtonDown()); EXPECT_EQ(1024, env->mouse_button_flags()); // ui::EF_LEFT_MOUSE_BUTTON EXPECT_EQ(event_location, env->last_mouse_location()); @@ -1200,7 +1201,7 @@ // aura::Env, location shouldn't be updated. EXPECT_EQ(ui::mojom::EventResult::UNHANDLED, window_tree()->GetEventResult(event_id)); - EXPECT_FALSE(window_delegate.got_release()); + EXPECT_EQ(0, window_delegate.release_count()); EXPECT_FALSE(env->IsMouseButtonDown()); EXPECT_FALSE(env->mouse_button_flags()); EXPECT_EQ(event_location, env->last_mouse_location()); @@ -1224,7 +1225,7 @@ child.SetBounds(gfx::Rect(10, 10, 100, 100)); child.Show(); - EXPECT_FALSE(window_delegate.got_press()); + EXPECT_EQ(0, window_delegate.press_count()); EXPECT_FALSE(env->is_touch_down()); const gfx::Point event_location(2, 3); @@ -1240,7 +1241,7 @@ EXPECT_TRUE(window_tree()->WasEventAcked(event_id)); EXPECT_EQ(ui::mojom::EventResult::HANDLED, window_tree()->GetEventResult(event_id)); - EXPECT_TRUE(window_delegate.got_press()); + EXPECT_EQ(1, window_delegate.press_count()); EXPECT_TRUE(env->is_touch_down()); window_delegate.reset(); @@ -1259,7 +1260,7 @@ // aura::Env. EXPECT_EQ(ui::mojom::EventResult::UNHANDLED, window_tree()->GetEventResult(event_id)); - EXPECT_FALSE(window_delegate.got_release()); + EXPECT_EQ(0, window_delegate.release_count()); EXPECT_FALSE(env->is_touch_down()); } @@ -2319,6 +2320,7 @@ WindowTreeHostMusInitParams init_params = WindowTreeClientPrivate(window_tree_client_impl()) .CallCreateInitParamsForNewDisplay(); + init_params.display_id = display_params->display->id(); init_params.display_init_params = std::move(display_params); WindowTreeHostMus window_tree_host(std::move(init_params)); window_tree_host.InitHost(); @@ -2392,7 +2394,9 @@ // Delegate received the event in Dips. const ui::PointerEvent* last_event = last_event_observed(); ASSERT_TRUE(last_event); - EXPECT_EQ(gfx::ConvertPointToDIP(2.0f, location_pixels), + // NOTE: the root and location are the same as there was no window supplied to + // OnPointerEventObserved(). + EXPECT_EQ(gfx::ConvertPointToDIP(2.0f, root_location_pixels), last_event->location()); EXPECT_EQ(gfx::ConvertPointToDIP(2.0f, root_location_pixels), last_event->root_location()); @@ -2429,8 +2433,8 @@ child2.SetBounds(gfx::Rect(20, 30, 100, 100)); child2.Show(); - EXPECT_FALSE(window_delegate1.got_move()); - EXPECT_FALSE(window_delegate2.got_move()); + EXPECT_EQ(0, window_delegate1.move_count()); + EXPECT_EQ(0, window_delegate2.move_count()); // child1 has a custom targeter set which would always return itself as the // target window therefore event should go to child1 and should be in dip. @@ -2447,8 +2451,8 @@ EXPECT_TRUE(window_tree()->WasEventAcked(event_id)); EXPECT_EQ(ui::mojom::EventResult::HANDLED, window_tree()->GetEventResult(event_id)); - EXPECT_TRUE(window_delegate1.got_move()); - EXPECT_FALSE(window_delegate2.got_move()); + EXPECT_EQ(1, window_delegate1.move_count()); + EXPECT_EQ(0, window_delegate2.move_count()); const gfx::Point event_location_in_dip(25, 30); EXPECT_EQ(event_location_in_dip, window_delegate1.last_event_location()); #if defined(USE_OZONE) @@ -2471,8 +2475,8 @@ EXPECT_TRUE(window_tree()->WasEventAcked(event_id)); EXPECT_EQ(ui::mojom::EventResult::HANDLED, window_tree()->GetEventResult(event_id)); - EXPECT_TRUE(window_delegate1.got_move()); - EXPECT_FALSE(window_delegate2.got_move()); + EXPECT_EQ(1, window_delegate1.move_count()); + EXPECT_EQ(0, window_delegate2.move_count()); gfx::Point transformed_event_location_in_dip(event_location_in_dip.x() + 20, event_location_in_dip.y() + 30); EXPECT_EQ(transformed_event_location_in_dip, @@ -2493,4 +2497,76 @@ window2.Init(ui::LAYER_NOT_DRAWN); } +TEST_F(WindowTreeClientWmTest, ObservedPointerEvents) { + const gfx::Rect bounds(1, 2, 101, 102); + std::unique_ptr<DisplayInitParams> display_params = + base::MakeUnique<DisplayInitParams>(); + const int64_t display_id = 201; + float device_scale_factor = 2.0f; + float ui_scale_factor = 1.5f; + display_params->display = base::MakeUnique<display::Display>(display_id); + display_params->display->set_bounds(bounds); + display_params->viewport_metrics.bounds_in_pixels = bounds; + display_params->viewport_metrics.device_scale_factor = device_scale_factor; + display_params->viewport_metrics.ui_scale_factor = ui_scale_factor; + WindowTreeHostMusInitParams init_params = + WindowTreeClientPrivate(window_tree_client_impl()) + .CallCreateInitParamsForNewDisplay(); + init_params.display_id = display_id; + init_params.display_init_params = std::move(display_params); + + WindowTreeHostMus window_tree_host(std::move(init_params)); + window_tree_host.InitHost(); + gfx::Transform scale_transform; + scale_transform.Scale(ui_scale_factor, ui_scale_factor); + window_tree_host.window()->SetTransform(scale_transform); + window_tree_host.compositor()->SetScaleAndSize(device_scale_factor, + bounds.size()); + + // Start a pointer watcher for all events excluding move events. + window_tree_client_impl()->StartPointerWatcher(false /* want_moves */); + + // Simulate the server sending an observed event. + const gfx::Point location_pixels(10, 12); + const gfx::Point root_location_pixels(14, 16); + std::unique_ptr<ui::PointerEvent> pointer_event_down(new ui::PointerEvent( + ui::ET_POINTER_DOWN, location_pixels, root_location_pixels, + ui::EF_CONTROL_DOWN, 0, + ui::PointerDetails(ui::EventPointerType::POINTER_TYPE_TOUCH, 1), + base::TimeTicks())); + std::unique_ptr<ui::PointerEvent> pointer_event_down2( + ui::Event::Clone(*pointer_event_down).release()->AsPointerEvent()); + window_tree_client()->OnPointerEventObserved(std::move(pointer_event_down), + 0u, display_id); + + ASSERT_FALSE(observed_pointer_events().empty()); + const ui::PointerEvent* last_event = observed_pointer_events().back().get(); + ASSERT_TRUE(last_event); + EXPECT_EQ(nullptr, last_event->target()); + // NOTE: the root and location are the same as there was no window supplied to + // OnPointerEventObserved(). + EXPECT_EQ(gfx::ConvertPointToDIP(device_scale_factor * ui_scale_factor, + root_location_pixels), + last_event->location()); + EXPECT_EQ(gfx::ConvertPointToDIP(device_scale_factor * ui_scale_factor, + root_location_pixels), + last_event->root_location()); + + observed_pointer_events().clear(); + window_tree_client()->OnPointerEventObserved( + std::move(pointer_event_down2), + WindowMus::Get(window_tree_host.window())->server_id(), display_id); + ASSERT_FALSE(observed_pointer_events().empty()); + last_event = observed_pointer_events().back().get(); + ASSERT_TRUE(last_event); + EXPECT_EQ(nullptr, last_event->target()); + // |location| from the server has already had |ui_scale_factor| applied, so + // it won't be reapplied here. + EXPECT_EQ(gfx::ConvertPointToDIP(device_scale_factor, location_pixels), + last_event->location()); + EXPECT_EQ(gfx::ConvertPointToDIP(device_scale_factor * ui_scale_factor, + root_location_pixels), + last_event->root_location()); +} + } // namespace aura
diff --git a/ui/aura/mus/window_tree_host_mus.cc b/ui/aura/mus/window_tree_host_mus.cc index 9213acff..066026ed 100644 --- a/ui/aura/mus/window_tree_host_mus.cc +++ b/ui/aura/mus/window_tree_host_mus.cc
@@ -41,8 +41,11 @@ delegate_(init_params.window_tree_client) { gfx::Rect bounds_in_pixels; display_init_params_ = std::move(init_params.display_init_params); - if (display_init_params_) + if (display_init_params_) { bounds_in_pixels = display_init_params_->viewport_metrics.bounds_in_pixels; + if (display_init_params_->display) + DCHECK_EQ(display_id_, display_init_params_->display->id()); + } window()->SetProperty(kWindowTreeHostMusKey, this); // TODO(sky): find a cleaner way to set this! Better solution is to likely // have constructor take aura::Window.
diff --git a/ui/aura/test/aura_test_base.cc b/ui/aura/test/aura_test_base.cc index 37991ac..58a7b61 100644 --- a/ui/aura/test/aura_test_base.cc +++ b/ui/aura/test/aura_test_base.cc
@@ -162,7 +162,10 @@ void AuraTestBase::OnLostConnection(WindowTreeClient* client) {} void AuraTestBase::OnPointerEventObserved(const ui::PointerEvent& event, - Window* target) {} + Window* target) { + observed_pointer_events_.push_back(std::unique_ptr<ui::PointerEvent>( + static_cast<ui::PointerEvent*>(ui::Event::Clone(event).release()))); +} void AuraTestBase::SetWindowManagerClient(WindowManagerClient* client) {}
diff --git a/ui/aura/test/aura_test_base.h b/ui/aura/test/aura_test_base.h index 1ef3d0c1..7835a515 100644 --- a/ui/aura/test/aura_test_base.h +++ b/ui/aura/test/aura_test_base.h
@@ -92,6 +92,10 @@ } ui::mojom::WindowTreeClient* window_tree_client(); + std::vector<std::unique_ptr<ui::PointerEvent>>& observed_pointer_events() { + return observed_pointer_events_; + } + // WindowTreeClientDelegate: void OnEmbed(std::unique_ptr<WindowTreeHostMus> window_tree_host) override; void OnUnembed(Window* root) override; @@ -158,6 +162,7 @@ PropertyConverter property_converter_; std::unique_ptr<AuraTestHelper> helper_; std::vector<std::unique_ptr<WindowTreeHostMus>> window_tree_hosts_; + std::vector<std::unique_ptr<ui::PointerEvent>> observed_pointer_events_; DISALLOW_COPY_AND_ASSIGN(AuraTestBase); };
diff --git a/ui/gl/init/gl_factory.cc b/ui/gl/init/gl_factory.cc index de074f4a..643941be 100644 --- a/ui/gl/init/gl_factory.cc +++ b/ui/gl/init/gl_factory.cc
@@ -18,37 +18,6 @@ namespace init { namespace { - -bool InitializeGLOneOffImplementationHelper(GLImplementation impl, - bool fallback_to_software_gl, - bool gpu_service_logging, - bool disable_gl_drawing, - bool init_extensions) { - bool initialized = - InitializeStaticGLBindings(impl) && InitializeGLOneOffPlatform(); - if (!initialized && fallback_to_software_gl) { - ShutdownGL(); - initialized = InitializeStaticGLBindings(GetSoftwareGLImplementation()) && - InitializeGLOneOffPlatform(); - } - if (initialized && init_extensions) { - initialized = InitializeExtensionSettingsOneOffPlatform(); - } - - if (!initialized) - ShutdownGL(); - - if (initialized) { - DVLOG(1) << "Using " << GetGLImplementationName(GetGLImplementation()) - << " GL implementation."; - if (gpu_service_logging) - InitializeDebugGLBindings(); - if (disable_gl_drawing) - InitializeNullDrawGLBindings(); - } - return initialized; -} - bool InitializeGLOneOffHelper(bool init_extensions) { DCHECK_EQ(kGLImplementationNone, GetGLImplementation()); @@ -86,9 +55,9 @@ bool gpu_service_logging = cmd->HasSwitch(switches::kEnableGPUServiceLogging); bool disable_gl_drawing = cmd->HasSwitch(switches::kDisableGLDrawingForTests); - return InitializeGLOneOffImplementationHelper( - impl, fallback_to_software_gl, gpu_service_logging, disable_gl_drawing, - init_extensions); + return InitializeGLOneOffImplementation(impl, fallback_to_software_gl, + gpu_service_logging, + disable_gl_drawing, init_extensions); } } // namespace @@ -106,10 +75,31 @@ bool InitializeGLOneOffImplementation(GLImplementation impl, bool fallback_to_software_gl, bool gpu_service_logging, - bool disable_gl_drawing) { - return InitializeGLOneOffImplementationHelper(impl, fallback_to_software_gl, - gpu_service_logging, - disable_gl_drawing, true); + bool disable_gl_drawing, + bool init_extensions) { + bool initialized = + InitializeStaticGLBindings(impl) && InitializeGLOneOffPlatform(); + if (!initialized && fallback_to_software_gl) { + ShutdownGL(); + initialized = InitializeStaticGLBindings(GetSoftwareGLImplementation()) && + InitializeGLOneOffPlatform(); + } + if (initialized && init_extensions) { + initialized = InitializeExtensionSettingsOneOffPlatform(); + } + + if (!initialized) + ShutdownGL(); + + if (initialized) { + DVLOG(1) << "Using " << GetGLImplementationName(GetGLImplementation()) + << " GL implementation."; + if (gpu_service_logging) + InitializeDebugGLBindings(); + if (disable_gl_drawing) + InitializeNullDrawGLBindings(); + } + return initialized; } void ShutdownGL() {
diff --git a/ui/gl/init/gl_factory.h b/ui/gl/init/gl_factory.h index f62042a..44819da 100644 --- a/ui/gl/init/gl_factory.h +++ b/ui/gl/init/gl_factory.h
@@ -50,7 +50,8 @@ GLImplementation impl, bool fallback_to_software_gl, bool gpu_service_logging, - bool disable_gl_drawing); + bool disable_gl_drawing, + bool init_extensions); // Clears GL bindings and resets GL implementation. GL_INIT_EXPORT void ShutdownGL();
diff --git a/ui/gl/test/gl_surface_test_support.cc b/ui/gl/test/gl_surface_test_support.cc index 32b45de..dcf153b 100644 --- a/ui/gl/test/gl_surface_test_support.cc +++ b/ui/gl/test/gl_surface_test_support.cc
@@ -25,8 +25,8 @@ namespace gl { -// static -void GLSurfaceTestSupport::InitializeOneOff() { +namespace { +void InitializeOneOffHelper(bool init_extensions) { DCHECK_EQ(kGLImplementationNone, GetGLImplementation()); #if defined(USE_X11) @@ -76,7 +76,19 @@ bool disable_gl_drawing = true; CHECK(init::InitializeGLOneOffImplementation( - impl, fallback_to_software_gl, gpu_service_logging, disable_gl_drawing)); + impl, fallback_to_software_gl, gpu_service_logging, disable_gl_drawing, + init_extensions)); +} +} // namespace + +// static +void GLSurfaceTestSupport::InitializeOneOff() { + InitializeOneOffHelper(true); +} + +// static +void GLSurfaceTestSupport::InitializeNoExtensionsOneOff() { + InitializeOneOffHelper(false); } // static @@ -93,8 +105,9 @@ bool gpu_service_logging = false; bool disable_gl_drawing = false; - CHECK(init::InitializeGLOneOffImplementation( - impl, fallback_to_software_gl, gpu_service_logging, disable_gl_drawing)); + CHECK(init::InitializeGLOneOffImplementation(impl, fallback_to_software_gl, + gpu_service_logging, + disable_gl_drawing, true)); } // static
diff --git a/ui/gl/test/gl_surface_test_support.h b/ui/gl/test/gl_surface_test_support.h index 2fa64aea..747ffd1c 100644 --- a/ui/gl/test/gl_surface_test_support.h +++ b/ui/gl/test/gl_surface_test_support.h
@@ -12,6 +12,7 @@ class GLSurfaceTestSupport { public: static void InitializeOneOff(); + static void InitializeNoExtensionsOneOff(); static void InitializeOneOffImplementation(GLImplementation impl, bool fallback_to_osmesa); static void InitializeOneOffWithMockBindings();
diff --git a/ui/message_center/views/message_center_button_bar.cc b/ui/message_center/views/message_center_button_bar.cc index 95500a5..116d3b84 100644 --- a/ui/message_center/views/message_center_button_bar.cc +++ b/ui/message_center/views/message_center_button_bar.cc
@@ -159,8 +159,7 @@ } void MessageCenterButtonBar::ViewVisibilityChanged() { - views::GridLayout* layout = new views::GridLayout(this); - SetLayoutManager(layout); + views::GridLayout* layout = views::GridLayout::CreateAndInstall(this); views::ColumnSet* column = layout->AddColumnSet(0); constexpr int kFooterLeftMargin = 4; column->AddPaddingColumn(0, kFooterLeftMargin);
diff --git a/ui/message_center/views/notifier_settings_view.cc b/ui/message_center/views/notifier_settings_view.cc index 639c334..f527556 100644 --- a/ui/message_center/views/notifier_settings_view.cc +++ b/ui/message_center/views/notifier_settings_view.cc
@@ -362,8 +362,7 @@ using views::ColumnSet; using views::GridLayout; - GridLayout* layout = new GridLayout(this); - SetLayoutManager(layout); + GridLayout* layout = GridLayout::CreateAndInstall(this); ColumnSet* cs = layout->AddColumnSet(0); // Add a column for the checkbox. cs->AddPaddingColumn(0, kInnateCheckboxRightPadding);
diff --git a/ui/views/color_chooser/color_chooser_view.cc b/ui/views/color_chooser/color_chooser_view.cc index df8d0cf..531e121 100644 --- a/ui/views/color_chooser/color_chooser_view.cc +++ b/ui/views/color_chooser/color_chooser_view.cc
@@ -377,8 +377,7 @@ AddChildView(container); View* container2 = new View(); - GridLayout* layout = new GridLayout(container2); - container2->SetLayoutManager(layout); + GridLayout* layout = GridLayout::CreateAndInstall(container2); ColumnSet* columns = layout->AddColumnSet(0); columns->AddColumn( GridLayout::LEADING, GridLayout::FILL, 0, GridLayout::USE_PREF, 0, 0);
diff --git a/ui/views/controls/message_box_view.cc b/ui/views/controls/message_box_view.cc index 2445444..af7b8062 100644 --- a/ui/views/controls/message_box_view.cc +++ b/ui/views/controls/message_box_view.cc
@@ -14,6 +14,7 @@ #include "ui/base/clipboard/clipboard.h" #include "ui/base/clipboard/scoped_clipboard_writer.h" #include "ui/gfx/geometry/insets.h" +#include "ui/views/border.h" #include "ui/views/controls/button/checkbox.h" #include "ui/views/controls/label.h" #include "ui/views/controls/link.h" @@ -178,6 +179,9 @@ // MessageBoxView, private: void MessageBoxView::Init(const InitParams& params) { + SetBorder(CreateEmptyBorder( + LayoutProvider::Get()->GetInsetsMetric(INSETS_DIALOG_CONTENTS))); + if (params.options & DETECT_DIRECTIONALITY) { std::vector<base::string16> texts; SplitStringIntoParagraphs(params.message, &texts); @@ -213,8 +217,7 @@ void MessageBoxView::ResetLayoutManager() { // Initialize the Grid Layout Manager used for this dialog box. - GridLayout* layout = GridLayout::CreatePanel(this); - SetLayoutManager(layout); + GridLayout* layout = GridLayout::CreateAndInstall(this); // Add the column set for the message displayed at the top of the dialog box. const int message_column_view_set_id = 0;
diff --git a/ui/views/examples/button_sticker_sheet.cc b/ui/views/examples/button_sticker_sheet.cc index 9b32de4f..eb804408 100644 --- a/ui/views/examples/button_sticker_sheet.cc +++ b/ui/views/examples/button_sticker_sheet.cc
@@ -31,7 +31,7 @@ const GridLayout::SizeType kColumnUsesFixedSize = GridLayout::FIXED; const int kColumnWidth = 96; - GridLayout* layout = new GridLayout(host); + GridLayout* layout = GridLayout::CreateAndInstall(host); ColumnSet* columns = layout->AddColumnSet(kStretchyGridColumnSetId); for (int i = 0; i < ncols; ++i) { if (i != 0) @@ -89,7 +89,6 @@ void ButtonStickerSheet::CreateExampleView(View* container) { GridLayout* layout = MakeStretchyGridLayout(container, 3); - container->SetLayoutManager(layout); if (!ui::MaterialDesignController::IsSecondaryUiMaterial()) { const char* kNeedsMdWarning =
diff --git a/ui/views/examples/dialog_example.cc b/ui/views/examples/dialog_example.cc index 2318b01..f5f348f 100644 --- a/ui/views/examples/dialog_example.cc +++ b/ui/views/examples/dialog_example.cc
@@ -133,8 +133,7 @@ views::LayoutProvider* provider = views::LayoutProvider::Get(); const int horizontal_spacing = provider->GetDistanceMetric(views::DISTANCE_RELATED_BUTTON_HORIZONTAL); - GridLayout* layout = GridLayout::CreatePanel(container); - container->SetLayoutManager(layout); + GridLayout* layout = GridLayout::CreateAndInstall(container); ColumnSet* column_set = layout->AddColumnSet(kFieldsColumnId); column_set->AddColumn(GridLayout::LEADING, GridLayout::FILL, kFixed, GridLayout::USE_PREF, 0, 0);
diff --git a/ui/views/examples/examples_window.cc b/ui/views/examples/examples_window.cc index ae5e2ab..a81925f 100644 --- a/ui/views/examples/examples_window.cc +++ b/ui/views/examples/examples_window.cc
@@ -143,8 +143,7 @@ combobox_->ModelChanged(); SetBackground(CreateStandardPanelBackground()); - GridLayout* layout = new GridLayout(this); - SetLayoutManager(layout); + GridLayout* layout = GridLayout::CreateAndInstall(this); ColumnSet* column_set = layout->AddColumnSet(0); column_set->AddPaddingColumn(0, 5); column_set->AddColumn(GridLayout::FILL, GridLayout::FILL, 1,
diff --git a/ui/views/examples/label_example.cc b/ui/views/examples/label_example.cc index 3244135..dbc8932 100644 --- a/ui/views/examples/label_example.cc +++ b/ui/views/examples/label_example.cc
@@ -163,8 +163,7 @@ View* control_container = new View(); control_container->SetBorder(CreateSolidBorder(2, SK_ColorGRAY)); control_container->SetBackground(CreateSolidBackground(SK_ColorLTGRAY)); - GridLayout* layout = GridLayout::CreatePanel(control_container); - control_container->SetLayoutManager(layout); + GridLayout* layout = GridLayout::CreateAndInstall(control_container); ColumnSet* column_set = layout->AddColumnSet(0); column_set->AddColumn(GridLayout::LEADING, GridLayout::FILL,
diff --git a/ui/views/examples/message_box_example.cc b/ui/views/examples/message_box_example.cc index cb9e924..3327ff9 100644 --- a/ui/views/examples/message_box_example.cc +++ b/ui/views/examples/message_box_example.cc
@@ -27,8 +27,7 @@ status_ = new LabelButton(this, ASCIIToUTF16("Show Status")); toggle_ = new LabelButton(this, ASCIIToUTF16("Toggle Checkbox")); - GridLayout* layout = new GridLayout(container); - container->SetLayoutManager(layout); + GridLayout* layout = GridLayout::CreateAndInstall(container); message_box_view_->SetCheckBoxLabel(ASCIIToUTF16("Check Box"));
diff --git a/ui/views/examples/multiline_example.cc b/ui/views/examples/multiline_example.cc index c92800f..e4d2461 100644 --- a/ui/views/examples/multiline_example.cc +++ b/ui/views/examples/multiline_example.cc
@@ -159,8 +159,7 @@ textfield_->set_controller(this); textfield_->SetText(kTestString); - GridLayout* layout = new GridLayout(container); - container->SetLayoutManager(layout); + GridLayout* layout = GridLayout::CreateAndInstall(container); ColumnSet* column_set = layout->AddColumnSet(0); column_set->AddColumn(GridLayout::LEADING, GridLayout::CENTER,
diff --git a/ui/views/examples/progress_bar_example.cc b/ui/views/examples/progress_bar_example.cc index 4cb75a15..c9d82665 100644 --- a/ui/views/examples/progress_bar_example.cc +++ b/ui/views/examples/progress_bar_example.cc
@@ -36,8 +36,7 @@ } void ProgressBarExample::CreateExampleView(View* container) { - GridLayout* layout = new GridLayout(container); - container->SetLayoutManager(layout); + GridLayout* layout = GridLayout::CreateAndInstall(container); ColumnSet* column_set = layout->AddColumnSet(0); column_set->AddColumn(GridLayout::TRAILING, GridLayout::CENTER, 0,
diff --git a/ui/views/examples/radio_button_example.cc b/ui/views/examples/radio_button_example.cc index 38072ef4..e0daa38f 100644 --- a/ui/views/examples/radio_button_example.cc +++ b/ui/views/examples/radio_button_example.cc
@@ -38,8 +38,7 @@ radio_buttons_[i]->set_listener(this); } - GridLayout* layout = new GridLayout(container); - container->SetLayoutManager(layout); + GridLayout* layout = GridLayout::CreateAndInstall(container); ColumnSet* column_set = layout->AddColumnSet(0); column_set->AddColumn(GridLayout::FILL, GridLayout::FILL,
diff --git a/ui/views/examples/scroll_view_example.cc b/ui/views/examples/scroll_view_example.cc index 39ffd8fb..ef6ff41 100644 --- a/ui/views/examples/scroll_view_example.cc +++ b/ui/views/examples/scroll_view_example.cc
@@ -87,8 +87,7 @@ scrollable_->SetBounds(0, 0, 1000, 100); scrollable_->SetColor(SK_ColorYELLOW, SK_ColorCYAN); - GridLayout* layout = new GridLayout(container); - container->SetLayoutManager(layout); + GridLayout* layout = GridLayout::CreateAndInstall(container); // Add scroll view. ColumnSet* column_set = layout->AddColumnSet(0);
diff --git a/ui/views/examples/tabbed_pane_example.cc b/ui/views/examples/tabbed_pane_example.cc index 524fec6c9..2623c10b 100644 --- a/ui/views/examples/tabbed_pane_example.cc +++ b/ui/views/examples/tabbed_pane_example.cc
@@ -27,8 +27,7 @@ add_at_ = new LabelButton(this, ASCIIToUTF16("Add At 1")); select_at_ = new LabelButton(this, ASCIIToUTF16("Select At 1")); - GridLayout* layout = new GridLayout(container); - container->SetLayoutManager(layout); + GridLayout* layout = GridLayout::CreateAndInstall(container); const int tabbed_pane_column = 0; ColumnSet* column_set = layout->AddColumnSet(tabbed_pane_column);
diff --git a/ui/views/examples/table_example.cc b/ui/views/examples/table_example.cc index 041270a..f3bd426 100644 --- a/ui/views/examples/table_example.cc +++ b/ui/views/examples/table_example.cc
@@ -58,8 +58,7 @@ column4_visible_checkbox_->SetChecked(true); column4_visible_checkbox_->set_listener(this); - GridLayout* layout = new GridLayout(container); - container->SetLayoutManager(layout); + GridLayout* layout = GridLayout::CreateAndInstall(container); std::vector<ui::TableColumn> columns; columns.push_back(TestTableColumn(0, "Fruit"));
diff --git a/ui/views/examples/text_example.cc b/ui/views/examples/text_example.cc index 6869b076..37b1ef11 100644 --- a/ui/views/examples/text_example.cc +++ b/ui/views/examples/text_example.cc
@@ -160,8 +160,7 @@ void TextExample::CreateExampleView(View* container) { text_view_ = new TextExampleView; text_view_->SetBorder(CreateSolidBorder(1, SK_ColorGRAY)); - GridLayout* layout = new GridLayout(container); - container->SetLayoutManager(layout); + GridLayout* layout = GridLayout::CreateAndInstall(container); layout->AddPaddingRow(0, 8); ColumnSet* column_set = layout->AddColumnSet(0);
diff --git a/ui/views/examples/textfield_example.cc b/ui/views/examples/textfield_example.cc index 6f3c5f2..e166ac0 100644 --- a/ui/views/examples/textfield_example.cc +++ b/ui/views/examples/textfield_example.cc
@@ -61,8 +61,7 @@ name_->set_controller(this); password_->set_controller(this); - GridLayout* layout = new GridLayout(container); - container->SetLayoutManager(layout); + GridLayout* layout = GridLayout::CreateAndInstall(container); ColumnSet* column_set = layout->AddColumnSet(0); column_set->AddColumn(GridLayout::LEADING, GridLayout::FILL,
diff --git a/ui/views/examples/tree_view_example.cc b/ui/views/examples/tree_view_example.cc index 8e0f6a1..4ecdd6d3 100644 --- a/ui/views/examples/tree_view_example.cc +++ b/ui/views/examples/tree_view_example.cc
@@ -54,8 +54,7 @@ change_title_->SetFocusForPlatform(); change_title_->set_request_focus_on_press(true); - GridLayout* layout = new GridLayout(container); - container->SetLayoutManager(layout); + GridLayout* layout = GridLayout::CreateAndInstall(container); const int tree_view_column = 0; ColumnSet* column_set = layout->AddColumnSet(tree_view_column);
diff --git a/ui/views/layout/grid_layout.cc b/ui/views/layout/grid_layout.cc index eff6527..0775b73 100644 --- a/ui/views/layout/grid_layout.cc +++ b/ui/views/layout/grid_layout.cc
@@ -632,29 +632,16 @@ // GridLayout ------------------------------------------------------------- -GridLayout::GridLayout(View* host) - : host_(host), - calculated_master_columns_(false), - remaining_row_span_(0), - current_row_(-1), - next_column_(0), - current_row_col_set_(nullptr), - adding_view_(false) { - DCHECK(host); +// static +GridLayout* GridLayout::CreateAndInstall(View* host) { + GridLayout* result = new GridLayout(host); + host->SetLayoutManager(result); + return result; } GridLayout::~GridLayout() { } -// static -GridLayout* GridLayout::CreatePanel(View* host) { - GridLayout* layout = new GridLayout(host); - host->SetBorder(CreateEmptyBorder( - LayoutProvider::Get()->GetInsetsMetric(INSETS_DIALOG_CONTENTS))); - host->SetLayoutManager(layout); - return layout; -} - ColumnSet* GridLayout::AddColumnSet(int id) { DCHECK(GetColumnSet(id) == nullptr); column_sets_.push_back(base::WrapUnique(new ColumnSet(id))); @@ -810,6 +797,17 @@ return pref.height(); } +GridLayout::GridLayout(View* host) + : host_(host), + calculated_master_columns_(false), + remaining_row_span_(0), + current_row_(-1), + next_column_(0), + current_row_col_set_(nullptr), + adding_view_(false) { + DCHECK(host); +} + void GridLayout::SizeRowsAndColumns(bool layout, int width, int height, gfx::Size* pref) const { // Protect against clients asking for metrics during the addition of a View.
diff --git a/ui/views/layout/grid_layout.h b/ui/views/layout/grid_layout.h index 66d9f65a..a2521d4 100644 --- a/ui/views/layout/grid_layout.h +++ b/ui/views/layout/grid_layout.h
@@ -100,12 +100,10 @@ USE_PREF }; - explicit GridLayout(View* host); - ~GridLayout() override; + // Creates a new GridLayout and installs it as the LayoutManager for |host|. + static GridLayout* CreateAndInstall(View* host); - // Creates a GridLayout, assigns it as the LayoutManager of |host|, and gives - // it a INSETS_PANEL-sized padding border. - static GridLayout* CreatePanel(View* host); + ~GridLayout() override; // Creates a new column set with the specified id and returns it. // The id is later used when starting a new row. @@ -180,6 +178,8 @@ void set_minimum_size(const gfx::Size& size) { minimum_size_ = size; } private: + explicit GridLayout(View* host); + // As both Layout and GetPreferredSize need to do nearly the same thing, // they both call into this method. This sizes the Columns/Rows as // appropriate. If layout is true, width/height give the width/height the
diff --git a/ui/views/layout/grid_layout_unittest.cc b/ui/views/layout/grid_layout_unittest.cc index ac58f08e..686eefb 100644 --- a/ui/views/layout/grid_layout_unittest.cc +++ b/ui/views/layout/grid_layout_unittest.cc
@@ -78,50 +78,54 @@ class GridLayoutTest : public testing::Test { public: - GridLayoutTest() : layout(&host) {} + GridLayoutTest() : layout_(GridLayout::CreateAndInstall(&host_)) {} void RemoveAll() { - for (int i = host.child_count() - 1; i >= 0; i--) - host.RemoveChildView(host.child_at(i)); + for (int i = host_.child_count() - 1; i >= 0; i--) + host_.RemoveChildView(host_.child_at(i)); } - void GetPreferredSize() { - pref = layout.GetPreferredSize(&host); - } + gfx::Size GetPreferredSize() { return layout_->GetPreferredSize(&host_); } - gfx::Size pref; - gfx::Rect bounds; - View host; - GridLayout layout; + View& host() { return host_; } + GridLayout* layout() { return layout_; } + + private: + View host_; + GridLayout* layout_; }; class GridLayoutAlignmentTest : public testing::Test { public: - GridLayoutAlignmentTest() : layout(&host) { - v1.SetPreferredSize(gfx::Size(10, 20)); + GridLayoutAlignmentTest() : layout_(GridLayout::CreateAndInstall(&host_)) { + v1_.SetPreferredSize(gfx::Size(10, 20)); } void RemoveAll() { - for (int i = host.child_count() - 1; i >= 0; i--) - host.RemoveChildView(host.child_at(i)); + for (int i = host_.child_count() - 1; i >= 0; i--) + host_.RemoveChildView(host_.child_at(i)); } void TestAlignment(GridLayout::Alignment alignment, gfx::Rect* bounds) { - ColumnSet* c1 = layout.AddColumnSet(0); + ColumnSet* c1 = layout_->AddColumnSet(0); c1->AddColumn(alignment, alignment, 1, GridLayout::USE_PREF, 0, 0); - layout.StartRow(1, 0); - layout.AddView(&v1); - gfx::Size pref = layout.GetPreferredSize(&host); + layout_->StartRow(1, 0); + layout_->AddView(&v1_); + gfx::Size pref = layout_->GetPreferredSize(&host_); EXPECT_EQ(gfx::Size(10, 20), pref); - host.SetBounds(0, 0, 100, 100); - layout.Layout(&host); - *bounds = v1.bounds(); + host_.SetBounds(0, 0, 100, 100); + layout_->Layout(&host_); + *bounds = v1_.bounds(); RemoveAll(); } - View host; - View v1; - GridLayout layout; + View& host() { return host_; } + GridLayout* layout() { return layout_; } + + private: + View host_; + View v1_; + GridLayout* layout_; }; TEST_F(GridLayoutAlignmentTest, Fill) { @@ -153,20 +157,20 @@ v1.SetPreferredSize(gfx::Size(10, 20)); View v2; v2.SetPreferredSize(gfx::Size(20, 20)); - ColumnSet* c1 = layout.AddColumnSet(0); + ColumnSet* c1 = layout()->AddColumnSet(0); c1->AddColumn(GridLayout::LEADING, GridLayout::LEADING, 0, GridLayout::USE_PREF, 0, 0); c1->AddColumn(GridLayout::LEADING, GridLayout::LEADING, 0, GridLayout::USE_PREF, 0, 0); - layout.StartRow(0, 0); - layout.AddView(&v1); - layout.AddView(&v2); + layout()->StartRow(0, 0); + layout()->AddView(&v1); + layout()->AddView(&v2); - GetPreferredSize(); + gfx::Size pref = GetPreferredSize(); EXPECT_EQ(gfx::Size(30, 20), pref); - host.SetBounds(0, 0, pref.width(), pref.height()); - layout.Layout(&host); + host().SetBounds(0, 0, pref.width(), pref.height()); + layout()->Layout(&host()); ExpectViewBoundsEquals(0, 0, 10, 20, &v1); ExpectViewBoundsEquals(10, 0, 20, 20, &v2); @@ -181,7 +185,7 @@ v2.SetPreferredSize(gfx::Size(20, 20)); View v3; v3.SetPreferredSize(gfx::Size(0, 20)); - ColumnSet* c1 = layout.AddColumnSet(0); + ColumnSet* c1 = layout()->AddColumnSet(0); // Fill widths. c1->AddColumn(GridLayout::FILL, GridLayout::LEADING, 0, GridLayout::USE_PREF, @@ -191,29 +195,30 @@ c1->AddColumn(GridLayout::FILL, GridLayout::LEADING, 0, GridLayout::USE_PREF, 0, 0); - layout.StartRow(0, 0); - layout.AddView(&v1); - layout.AddView(&v2); - layout.AddView(&v3); + layout()->StartRow(0, 0); + layout()->AddView(&v1); + layout()->AddView(&v2); + layout()->AddView(&v3); // Link all the columns. c1->LinkColumnSizes(0, 1, 2, -1); - GetPreferredSize(); + gfx::Size pref = GetPreferredSize(); // |v1| and |v3| should obtain the same width as |v2|, since |v2| is largest. + pref = GetPreferredSize(); EXPECT_EQ(gfx::Size(20 + 20 + 20, 20), pref); - host.SetBounds(0, 0, pref.width(), pref.height()); - layout.Layout(&host); + host().SetBounds(0, 0, pref.width(), pref.height()); + layout()->Layout(&host()); ExpectViewBoundsEquals(0, 0, 20, 20, &v1); ExpectViewBoundsEquals(20, 0, 20, 20, &v2); ExpectViewBoundsEquals(40, 0, 20, 20, &v3); // If the limit is zero, behaves as though the columns are not linked. c1->set_linked_column_size_limit(0); - GetPreferredSize(); + pref = GetPreferredSize(); EXPECT_EQ(gfx::Size(10 + 20 + 0, 20), pref); - host.SetBounds(0, 0, pref.width(), pref.height()); - layout.Layout(&host); + host().SetBounds(0, 0, pref.width(), pref.height()); + layout()->Layout(&host()); ExpectViewBoundsEquals(0, 0, 10, 20, &v1); ExpectViewBoundsEquals(10, 0, 20, 20, &v2); ExpectViewBoundsEquals(30, 0, 0, 20, &v3); @@ -221,12 +226,12 @@ // Set a size limit. c1->set_linked_column_size_limit(40); v1.SetPreferredSize(gfx::Size(35, 20)); - GetPreferredSize(); // |v1| now dominates, but it is still below the limit. + pref = GetPreferredSize(); EXPECT_EQ(gfx::Size(35 + 35 + 35, 20), pref); - host.SetBounds(0, 0, pref.width(), pref.height()); - layout.Layout(&host); + host().SetBounds(0, 0, pref.width(), pref.height()); + layout()->Layout(&host()); ExpectViewBoundsEquals(0, 0, 35, 20, &v1); ExpectViewBoundsEquals(35, 0, 35, 20, &v2); ExpectViewBoundsEquals(70, 0, 35, 20, &v3); @@ -234,10 +239,10 @@ // Go over the limit. |v1| shouldn't influence size at all, but the others // should still be linked to the next largest width. v1.SetPreferredSize(gfx::Size(45, 20)); - GetPreferredSize(); + pref = GetPreferredSize(); EXPECT_EQ(gfx::Size(45 + 20 + 20, 20), pref); - host.SetBounds(0, 0, pref.width(), pref.height()); - layout.Layout(&host); + host().SetBounds(0, 0, pref.width(), pref.height()); + layout()->Layout(&host()); ExpectViewBoundsEquals(0, 0, 45, 20, &v1); ExpectViewBoundsEquals(45, 0, 20, 20, &v2); ExpectViewBoundsEquals(65, 0, 20, 20, &v3); @@ -250,21 +255,21 @@ v1.SetPreferredSize(gfx::Size(100, 20)); View v2; v2.SetPreferredSize(gfx::Size(10, 40)); - ColumnSet* c1 = layout.AddColumnSet(0); + ColumnSet* c1 = layout()->AddColumnSet(0); c1->AddColumn(GridLayout::LEADING, GridLayout::LEADING, 0, GridLayout::USE_PREF, 0, 0); c1->AddColumn(GridLayout::LEADING, GridLayout::LEADING, 1, GridLayout::USE_PREF, 0, 0); - layout.StartRow(0, 0); - layout.AddView(&v1, 2, 1); - layout.StartRow(0, 0); - layout.AddView(&v2); + layout()->StartRow(0, 0); + layout()->AddView(&v1, 2, 1); + layout()->StartRow(0, 0); + layout()->AddView(&v2); - GetPreferredSize(); + gfx::Size pref = GetPreferredSize(); EXPECT_EQ(gfx::Size(100, 60), pref); - host.SetBounds(0, 0, pref.width(), pref.height()); - layout.Layout(&host); + host().SetBounds(0, 0, pref.width(), pref.height()); + layout()->Layout(&host()); ExpectViewBoundsEquals(0, 0, 100, 20, &v1); ExpectViewBoundsEquals(0, 20, 10, 40, &v2); @@ -276,22 +281,22 @@ v1.SetPreferredSize(gfx::Size(100, 20)); View v2; v2.SetPreferredSize(gfx::Size(10, 20)); - ColumnSet* c1 = layout.AddColumnSet(0); + ColumnSet* c1 = layout()->AddColumnSet(0); c1->AddColumn(GridLayout::LEADING, GridLayout::LEADING, 1, GridLayout::USE_PREF, 0, 0); c1->AddColumn(GridLayout::LEADING, GridLayout::LEADING, 0, GridLayout::USE_PREF, 0, 0); - layout.StartRow(0, 0); - layout.AddView(&v1, 2, 1); - layout.StartRow(0, 0); - layout.SkipColumns(1); - layout.AddView(&v2); + layout()->StartRow(0, 0); + layout()->AddView(&v1, 2, 1); + layout()->StartRow(0, 0); + layout()->SkipColumns(1); + layout()->AddView(&v2); - GetPreferredSize(); + gfx::Size pref = GetPreferredSize(); EXPECT_EQ(gfx::Size(100, 40), pref); - host.SetBounds(0, 0, pref.width(), pref.height()); - layout.Layout(&host); + host().SetBounds(0, 0, pref.width(), pref.height()); + layout()->Layout(&host()); ExpectViewBoundsEquals(0, 0, 100, 20, &v1); ExpectViewBoundsEquals(90, 20, 10, 20, &v2); @@ -305,22 +310,22 @@ v2.SetPreferredSize(gfx::Size(10, 20)); View v3; v3.SetPreferredSize(gfx::Size(10, 20)); - ColumnSet* c1 = layout.AddColumnSet(0); + ColumnSet* c1 = layout()->AddColumnSet(0); c1->AddColumn(GridLayout::LEADING, GridLayout::LEADING, 0, GridLayout::USE_PREF, 0, 0); c1->AddColumn(GridLayout::LEADING, GridLayout::LEADING, 0, GridLayout::USE_PREF, 0, 0); - layout.StartRow(0, 0); - layout.AddView(&v1, 2, 1); - layout.StartRow(0, 0); - layout.AddView(&v2); - layout.AddView(&v3); + layout()->StartRow(0, 0); + layout()->AddView(&v1, 2, 1); + layout()->StartRow(0, 0); + layout()->AddView(&v2); + layout()->AddView(&v3); - GetPreferredSize(); + gfx::Size pref = GetPreferredSize(); EXPECT_EQ(gfx::Size(100, 40), pref); - host.SetBounds(0, 0, pref.width(), pref.height()); - layout.Layout(&host); + host().SetBounds(0, 0, pref.width(), pref.height()); + layout()->Layout(&host()); ExpectViewBoundsEquals(0, 0, 100, 20, &v1); ExpectViewBoundsEquals(0, 20, 10, 20, &v2); ExpectViewBoundsEquals(50, 20, 10, 20, &v3); @@ -330,7 +335,7 @@ TEST_F(GridLayoutTest, ColSpan4) { - ColumnSet* set = layout.AddColumnSet(0); + ColumnSet* set = layout()->AddColumnSet(0); set->AddColumn(GridLayout::LEADING, GridLayout::LEADING, 0, GridLayout::USE_PREF, 0, 0); @@ -343,17 +348,17 @@ v2.SetPreferredSize(gfx::Size(10, 10)); View v3; v3.SetPreferredSize(gfx::Size(25, 20)); - layout.StartRow(0, 0); - layout.AddView(&v1); - layout.AddView(&v2); - layout.StartRow(0, 0); - layout.AddView(&v3, 2, 1); + layout()->StartRow(0, 0); + layout()->AddView(&v1); + layout()->AddView(&v2); + layout()->StartRow(0, 0); + layout()->AddView(&v3, 2, 1); - GetPreferredSize(); + gfx::Size pref = GetPreferredSize(); EXPECT_EQ(gfx::Size(25, 30), pref); - host.SetBounds(0, 0, pref.width(), pref.height()); - layout.Layout(&host); + host().SetBounds(0, 0, pref.width(), pref.height()); + layout()->Layout(&host()); ExpectViewBoundsEquals(0, 0, 10, 10, &v1); ExpectViewBoundsEquals(12, 0, 10, 10, &v2); ExpectViewBoundsEquals(0, 10, 25, 20, &v3); @@ -364,7 +369,7 @@ // Verifies the sizing of a view that doesn't start in the first column // and has a column span > 1 (crbug.com/254092). TEST_F(GridLayoutTest, ColSpanStartSecondColumn) { - ColumnSet* set = layout.AddColumnSet(0); + ColumnSet* set = layout()->AddColumnSet(0); set->AddColumn(GridLayout::FILL, GridLayout::FILL, 0, GridLayout::USE_PREF, 0, 0); @@ -378,15 +383,15 @@ View v2; v2.SetPreferredSize(gfx::Size(20, 10)); - layout.StartRow(0, 0); - layout.AddView(&v1); - layout.AddView(&v2, 2, 1); + layout()->StartRow(0, 0); + layout()->AddView(&v1); + layout()->AddView(&v2, 2, 1); - GetPreferredSize(); + gfx::Size pref = GetPreferredSize(); EXPECT_EQ(gfx::Size(30, 10), pref); - host.SetBounds(0, 0, pref.width(), pref.height()); - layout.Layout(&host); + host().SetBounds(0, 0, pref.width(), pref.height()); + layout()->Layout(&host()); ExpectViewBoundsEquals(0, 0, 10, 10, &v1); ExpectViewBoundsEquals(10, 0, 20, 10, &v2); @@ -398,21 +403,21 @@ v1.SetPreferredSize(gfx::Size(50, 20)); View v2; v2.SetPreferredSize(gfx::Size(10, 10)); - ColumnSet* c1 = layout.AddColumnSet(0); + ColumnSet* c1 = layout()->AddColumnSet(0); c1->AddColumn(GridLayout::LEADING, GridLayout::LEADING, 0, GridLayout::USE_PREF, 0, 0); c1->AddColumn(GridLayout::LEADING, GridLayout::LEADING, 0, GridLayout::USE_PREF, 0, 0); c1->LinkColumnSizes(0, 1, -1); - layout.StartRow(0, 0); - layout.AddView(&v1); - layout.AddView(&v2); + layout()->StartRow(0, 0); + layout()->AddView(&v1); + layout()->AddView(&v2); - gfx::Size pref = layout.GetPreferredSize(&host); + gfx::Size pref = GetPreferredSize(); EXPECT_EQ(gfx::Size(100, 20), pref); - host.SetBounds(0, 0, pref.width(), pref.height()); - layout.Layout(&host); + host().SetBounds(0, 0, pref.width(), pref.height()); + layout()->Layout(&host()); ExpectViewBoundsEquals(0, 0, 50, 20, &v1); ExpectViewBoundsEquals(50, 0, 10, 10, &v2); @@ -424,17 +429,17 @@ v1.SetPreferredSize(gfx::Size(50, 20)); View v2; v2.SetPreferredSize(gfx::Size(10, 10)); - ColumnSet* c1 = layout.AddColumnSet(0); + ColumnSet* c1 = layout()->AddColumnSet(0); c1->AddColumn(GridLayout::FILL, GridLayout::LEADING, 1, GridLayout::USE_PREF, 0, 0); c1->AddColumn(GridLayout::LEADING, GridLayout::LEADING, 0, GridLayout::USE_PREF, 0, 0); - layout.StartRow(0, 0); - layout.AddView(&v1); - layout.AddView(&v2); + layout()->StartRow(0, 0); + layout()->AddView(&v1); + layout()->AddView(&v2); - host.SetBounds(0, 0, 110, 20); - layout.Layout(&host); + host().SetBounds(0, 0, 110, 20); + layout()->Layout(&host()); ExpectViewBoundsEquals(0, 0, 100, 20, &v1); ExpectViewBoundsEquals(100, 0, 10, 10, &v2); @@ -446,17 +451,17 @@ v1.SetPreferredSize(gfx::Size(50, 20)); View v2; v2.SetPreferredSize(gfx::Size(10, 10)); - ColumnSet* c1 = layout.AddColumnSet(0); + ColumnSet* c1 = layout()->AddColumnSet(0); c1->AddColumn(GridLayout::FILL, GridLayout::LEADING, 1, GridLayout::USE_PREF, 0, 0); c1->AddColumn(GridLayout::TRAILING, GridLayout::LEADING, 1, GridLayout::USE_PREF, 0, 0); - layout.StartRow(0, 0); - layout.AddView(&v1); - layout.AddView(&v2); + layout()->StartRow(0, 0); + layout()->AddView(&v1); + layout()->AddView(&v2); - host.SetBounds(0, 0, 120, 20); - layout.Layout(&host); + host().SetBounds(0, 0, 120, 20); + layout()->Layout(&host()); ExpectViewBoundsEquals(0, 0, 80, 20, &v1); ExpectViewBoundsEquals(110, 0, 10, 10, &v2); @@ -472,20 +477,20 @@ v2.SetPreferredSize(gfx::Size(10, 10)); View v3; v3.SetPreferredSize(gfx::Size(10, 10)); - ColumnSet* c1 = layout.AddColumnSet(0); + ColumnSet* c1 = layout()->AddColumnSet(0); c1->AddColumn(GridLayout::FILL, GridLayout::LEADING, 1, GridLayout::USE_PREF, 0, 0); c1->AddColumn(GridLayout::FILL, GridLayout::LEADING, 1, GridLayout::USE_PREF, 0, 0); c1->AddColumn(GridLayout::TRAILING, GridLayout::LEADING, 0, GridLayout::USE_PREF, 0, 0); - layout.StartRow(0, 0); - layout.AddView(&v1); - layout.AddView(&v2); - layout.AddView(&v3); + layout()->StartRow(0, 0); + layout()->AddView(&v1); + layout()->AddView(&v2); + layout()->AddView(&v3); - host.SetBounds(0, 0, 31, 10); - layout.Layout(&host); + host().SetBounds(0, 0, 31, 10); + layout()->Layout(&host()); ExpectViewBoundsEquals(0, 0, 10, 10, &v1); ExpectViewBoundsEquals(10, 0, 11, 10, &v2); ExpectViewBoundsEquals(21, 0, 10, 10, &v3); @@ -498,19 +503,19 @@ v1.SetPreferredSize(gfx::Size(50, 20)); View v2; v2.SetPreferredSize(gfx::Size(10, 10)); - ColumnSet* c1 = layout.AddColumnSet(0); + ColumnSet* c1 = layout()->AddColumnSet(0); c1->AddColumn(GridLayout::FILL, GridLayout::FILL, 1, GridLayout::USE_PREF, 0, 0); - layout.StartRow(1, 0); - layout.AddView(&v1); - layout.StartRow(0, 0); - layout.AddView(&v2); + layout()->StartRow(1, 0); + layout()->AddView(&v1); + layout()->StartRow(0, 0); + layout()->AddView(&v2); - GetPreferredSize(); + gfx::Size pref = GetPreferredSize(); EXPECT_EQ(gfx::Size(50, 30), pref); - host.SetBounds(0, 0, 50, 100); - layout.Layout(&host); + host().SetBounds(0, 0, 50, 100); + layout()->Layout(&host()); ExpectViewBoundsEquals(0, 0, 50, 90, &v1); ExpectViewBoundsEquals(0, 90, 50, 10, &v2); @@ -518,29 +523,29 @@ } TEST_F(GridLayoutTest, Border) { - host.SetBorder(CreateEmptyBorder(1, 2, 3, 4)); + host().SetBorder(CreateEmptyBorder(1, 2, 3, 4)); View v1; v1.SetPreferredSize(gfx::Size(10, 20)); - ColumnSet* c1 = layout.AddColumnSet(0); + ColumnSet* c1 = layout()->AddColumnSet(0); c1->AddColumn(GridLayout::LEADING, GridLayout::LEADING, 0, GridLayout::USE_PREF, 0, 0); - layout.StartRow(0, 0); - layout.AddView(&v1); + layout()->StartRow(0, 0); + layout()->AddView(&v1); - GetPreferredSize(); + gfx::Size pref = GetPreferredSize(); EXPECT_EQ(gfx::Size(16, 24), pref); - host.SetBounds(0, 0, pref.width(), pref.height()); - layout.Layout(&host); + host().SetBounds(0, 0, pref.width(), pref.height()); + layout()->Layout(&host()); ExpectViewBoundsEquals(2, 1, 10, 20, &v1); RemoveAll(); } TEST_F(GridLayoutTest, FixedSize) { - host.SetBorder(CreateEmptyBorder(2, 2, 2, 2)); + host().SetBorder(CreateEmptyBorder(2, 2, 2, 2)); - ColumnSet* set = layout.AddColumnSet(0); + ColumnSet* set = layout()->AddColumnSet(0); int column_count = 4; int title_width = 100; @@ -558,17 +563,17 @@ } for (int row = 0; row < row_count; ++row) { - layout.StartRow(0, 0); + layout()->StartRow(0, 0); for (int col = 0; col < column_count; ++col) { - layout.AddView(CreateSizedView(gfx::Size(pref_width, pref_height))); + layout()->AddView(CreateSizedView(gfx::Size(pref_width, pref_height))); } } - layout.Layout(&host); + layout()->Layout(&host()); for (int i = 0; i < column_count; ++i) { for (int row = 0; row < row_count; ++row) { - View* view = host.child_at(row * column_count + i); + View* view = host().child_at(row * column_count + i); ExpectViewBoundsEquals( 2 + title_width * i + (title_width - pref_width) / 2, 2 + pref_height * row, @@ -577,13 +582,13 @@ } } - GetPreferredSize(); + gfx::Size pref = GetPreferredSize(); EXPECT_EQ(gfx::Size(column_count * title_width + 4, row_count * pref_height + 4), pref); } TEST_F(GridLayoutTest, RowSpanWithPaddingRow) { - ColumnSet* set = layout.AddColumnSet(0); + ColumnSet* set = layout()->AddColumnSet(0); set->AddColumn(GridLayout::CENTER, GridLayout::CENTER, @@ -592,13 +597,13 @@ 10, 10); - layout.StartRow(0, 0); - layout.AddView(CreateSizedView(gfx::Size(10, 10)), 1, 2); - layout.AddPaddingRow(0, 10); + layout()->StartRow(0, 0); + layout()->AddView(CreateSizedView(gfx::Size(10, 10)), 1, 2); + layout()->AddPaddingRow(0, 10); } TEST_F(GridLayoutTest, RowSpan) { - ColumnSet* set = layout.AddColumnSet(0); + ColumnSet* set = layout()->AddColumnSet(0); set->AddColumn(GridLayout::LEADING, GridLayout::LEADING, @@ -613,112 +618,114 @@ 0, 0); - layout.StartRow(0, 0); - layout.AddView(CreateSizedView(gfx::Size(20, 10))); - layout.AddView(CreateSizedView(gfx::Size(20, 40)), 1, 2); - layout.StartRow(1, 0); + layout()->StartRow(0, 0); + layout()->AddView(CreateSizedView(gfx::Size(20, 10))); + layout()->AddView(CreateSizedView(gfx::Size(20, 40)), 1, 2); + layout()->StartRow(1, 0); View* s3 = CreateSizedView(gfx::Size(20, 10)); - layout.AddView(s3); + layout()->AddView(s3); - GetPreferredSize(); + gfx::Size pref = GetPreferredSize(); EXPECT_EQ(gfx::Size(40, 40), pref); - host.SetBounds(0, 0, pref.width(), pref.height()); - layout.Layout(&host); + host().SetBounds(0, 0, pref.width(), pref.height()); + layout()->Layout(&host()); ExpectViewBoundsEquals(0, 10, 20, 10, s3); } TEST_F(GridLayoutTest, RowSpan2) { - ColumnSet* set = layout.AddColumnSet(0); + ColumnSet* set = layout()->AddColumnSet(0); set->AddColumn(GridLayout::LEADING, GridLayout::LEADING, 0, GridLayout::USE_PREF, 0, 0); set->AddColumn(GridLayout::LEADING, GridLayout::LEADING, 0,GridLayout::USE_PREF, 0, 0); - layout.StartRow(0, 0); - layout.AddView(CreateSizedView(gfx::Size(20, 20))); + layout()->StartRow(0, 0); + layout()->AddView(CreateSizedView(gfx::Size(20, 20))); View* s3 = CreateSizedView(gfx::Size(64, 64)); - layout.AddView(s3, 1, 3); + layout()->AddView(s3, 1, 3); - layout.AddPaddingRow(0, 10); + layout()->AddPaddingRow(0, 10); - layout.StartRow(0, 0); - layout.AddView(CreateSizedView(gfx::Size(10, 20))); + layout()->StartRow(0, 0); + layout()->AddView(CreateSizedView(gfx::Size(10, 20))); - GetPreferredSize(); + gfx::Size pref = GetPreferredSize(); EXPECT_EQ(gfx::Size(84, 64), pref); - host.SetBounds(0, 0, pref.width(), pref.height()); - layout.Layout(&host); + host().SetBounds(0, 0, pref.width(), pref.height()); + layout()->Layout(&host()); ExpectViewBoundsEquals(20, 0, 64, 64, s3); } TEST_F(GridLayoutTest, FixedViewWidth) { - ColumnSet* set = layout.AddColumnSet(0); + ColumnSet* set = layout()->AddColumnSet(0); set->AddColumn(GridLayout::LEADING, GridLayout::LEADING, 0, GridLayout::USE_PREF, 0, 0); set->AddColumn(GridLayout::LEADING, GridLayout::LEADING, 0,GridLayout::USE_PREF, 0, 0); - layout.StartRow(0, 0); + layout()->StartRow(0, 0); View* view = CreateSizedView(gfx::Size(30, 40)); - layout.AddView(view, 1, 1, GridLayout::LEADING, GridLayout::LEADING, 10, 0); + layout()->AddView(view, 1, 1, GridLayout::LEADING, GridLayout::LEADING, 10, + 0); - GetPreferredSize(); + gfx::Size pref = GetPreferredSize(); EXPECT_EQ(10, pref.width()); EXPECT_EQ(40, pref.height()); - host.SetBounds(0, 0, pref.width(), pref.height()); - layout.Layout(&host); + host().SetBounds(0, 0, pref.width(), pref.height()); + layout()->Layout(&host()); ExpectViewBoundsEquals(0, 0, 10, 40, view); } TEST_F(GridLayoutTest, FixedViewHeight) { - ColumnSet* set = layout.AddColumnSet(0); + ColumnSet* set = layout()->AddColumnSet(0); set->AddColumn(GridLayout::LEADING, GridLayout::LEADING, 0, GridLayout::USE_PREF, 0, 0); set->AddColumn(GridLayout::LEADING, GridLayout::LEADING, 0,GridLayout::USE_PREF, 0, 0); - layout.StartRow(0, 0); + layout()->StartRow(0, 0); View* view = CreateSizedView(gfx::Size(30, 40)); - layout.AddView(view, 1, 1, GridLayout::LEADING, GridLayout::LEADING, 0, 10); + layout()->AddView(view, 1, 1, GridLayout::LEADING, GridLayout::LEADING, 0, + 10); - GetPreferredSize(); + gfx::Size pref = GetPreferredSize(); EXPECT_EQ(30, pref.width()); EXPECT_EQ(10, pref.height()); - host.SetBounds(0, 0, pref.width(), pref.height()); - layout.Layout(&host); + host().SetBounds(0, 0, pref.width(), pref.height()); + layout()->Layout(&host()); ExpectViewBoundsEquals(0, 0, 30, 10, view); } // Make sure that for views that span columns the underlying columns are resized // based on the resize percent of the column. TEST_F(GridLayoutTest, ColumnSpanResizing) { - ColumnSet* set = layout.AddColumnSet(0); + ColumnSet* set = layout()->AddColumnSet(0); set->AddColumn(GridLayout::FILL, GridLayout::CENTER, 2, GridLayout::USE_PREF, 0, 0); set->AddColumn(GridLayout::FILL, GridLayout::CENTER, 4, GridLayout::USE_PREF, 0, 0); - layout.StartRow(0, 0); + layout()->StartRow(0, 0); // span_view spans two columns and is twice as big the views added below. View* span_view = CreateSizedView(gfx::Size(12, 40)); - layout.AddView(span_view, 2, 1, GridLayout::LEADING, GridLayout::LEADING); + layout()->AddView(span_view, 2, 1, GridLayout::LEADING, GridLayout::LEADING); - layout.StartRow(0, 0); + layout()->StartRow(0, 0); View* view1 = CreateSizedView(gfx::Size(2, 40)); View* view2 = CreateSizedView(gfx::Size(4, 40)); - layout.AddView(view1); - layout.AddView(view2); + layout()->AddView(view1); + layout()->AddView(view2); - host.SetBounds(0, 0, 12, 80); - layout.Layout(&host); + host().SetBounds(0, 0, 12, 80); + layout()->Layout(&host()); ExpectViewBoundsEquals(0, 0, 12, 40, span_view); @@ -736,55 +743,55 @@ // there is additional space in the case we have column sets of different // preferred sizes. TEST_F(GridLayoutTest, ColumnResizingOnGetPreferredSize) { - ColumnSet* set = layout.AddColumnSet(0); + ColumnSet* set = layout()->AddColumnSet(0); set->AddColumn(GridLayout::FILL, GridLayout::CENTER, 1, GridLayout::USE_PREF, 0, 0); - set = layout.AddColumnSet(1); + set = layout()->AddColumnSet(1); set->AddColumn(GridLayout::FILL, GridLayout::CENTER, 1, GridLayout::USE_PREF, 0, 0); - set = layout.AddColumnSet(2); + set = layout()->AddColumnSet(2); set->AddColumn(GridLayout::FILL, GridLayout::CENTER, 1, GridLayout::USE_PREF, 0, 0); // Make a row containing a flexible view that trades width for height. - layout.StartRow(0, 0); + layout()->StartRow(0, 0); View* view1 = new FlexibleView(100); - layout.AddView(view1, 1, 1, GridLayout::FILL, GridLayout::LEADING); + layout()->AddView(view1, 1, 1, GridLayout::FILL, GridLayout::LEADING); // The second row contains a view of fixed size that will enforce a column // width of 20 pixels. - layout.StartRow(0, 1); + layout()->StartRow(0, 1); View* view2 = CreateSizedView(gfx::Size(20, 20)); - layout.AddView(view2, 1, 1, GridLayout::FILL, GridLayout::LEADING); + layout()->AddView(view2, 1, 1, GridLayout::FILL, GridLayout::LEADING); // Add another flexible view in row three in order to ensure column set // ordering doesn't influence sizing behaviour. - layout.StartRow(0, 2); + layout()->StartRow(0, 2); View* view3 = new FlexibleView(40); - layout.AddView(view3, 1, 1, GridLayout::FILL, GridLayout::LEADING); + layout()->AddView(view3, 1, 1, GridLayout::FILL, GridLayout::LEADING); // We expect a height of 50: 30 from the variable width view in the first row // plus 20 from the statically sized view in the second row. The flexible // view in the third row should contribute no height. - EXPECT_EQ(gfx::Size(20, 50), layout.GetPreferredSize(&host)); + EXPECT_EQ(gfx::Size(20, 50), GetPreferredSize()); } TEST_F(GridLayoutTest, MinimumPreferredSize) { View v1; v1.SetPreferredSize(gfx::Size(10, 20)); - ColumnSet* set = layout.AddColumnSet(0); + ColumnSet* set = layout()->AddColumnSet(0); set->AddColumn(GridLayout::FILL, GridLayout::FILL, 0, GridLayout::USE_PREF, 0, 0); - layout.StartRow(0, 0); - layout.AddView(&v1); + layout()->StartRow(0, 0); + layout()->AddView(&v1); - GetPreferredSize(); + gfx::Size pref = GetPreferredSize(); EXPECT_EQ(gfx::Size(10, 20), pref); - layout.set_minimum_size(gfx::Size(40, 40)); - GetPreferredSize(); + layout()->set_minimum_size(gfx::Size(40, 40)); + pref = GetPreferredSize(); EXPECT_EQ(gfx::Size(40, 40), pref); RemoveAll(); @@ -801,22 +808,18 @@ if (PlatformTestHelper::IsMus()) return; - // Don't use the |layout| data member from the test harness, otherwise - // SetLayoutManager() can take not take ownership. - GridLayout* grid_layout = new GridLayout(&host); - host.SetLayoutManager(grid_layout); - ColumnSet* set = grid_layout->AddColumnSet(0); + ColumnSet* set = layout()->AddColumnSet(0); set->AddColumn(GridLayout::FILL, GridLayout::FILL, 0, GridLayout::USE_PREF, 0, 0); - grid_layout->StartRow(0, 0); + layout()->StartRow(0, 0); LayoutOnAddView view; - EXPECT_DCHECK_DEATH(grid_layout->AddView(&view)); + EXPECT_DCHECK_DEATH(layout()->AddView(&view)); // Death tests use fork(), so nothing should be added here. EXPECT_FALSE(view.parent()); // If the View has nothing to change, adding should succeed. view.set_target_size(view.GetPreferredSize()); - grid_layout->AddView(&view); + layout()->AddView(&view); EXPECT_TRUE(view.parent()); RemoveAll();
diff --git a/ui/views/win/fullscreen_handler.cc b/ui/views/win/fullscreen_handler.cc index 6cbe30a..7f13e846 100644 --- a/ui/views/win/fullscreen_handler.cc +++ b/ui/views/win/fullscreen_handler.cc
@@ -29,6 +29,24 @@ SetFullscreenImpl(fullscreen); } +void FullscreenHandler::MarkFullscreen(bool fullscreen) { + if (!task_bar_list_) { + HRESULT hr = + ::CoCreateInstance(CLSID_TaskbarList, NULL, CLSCTX_INPROC_SERVER, + IID_PPV_ARGS(&task_bar_list_)); + if (SUCCEEDED(hr) && FAILED(task_bar_list_->HrInit())) + task_bar_list_ = nullptr; + } + + // As per MSDN marking the window as fullscreen should ensure that the + // taskbar is moved to the bottom of the Z-order when the fullscreen window + // is activated. If the window is not fullscreen, the Shell falls back to + // heuristics to determine how the window should be treated, which means + // that it could still consider the window as fullscreen. :( + if (task_bar_list_) + task_bar_list_->MarkFullscreenWindow(hwnd_, !!fullscreen); +} + gfx::Rect FullscreenHandler::GetRestoreBounds() const { return gfx::Rect(saved_window_info_.window_rect); } @@ -86,21 +104,7 @@ SWP_NOZORDER | SWP_NOACTIVATE | SWP_FRAMECHANGED); } - if (!task_bar_list_) { - HRESULT hr = ::CoCreateInstance(CLSID_TaskbarList, NULL, - CLSCTX_INPROC_SERVER, - IID_PPV_ARGS(&task_bar_list_)); - if (SUCCEEDED(hr) && FAILED(task_bar_list_->HrInit())) - task_bar_list_ = nullptr; - } - - // As per MSDN marking the window as fullscreen should ensure that the - // taskbar is moved to the bottom of the Z-order when the fullscreen window - // is activated. If the window is not fullscreen, the Shell falls back to - // heuristics to determine how the window should be treated, which means - // that it could still consider the window as fullscreen. :( - if (task_bar_list_) - task_bar_list_->MarkFullscreenWindow(hwnd_, !!fullscreen); + MarkFullscreen(fullscreen); } } // namespace views
diff --git a/ui/views/win/fullscreen_handler.h b/ui/views/win/fullscreen_handler.h index 772d275..62eb9b4 100644 --- a/ui/views/win/fullscreen_handler.h +++ b/ui/views/win/fullscreen_handler.h
@@ -27,6 +27,9 @@ void SetFullscreen(bool fullscreen); + // Informs the taskbar whether the window is a fullscreen window. + void MarkFullscreen(bool fullscreen); + gfx::Rect GetRestoreBounds() const; bool fullscreen() const { return fullscreen_; }
diff --git a/ui/views/win/hwnd_message_handler.cc b/ui/views/win/hwnd_message_handler.cc index 451a5c06..b611fa18 100644 --- a/ui/views/win/hwnd_message_handler.cc +++ b/ui/views/win/hwnd_message_handler.cc
@@ -1139,6 +1139,11 @@ GetMonitorInfo(MonitorFromWindow(hwnd(), MONITOR_DEFAULTTOPRIMARY), &monitor_info); SetBoundsInternal(gfx::Rect(monitor_info.rcMonitor), false); + // Inform the taskbar that this window is now a fullscreen window so it go + // behind the window in the Z-Order. The taskbar heuristics to detect + // fullscreen windows are not reliable. Marking it explicitly seems to work + // around these problems. + fullscreen_handler()->MarkFullscreen(true); background_fullscreen_hack_ = false; } else { // If the window becoming active has a fullscreen window on the same @@ -3005,6 +3010,11 @@ shrunk_rect.set_height(shrunk_rect.height() - 1); background_fullscreen_hack_ = true; SetBoundsInternal(shrunk_rect, false); + // Inform the taskbar that this window is no longer a fullscreen window so it + // can bring itself to the top of the Z-Order. The taskbar heuristics to + // detect fullscreen windows are not reliable. Marking it explicitly seems to + // work around these problems. + fullscreen_handler()->MarkFullscreen(false); } void HWNDMessageHandler::DestroyAXSystemCaret() {
diff --git a/ui/views/window/dialog_client_view.cc b/ui/views/window/dialog_client_view.cc index 256ba5dc..7cd77e42 100644 --- a/ui/views/window/dialog_client_view.cc +++ b/ui/views/window/dialog_client_view.cc
@@ -326,14 +326,14 @@ void DialogClientView::SetupLayout() { base::AutoReset<bool> auto_reset(&adding_or_removing_views_, true); - GridLayout* layout = new GridLayout(button_row_container_); - layout->set_minimum_size(minimum_size_); FocusManager* focus_manager = GetFocusManager(); ViewTracker view_tracker(focus_manager->GetFocusedView()); // Clobber any existing LayoutManager since it has weak references to child // Views which may be removed by SetupViews(). - button_row_container_->SetLayoutManager(layout); + GridLayout* layout = GridLayout::CreateAndInstall(button_row_container_); + layout->set_minimum_size(minimum_size_); + SetupViews(); const std::array<View*, kNumButtons> views = GetButtonRowViews();