diff --git a/DEPS b/DEPS index 25f7563..417ffde 100644 --- a/DEPS +++ b/DEPS
@@ -208,7 +208,7 @@ # Three lines of non-changing comments so that # the commit queue can handle CLs rolling V8 # and whatever else without interference from each other. - 'v8_revision': '929acfd81ac536f1b7d5699427a01e1238efabd4', + 'v8_revision': 'f53ac97298868f535ef84b516d80ebb6d41c67eb', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling swarming_client # and whatever else without interference from each other. @@ -216,11 +216,11 @@ # Three lines of non-changing comments so that # the commit queue can handle CLs rolling ANGLE # and whatever else without interference from each other. - 'angle_revision': '79e39478028c56b4be4e4549f8c9e53381ae48ce', + 'angle_revision': 'c99e405cc2b3f1c358bf2e594ba8c9d4658bc82c', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling SwiftShader # and whatever else without interference from each other. - 'swiftshader_revision': '69b79eec6533f113920a5dd863ee2dea7d0c64e7', + 'swiftshader_revision': 'a0aeb64e01d75b257495eda180bea9758d072005', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling PDFium # and whatever else without interference from each other. @@ -283,7 +283,7 @@ # Three lines of non-changing comments so that # the commit queue can handle CLs rolling devtools-frontend # and whatever else without interference from each other. - 'devtools_frontend_revision': '92feb60a1acd5fb8a9be021111df077d3d29bc2d', + 'devtools_frontend_revision': '695ff14eb349e747ed16e3ca89265f41b883936d', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling libprotobuf-mutator # and whatever else without interference from each other. @@ -323,7 +323,7 @@ # Three lines of non-changing comments so that # the commit queue can handle CLs rolling feed # and whatever else without interference from each other. - 'dawn_revision': '54586e61210cfdfc9835f81b57c794f4ac19356b', + 'dawn_revision': 'cc84ee24fc89229d956a6cd1a8ed0f7fd7528c1b', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling feed # and whatever else without interference from each other. @@ -379,8 +379,7 @@ # Three lines of non-changing comments so that # the commit queue can handle CLs rolling feed # and whatever else without interference from each other. - # TODO(crbug.com/1166332) rename to clang_format_revision. - 'clang_fmt_revision': '99803d74e35962f63a775f29477882afd4d57d94', + 'clang_format_revision': '99803d74e35962f63a775f29477882afd4d57d94', # If you change this, also update the libc++ revision in # //buildtools/deps_revisions.gni. @@ -411,7 +410,7 @@ 'src/buildtools/clang_format/script': Var('chromium_git') + '/external/github.com/llvm/llvm-project/clang/tools/clang-format.git@' + - Var('clang_fmt_revision'), + Var('clang_format_revision'), 'src/buildtools/linux64': { 'packages': [ { @@ -910,7 +909,7 @@ # Tools used when building Chrome for Chrome OS. This affects both the Simple # Chrome workflow, as well as the chromeos-chrome ebuild. 'src/third_party/chromite': { - 'url': Var('chromium_git') + '/chromiumos/chromite.git' + '@' + '16ecc391ff9a68eef9375cb5d7f7afca3c2b6aac', + 'url': Var('chromium_git') + '/chromiumos/chromite.git' + '@' + 'd93177cb824290cc917dd4309319995bdd34cf3a', 'condition': 'checkout_chromeos', }, @@ -930,7 +929,7 @@ }, 'src/third_party/depot_tools': - Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + '593a6b575b137c42c91ef2439dbf6c526e5c5980', + Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + '36de4be91ef66852c49aa5fd9e0cc31d5ec05ae9', 'src/third_party/devtools-frontend/src': Var('chromium_git') + '/devtools/devtools-frontend' + '@' + Var('devtools_frontend_revision'), @@ -1302,7 +1301,7 @@ }, 'src/third_party/perfetto': - Var('android_git') + '/platform/external/perfetto.git' + '@' + '6ecb51ac72ea4de3663b4442ad3e72b64f660af1', + Var('android_git') + '/platform/external/perfetto.git' + '@' + '3d4f2c26f0d21ae10266f3be3b15c8b7fcf0a6ed', 'src/third_party/perl': { 'url': Var('chromium_git') + '/chromium/deps/perl.git' + '@' + '6f3e5028eb65d0b4c5fdd792106ac4c84eee1eb3', @@ -1538,7 +1537,7 @@ Var('chromium_git') + '/external/github.com/gpuweb/cts.git' + '@' + '66460536ee975a3e98931b7b40a661a63fd9cd57', 'src/third_party/webrtc': - Var('webrtc_git') + '/src.git' + '@' + '6097b0fac0946a29d59a9266ea656c39b3fd7336', + Var('webrtc_git') + '/src.git' + '@' + 'ab6335041130fdc0365a604f49668eac9194f9d6', 'src/third_party/libgifcodec': Var('skia_git') + '/libgifcodec' + '@'+ Var('libgifcodec_revision'), @@ -1586,7 +1585,7 @@ 'packages': [ { 'package': 'skia/tools/goldctl/windows-amd64', - 'version': '5QaZrGtCcd9HFM-qeg4vP-CrEpvhqlZtkfwAtVBee2oC', + 'version': 'wr5p_MyXcHVxA-eHznijCeFaqR2HUo02Hw70e0CWCIoC', }, ], 'dep_type': 'cipd', @@ -1596,7 +1595,7 @@ 'packages': [ { 'package': 'skia/tools/goldctl/mac-amd64', - 'version': 'F9PFp7kNc3dn_X7b1PYmPsEb3VMHDY-8pXiZ7WNi09MC', + 'version': 'TPtcrf_XH3mB9pdK4W3N0QPZunsqJ08T7U7fWPvMnRAC', }, ], 'dep_type': 'cipd', @@ -1610,7 +1609,7 @@ Var('chromium_git') + '/v8/v8.git' + '@' + Var('v8_revision'), 'src-internal': { - 'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@e7c96aaf8edb25f561c2c8fc45bee7e2893a867b', + 'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@19b198ff86078b2069ba923779293444b57856a2', 'condition': 'checkout_src_internal', },
diff --git a/android_webview/browser/BUILD.gn b/android_webview/browser/BUILD.gn index 41991c8..5710717 100644 --- a/android_webview/browser/BUILD.gn +++ b/android_webview/browser/BUILD.gn
@@ -100,6 +100,10 @@ "aw_web_contents_view_delegate.h", "aw_web_ui_controller_factory.cc", "aw_web_ui_controller_factory.h", + "component_updater/loader_policies/origin_trials_component_loader_policy.cc", + "component_updater/loader_policies/origin_trials_component_loader_policy.h", + "component_updater/origin_trials_component_loader.cc", + "component_updater/origin_trials_component_loader.h", "component_updater/registration.cc", "component_updater/registration.h", "component_updater/trust_token_key_commitments_component_loader.cc", @@ -199,9 +203,11 @@ "//components/cdm/browser", "//components/component_updater/android:embedded_component_loader", "//components/component_updater/android:loader_policies", + "//components/component_updater/installer_policies", "//components/content_capture/android", "//components/content_capture/browser", "//components/embedder_support/android:util", + "//components/embedder_support/origin_trials", "//components/favicon_base:favicon_base", "//components/flags_ui", "//components/keyed_service/content",
diff --git a/android_webview/browser/DEPS b/android_webview/browser/DEPS index 528342ad..1c00556 100644 --- a/android_webview/browser/DEPS +++ b/android_webview/browser/DEPS
@@ -14,10 +14,12 @@ "+components/autofill/core/common", "+components/cdm/browser", "+components/component_updater/android", + "+components/component_updater/installer_policies", "+components/crash/content/browser", "+components/crash/core", "+components/download/public/common", "+components/embedder_support/android/metrics", + "+components/embedder_support/origin_trials", "+components/favicon_base", "+components/flags_ui", "+components/heap_profiling",
diff --git a/android_webview/browser/aw_feature_list_creator.cc b/android_webview/browser/aw_feature_list_creator.cc index c749c87a..8a330880 100644 --- a/android_webview/browser/aw_feature_list_creator.cc +++ b/android_webview/browser/aw_feature_list_creator.cc
@@ -29,6 +29,7 @@ #include "cc/base/switches.h" #include "components/autofill/core/common/autofill_prefs.h" #include "components/embedder_support/android/metrics/android_metrics_service_client.h" +#include "components/embedder_support/origin_trials/origin_trial_prefs.h" #include "components/metrics/metrics_pref_names.h" #include "components/metrics/persistent_histograms.h" #include "components/policy/core/browser/configuration_policy_pref_store.h" @@ -122,6 +123,7 @@ AwMetricsServiceClient::RegisterPrefs(pref_registry.get()); variations::VariationsService::RegisterPrefs(pref_registry.get()); + embedder_support::OriginTrialPrefs::RegisterPrefs(pref_registry.get()); AwBrowserProcess::RegisterNetworkContextLocalStatePrefs(pref_registry.get()); PrefServiceFactory pref_service_factory;
diff --git a/android_webview/browser/component_updater/loader_policies/origin_trials_component_loader_policy.cc b/android_webview/browser/component_updater/loader_policies/origin_trials_component_loader_policy.cc new file mode 100644 index 0000000..c917039f --- /dev/null +++ b/android_webview/browser/component_updater/loader_policies/origin_trials_component_loader_policy.cc
@@ -0,0 +1,49 @@ +// Copyright 2021 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 "android_webview/browser/component_updater/loader_policies/origin_trials_component_loader_policy.h" + +#include <stdint.h> + +#include <memory> +#include <string> +#include <vector> + +#include "android_webview/browser/aw_browser_process.h" +#include "base/containers/flat_map.h" +#include "base/values.h" +#include "base/version.h" +#include "components/component_updater/installer_policies/origin_trials_component_installer.h" +#include "components/embedder_support/origin_trials/component_updater_utils.h" + +namespace android_webview { + +OriginTrialsComponentLoaderPolicy::OriginTrialsComponentLoaderPolicy() = + default; + +OriginTrialsComponentLoaderPolicy::~OriginTrialsComponentLoaderPolicy() = + default; + +void OriginTrialsComponentLoaderPolicy::ComponentLoaded( + const base::Version& version, + const base::flat_map<std::string, int>& fd_map, + std::unique_ptr<base::DictionaryValue> manifest) { + // Read the configuration from the manifest and set values in browser + // local_state. These will be used on the next browser restart. + // If an individual configuration value is missing, treat as a reset to the + // browser defaults. + embedder_support::ReadOriginTrialsConfigAndPopulateLocalState( + android_webview::AwBrowserProcess::GetInstance()->local_state(), + std::move(manifest)); +} + +void OriginTrialsComponentLoaderPolicy::ComponentLoadFailed() {} + +void OriginTrialsComponentLoaderPolicy::GetHash( + std::vector<uint8_t>* hash) const { + component_updater::OriginTrialsComponentInstallerPolicy::GetComponentHash( + hash); +} + +} // namespace android_webview
diff --git a/android_webview/browser/component_updater/loader_policies/origin_trials_component_loader_policy.h b/android_webview/browser/component_updater/loader_policies/origin_trials_component_loader_policy.h new file mode 100644 index 0000000..f17f313 --- /dev/null +++ b/android_webview/browser/component_updater/loader_policies/origin_trials_component_loader_policy.h
@@ -0,0 +1,49 @@ +// Copyright 2021 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 ANDROID_WEBVIEW_BROWSER_COMPONENT_UPDATER_LOADER_POLICIES_ORIGIN_TRIALS_COMPONENT_LOADER_POLICY_H_ +#define ANDROID_WEBVIEW_BROWSER_COMPONENT_UPDATER_LOADER_POLICIES_ORIGIN_TRIALS_COMPONENT_LOADER_POLICY_H_ + +#include <stdint.h> + +#include <memory> +#include <string> +#include <vector> + +#include "base/containers/flat_map.h" +#include "components/component_updater/android/component_loader_policy.h" + +namespace base { +class DictionaryValue; +class Version; +} // namespace base + +namespace android_webview { + +// OriginTrialsComponentLoaderPolicy defines a loader responsible +// for receiving origin trials config. +class OriginTrialsComponentLoaderPolicy + : public component_updater::ComponentLoaderPolicy { + public: + OriginTrialsComponentLoaderPolicy(); + ~OriginTrialsComponentLoaderPolicy() override; + + OriginTrialsComponentLoaderPolicy(const OriginTrialsComponentLoaderPolicy&) = + delete; + OriginTrialsComponentLoaderPolicy& operator=( + const OriginTrialsComponentLoaderPolicy&) = delete; + + private: + // The following methods override ComponentLoaderPolicy. + void ComponentLoaded( + const base::Version& version, + const base::flat_map<std::string, int>& fd_map, + std::unique_ptr<base::DictionaryValue> manifest) override; + void ComponentLoadFailed() override; + void GetHash(std::vector<uint8_t>* hash) const override; +}; + +} // namespace android_webview + +#endif // ANDROID_WEBVIEW_BROWSER_COMPONENT_UPDATER_LOADER_POLICIES_ORIGIN_TRIALS_COMPONENT_LOADER_POLICY_H_ \ No newline at end of file
diff --git a/android_webview/browser/component_updater/origin_trials_component_loader.cc b/android_webview/browser/component_updater/origin_trials_component_loader.cc new file mode 100644 index 0000000..428d672 --- /dev/null +++ b/android_webview/browser/component_updater/origin_trials_component_loader.cc
@@ -0,0 +1,25 @@ +// Copyright 2021 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 "android_webview/browser/component_updater/origin_trials_component_loader.h" + +#include <memory> + +#include "android_webview/browser/component_updater/loader_policies/origin_trials_component_loader_policy.h" +#include "android_webview/common/aw_features.h" +#include "base/feature_list.h" +#include "components/component_updater/android/component_loader_policy.h" +#include "components/component_updater/android/component_loader_policy_forward.h" + +namespace android_webview { + +void LoadOriginTrialsComponent( + component_updater::ComponentLoaderPolicyVector& policies) { + if (!base::FeatureList::IsEnabled(features::kWebViewOriginTrials)) + return; + + policies.push_back(std::make_unique<OriginTrialsComponentLoaderPolicy>()); +} + +} // namespace android_webview
diff --git a/android_webview/browser/component_updater/origin_trials_component_loader.h b/android_webview/browser/component_updater/origin_trials_component_loader.h new file mode 100644 index 0000000..b3c13bb3 --- /dev/null +++ b/android_webview/browser/component_updater/origin_trials_component_loader.h
@@ -0,0 +1,23 @@ +// Copyright 2021 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 ANDROID_WEBVIEW_BROWSER_COMPONENT_UPDATER_ORIGIN_TRIALS_COMPONENT_LOADER_H_ +#define ANDROID_WEBVIEW_BROWSER_COMPONENT_UPDATER_ORIGIN_TRIALS_COMPONENT_LOADER_H_ + +#include "components/component_updater/android/component_loader_policy_forward.h" + +namespace component_updater { +class ComponentLoaderPolicy; +} // namespace component_updater + +namespace android_webview { + +// Adds origin trials ComponentLoaderPolicy to `policies`, if Origin Trials +// support is enabled. +void LoadOriginTrialsComponent( + component_updater::ComponentLoaderPolicyVector& policies); + +} // namespace android_webview + +#endif // ANDROID_WEBVIEW_BROWSER_COMPONENT_UPDATER_ORIGIN_TRIALS_COMPONENT_LOADER_H_
diff --git a/android_webview/browser/component_updater/registration.cc b/android_webview/browser/component_updater/registration.cc index 54fc5a7..574702eb 100644 --- a/android_webview/browser/component_updater/registration.cc +++ b/android_webview/browser/component_updater/registration.cc
@@ -4,13 +4,15 @@ #include "android_webview/browser/component_updater/registration.h" +#include "android_webview/browser/component_updater/origin_trials_component_loader.h" #include "android_webview/browser/component_updater/trust_token_key_commitments_component_loader.h" namespace android_webview { component_updater::ComponentLoaderPolicyVector GetComponentLoaderPolicies() { component_updater::ComponentLoaderPolicyVector policies; - LoadTrustTokenKeyCommitmentsComponent(&policies); + LoadTrustTokenKeyCommitmentsComponent(policies); + LoadOriginTrialsComponent(policies); return policies; }
diff --git a/android_webview/browser/component_updater/trust_token_key_commitments_component_loader.cc b/android_webview/browser/component_updater/trust_token_key_commitments_component_loader.cc index 3eedac6..48547c6 100644 --- a/android_webview/browser/component_updater/trust_token_key_commitments_component_loader.cc +++ b/android_webview/browser/component_updater/trust_token_key_commitments_component_loader.cc
@@ -21,7 +21,7 @@ // Add trust tokens ComponentLoaderPolicy to the given policies vector, if Trust // Tokens is enabled. void LoadTrustTokenKeyCommitmentsComponent( - ComponentLoaderPolicyVector* policies) { + ComponentLoaderPolicyVector& policies) { if (!base::FeatureList::IsEnabled(network::features::kTrustTokens)) return; @@ -29,7 +29,7 @@ << "Registering Trust Token Key Commitments component for loading in " "embedded WebView."; - policies->push_back( + policies.push_back( std::make_unique< component_updater::TrustTokenKeyCommitmentsComponentLoaderPolicy>( /* on_commitments_ready = */ base::BindRepeating(
diff --git a/android_webview/browser/component_updater/trust_token_key_commitments_component_loader.h b/android_webview/browser/component_updater/trust_token_key_commitments_component_loader.h index ccff9b8..2928bee 100644 --- a/android_webview/browser/component_updater/trust_token_key_commitments_component_loader.h +++ b/android_webview/browser/component_updater/trust_token_key_commitments_component_loader.h
@@ -17,7 +17,7 @@ std::vector<std::unique_ptr<component_updater::ComponentLoaderPolicy>>; void LoadTrustTokenKeyCommitmentsComponent( - ComponentLoaderPolicyVector* policies); + ComponentLoaderPolicyVector& policies); } // namespace android_webview
diff --git a/ash/accessibility/point_scan_controller.cc b/ash/accessibility/point_scan_controller.cc index 424dd508..9fd563b 100644 --- a/ash/accessibility/point_scan_controller.cc +++ b/ash/accessibility/point_scan_controller.cc
@@ -11,7 +11,7 @@ namespace { constexpr int kDefaultRangeWidthDips = 150; -constexpr float kDefaultRangeHeightDips = 120; +constexpr int kDefaultRangeHeightDips = 120; constexpr float kLineScanSlowDownFactor = 0.5f; } // namespace
diff --git a/ash/accessibility/point_scan_layer_animation_info.cc b/ash/accessibility/point_scan_layer_animation_info.cc index 613be15e..1b146ad4 100644 --- a/ash/accessibility/point_scan_layer_animation_info.cc +++ b/ash/accessibility/point_scan_layer_animation_info.cc
@@ -4,11 +4,16 @@ #include "ash/accessibility/point_scan_layer_animation_info.h" +namespace { +constexpr base::TimeDelta kLingerDelay = base::TimeDelta::FromMilliseconds(250); +} + namespace ash { void PointScanLayerAnimationInfo::Clear() { start_time = base::TimeTicks(); change_time = base::TimeTicks(); + linger_until = base::TimeTicks(); offset = 0; offset_bound = 0; offset_start = 0; @@ -19,19 +24,27 @@ if (timestamp < animation_info->start_time) timestamp = animation_info->start_time; - float change_delta = (timestamp - animation_info->start_time).InSecondsF(); + if (timestamp < animation_info->linger_until) + return; + + base::TimeTicks change_from_time = + std::max(animation_info->linger_until, animation_info->start_time); + float change_delta = (timestamp - change_from_time).InSecondsF(); + if (change_from_time == base::TimeTicks()) + change_delta = 0; + float offset_delta = animation_info->offset_bound * + (change_delta / animation_info->animation_rate); + animation_info->offset += offset_delta; if (animation_info->offset > animation_info->offset_bound) { animation_info->offset = animation_info->offset_bound; animation_info->animation_rate *= -1; + animation_info->linger_until = timestamp + kLingerDelay; } else if (animation_info->offset < animation_info->offset_start) { animation_info->offset = animation_info->offset_start; animation_info->animation_rate *= -1; + animation_info->linger_until = timestamp + kLingerDelay; } - - float offset_delta = animation_info->offset_bound * - (change_delta / animation_info->animation_rate); - animation_info->offset += offset_delta; } } // namespace ash
diff --git a/ash/accessibility/point_scan_layer_animation_info.h b/ash/accessibility/point_scan_layer_animation_info.h index c9fb4ce..e96ee68 100644 --- a/ash/accessibility/point_scan_layer_animation_info.h +++ b/ash/accessibility/point_scan_layer_animation_info.h
@@ -12,6 +12,7 @@ struct PointScanLayerAnimationInfo { base::TimeTicks start_time; base::TimeTicks change_time; + base::TimeTicks linger_until; float offset = 0; float offset_bound = 0; float offset_start = 0;
diff --git a/ash/fast_ink/fast_ink_pointer_controller.cc b/ash/fast_ink/fast_ink_pointer_controller.cc index 8bc44ba2..e32e4b8 100644 --- a/ash/fast_ink/fast_ink_pointer_controller.cc +++ b/ash/fast_ink/fast_ink_pointer_controller.cc
@@ -4,10 +4,12 @@ #include "ash/fast_ink/fast_ink_pointer_controller.h" +#include "ash/public/cpp/stylus_utils.h" #include "ui/aura/window.h" #include "ui/display/screen.h" #include "ui/events/base_event_utils.h" #include "ui/views/widget/widget.h" +#include "ui/wm/core/coordinate_conversion.h" namespace fast_ink { namespace { @@ -18,9 +20,14 @@ } // namespace +// TODO(llin): Update |enabled_for_mouse_event_| based on whether the user has +// been interacted with a stylus. FastInkPointerController::FastInkPointerController() : presentation_delay_( - base::TimeDelta::FromMilliseconds(kPresentationDelayMs)) {} + base::TimeDelta::FromMilliseconds(kPresentationDelayMs)), + enabled_for_mouse_event_(!ash::stylus_utils::HasStylusInput()) { + input_device_event_observation_.Observe(ui::DeviceDataManager::GetInstance()); +} FastInkPointerController::~FastInkPointerController() {} @@ -32,51 +39,142 @@ // while it is being animated away. } -bool FastInkPointerController::CanStartNewGesture(ui::TouchEvent* event) { - // 1) The stylus is pressed - // 2) The stylus is moving, but the pointer session has not started yet - // (most likely because the preceding press event was consumed by another - // handler). - return (event->type() == ui::ET_TOUCH_PRESSED || - (event->type() == ui::ET_TOUCH_MOVED && !GetPointerView())); +void FastInkPointerController::AddExcludedWindow(aura::Window* window) { + DCHECK(window); + excluded_windows_.Add(window); } -void FastInkPointerController::OnTouchEvent(ui::TouchEvent* event) { - if (!enabled_) - return; +bool FastInkPointerController::CanStartNewGesture(ui::LocatedEvent* event) { + if (IsPointerInExcludedWindows(event)) + return false; - if (event->pointer_details().pointer_type != ui::EventPointerType::kPen) - return; + // 1) The stylus/finger is pressed. + // 2) The stylus/finger is moving, but the pointer session has not started yet + // (most likely because the preceding press event was consumed by another + // handler). + bool can_start_on_touch_event = + event->type() == ui::ET_TOUCH_PRESSED || + (event->type() == ui::ET_TOUCH_MOVED && !GetPointerView()); + if (can_start_on_touch_event) + return true; - if (event->type() != ui::ET_TOUCH_MOVED && - event->type() != ui::ET_TOUCH_PRESSED && - event->type() != ui::ET_TOUCH_RELEASED) - return; + // 1) The mouse is pressed. + // 2) The mouse is moving, but the pointer session has not started yet + // (most likely because the preceding press event was consumed by another + // handler). + bool can_start_on_mouse_event = + event->type() == ui::ET_MOUSE_PRESSED || + (event->type() == ui::ET_MOUSE_MOVED && !GetPointerView()); + if (can_start_on_mouse_event) + return true; - // Find the root window that the event was captured on. We never need to - // switch between different root windows because it is not physically possible - // to seamlessly drag a stylus between two displays like it is with a mouse. + return false; +} + +bool FastInkPointerController::ShouldProcessEvent(ui::LocatedEvent* event) { + return event->type() == ui::ET_TOUCH_RELEASED || + event->type() == ui::ET_TOUCH_MOVED || + event->type() == ui::ET_TOUCH_PRESSED || + event->type() == ui::ET_MOUSE_PRESSED || + event->type() == ui::ET_MOUSE_RELEASED || + event->type() == ui::ET_MOUSE_MOVED; +} + +bool FastInkPointerController::IsPointerInExcludedWindows( + ui::LocatedEvent* event) { + gfx::Point screen_location = event->location(); + aura::Window* event_target = static_cast<aura::Window*>(event->target()); + wm::ConvertPointToScreen(event_target, &screen_location); + + for (const auto* excluded_window : excluded_windows_.windows()) { + if (excluded_window->GetBoundsInScreen().Contains(screen_location)) { + return true; + } + } + + return false; +} + +bool FastInkPointerController::MaybeCreatePointerView( + ui::LocatedEvent* event, + bool can_start_new_gesture) { aura::Window* root_window = static_cast<aura::Window*>(event->target())->GetRootWindow(); - - if (CanStartNewGesture(event)) { + if (can_start_new_gesture) { DestroyPointerView(); CreatePointerView(presentation_delay_, root_window); } else { views::View* pointer_view = GetPointerView(); if (!pointer_view) - return; + return false; views::Widget* widget = pointer_view->GetWidget(); if (widget->IsClosed() || widget->GetNativeWindow()->GetRootWindow() != root_window) { // The pointer widget is no longer valid, end the current pointer session. DestroyPointerView(); - return; + return false; } } - UpdatePointerView(event); - event->StopPropagation(); + return true; +} + +void FastInkPointerController::OnTouchEvent(ui::TouchEvent* event) { + const int touch_id = event->pointer_details().id; + // Keep track of touch point count. + if (event->type() == ui::ET_TOUCH_PRESSED) + touch_ids_.insert(touch_id); + if (event->type() == ui::ET_TOUCH_RELEASED || + event->type() == ui::ET_TOUCH_CANCELLED) { + auto iter = touch_ids_.find(touch_id); + + // Can happen if this object is constructed while fingers were down. + if (iter == touch_ids_.end()) + return; + + touch_ids_.erase(touch_id); + } + + if (!enabled_) + return; + + // Disable on touch events if the device has stylus. + if (ash::stylus_utils::HasStylusInput() && + event->pointer_details().pointer_type != ui::EventPointerType::kPen) { + return; + } + + // Disable for multiple fingers touch. + if (touch_ids_.size() > 1) { + DestroyPointerView(); + return; + } + + if (!ShouldProcessEvent(event)) + return; + + // Update pointer view and stop event propagation if pointer view is + // available. + if (MaybeCreatePointerView(event, CanStartNewGesture(event))) { + UpdatePointerView(event); + event->StopPropagation(); + } +} + +void FastInkPointerController::OnMouseEvent(ui::MouseEvent* event) { + if (!enabled_ || !enabled_for_mouse_event_ || !ShouldProcessEvent(event)) + return; + + // Update pointer view and stop event propagation if pointer view is + // available. + if (MaybeCreatePointerView(event, CanStartNewGesture(event))) { + UpdatePointerView(event); + event->StopPropagation(); + } +} + +void FastInkPointerController::OnDeviceListsComplete() { + enabled_for_mouse_event_ = !ash::stylus_utils::HasStylusInput(); } } // namespace fast_ink
diff --git a/ash/fast_ink/fast_ink_pointer_controller.h b/ash/fast_ink/fast_ink_pointer_controller.h index 017269f..3d7a0b6 100644 --- a/ash/fast_ink/fast_ink_pointer_controller.h +++ b/ash/fast_ink/fast_ink_pointer_controller.h
@@ -6,22 +6,31 @@ #define ASH_FAST_INK_FAST_INK_POINTER_CONTROLLER_H_ #include "base/macros.h" +#include "base/scoped_observation.h" #include "base/time/time.h" +#include "ui/aura/window_tracker.h" +#include "ui/events/devices/device_data_manager.h" +#include "ui/events/devices/input_device_event_observer.h" #include "ui/events/event_handler.h" namespace aura { class Window; -} +} // namespace aura + +namespace ui { +class LocatedEvent; +} // namespace ui namespace views { class View; -} +} // namespace views namespace fast_ink { // Base class for a fast ink based pointer controller. Enables/disables // the pointer, receives points and passes them off to be rendered. -class FastInkPointerController : public ui::EventHandler { +class FastInkPointerController : public ui::EventHandler, + public ui::InputDeviceEventObserver { public: FastInkPointerController(); ~FastInkPointerController() override; @@ -32,13 +41,34 @@ // the pointer. virtual void SetEnabled(bool enabled); + // Add window that should be excluded from handling events. + void AddExcludedWindow(aura::Window* window); + protected: // Whether the controller is ready to start handling a new gesture. - virtual bool CanStartNewGesture(ui::TouchEvent* event); + virtual bool CanStartNewGesture(ui::LocatedEvent* event); + // Whether the event should be processed and stop propagation. + virtual bool ShouldProcessEvent(ui::LocatedEvent* event); + + bool enabled_for_mouse_event() const { return enabled_for_mouse_event_; } + + // Return true if the location of the event is in one of the excluded windows. + bool IsPointerInExcludedWindows(ui::LocatedEvent* event); private: + // Creates new pointer view if `can_start_new_gesture` is true. Otherwise, try + // to re-use existing one. Ends the current pointer session if the pointer + // widget is no longer valid. Returns true if there is a pointer view + // available. + bool MaybeCreatePointerView(ui::LocatedEvent* event, + bool can_start_new_gesture); + // ui::EventHandler: void OnTouchEvent(ui::TouchEvent* event) override; + void OnMouseEvent(ui::MouseEvent* event) override; + + // ui::InputDeviceEventObserver: + void OnDeviceListsComplete() override; // Returns the pointer view. virtual views::View* GetPointerView() const = 0; @@ -49,6 +79,7 @@ // Updates the pointer view. virtual void UpdatePointerView(ui::TouchEvent* event) = 0; + virtual void UpdatePointerView(ui::MouseEvent* event) {} // Destroys the pointer view if it exists. virtual void DestroyPointerView() = 0; @@ -57,6 +88,18 @@ const base::TimeDelta presentation_delay_; bool enabled_ = false; + // True if enabled for mouse event. + bool enabled_for_mouse_event_ = false; + + // Set of touch ids. + std::set<int> touch_ids_; + + // If the pointer event is in the bound of any of the |excluded_windows_|. + // Skip processing the event. + aura::WindowTracker excluded_windows_; + + base::ScopedObservation<ui::DeviceDataManager, ui::InputDeviceEventObserver> + input_device_event_observation_{this}; DISALLOW_COPY_AND_ASSIGN(FastInkPointerController); };
diff --git a/ash/fast_ink/laser/laser_pointer_controller.cc b/ash/fast_ink/laser/laser_pointer_controller.cc index 58e9a3c9..bfc84e8 100644 --- a/ash/fast_ink/laser/laser_pointer_controller.cc +++ b/ash/fast_ink/laser/laser_pointer_controller.cc
@@ -77,6 +77,14 @@ void LaserPointerController::UpdatePointerView(ui::TouchEvent* event) { LaserPointerView* laser_pointer_view = GetLaserPointerView(); + + if (IsPointerInExcludedWindows(event)) { + // Destroy the |LaserPointerView| since the pointer is in the bound of + // excluded windows. + DestroyPointerView(); + return; + } + laser_pointer_view->AddNewPoint(event->root_location_f(), event->time_stamp()); if (event->type() == ui::ET_TOUCH_RELEASED) { @@ -85,17 +93,49 @@ } } +void LaserPointerController::UpdatePointerView(ui::MouseEvent* event) { + LaserPointerView* laser_pointer_view = GetLaserPointerView(); + if (event->type() == ui::ET_MOUSE_MOVED) { + if (IsPointerInExcludedWindows(event)) { + // Destroy the |LaserPointerView| since the cursor is in the bound of + // excluded windows. + DestroyPointerView(); + return; + } + + // TODO(llin): Hide the mouse cursor. + } + + laser_pointer_view->AddNewPoint(event->root_location_f(), + event->time_stamp()); + if (event->type() == ui::ET_MOUSE_RELEASED) { + laser_pointer_view->FadeOut(base::BindOnce( + &LaserPointerController::DestroyPointerView, base::Unretained(this))); + } +} + void LaserPointerController::DestroyPointerView() { laser_pointer_view_widget_.reset(); } -bool LaserPointerController::CanStartNewGesture(ui::TouchEvent* event) { +bool LaserPointerController::CanStartNewGesture(ui::LocatedEvent* event) { // Ignore events over the palette. + // TODO(llin): Register palette as a excluded window instead. if (palette_utils::PaletteContainsPointInScreen(event->root_location())) return false; return FastInkPointerController::CanStartNewGesture(event); } +bool LaserPointerController::ShouldProcessEvent(ui::LocatedEvent* event) { + // Allow clicking when laser pointer is enabled. + if (event->type() == ui::ET_MOUSE_PRESSED || + event->type() == ui::ET_MOUSE_RELEASED) { + return false; + } + + return FastInkPointerController::ShouldProcessEvent(event); +} + void LaserPointerController::NotifyStateChanged(bool enabled) { for (LaserPointerObserver& observer : observers_) observer.OnLaserPointerStateChanged(enabled);
diff --git a/ash/fast_ink/laser/laser_pointer_controller.h b/ash/fast_ink/laser/laser_pointer_controller.h index 9068b48..79388f1 100644 --- a/ash/fast_ink/laser/laser_pointer_controller.h +++ b/ash/fast_ink/laser/laser_pointer_controller.h
@@ -47,8 +47,10 @@ void CreatePointerView(base::TimeDelta presentation_delay, aura::Window* root_window) override; void UpdatePointerView(ui::TouchEvent* event) override; + void UpdatePointerView(ui::MouseEvent* event) override; void DestroyPointerView() override; - bool CanStartNewGesture(ui::TouchEvent* event) override; + bool CanStartNewGesture(ui::LocatedEvent* event) override; + bool ShouldProcessEvent(ui::LocatedEvent* event) override; void NotifyStateChanged(bool enabled);
diff --git a/ash/fast_ink/laser/laser_pointer_controller_unittest.cc b/ash/fast_ink/laser/laser_pointer_controller_unittest.cc index d81e625..9f53207 100644 --- a/ash/fast_ink/laser/laser_pointer_controller_unittest.cc +++ b/ash/fast_ink/laser/laser_pointer_controller_unittest.cc
@@ -6,6 +6,7 @@ #include "ash/fast_ink/laser/laser_pointer_controller_test_api.h" #include "ash/fast_ink/laser/laser_pointer_view.h" +#include "ash/public/cpp/stylus_utils.h" #include "ash/shell.h" #include "ash/test/ash_test_base.h" #include "ui/events/test/event_generator.h" @@ -54,6 +55,65 @@ } protected: + void VerifyLaserPointerRendererTouchEvent() { + ui::test::EventGenerator* event_generator = GetEventGenerator(); + + // When disabled the laser pointer should not be showing. + event_generator->MoveTouch(gfx::Point(1, 1)); + EXPECT_FALSE(controller_test_api_->IsShowingLaserPointer()); + + // Verify that by enabling the mode, the laser pointer should still not be + // showing. + controller_test_api_->SetEnabled(true); + EXPECT_FALSE(controller_test_api_->IsShowingLaserPointer()); + + // Verify moving the finger 4 times will not display the laser pointer. + event_generator->MoveTouch(gfx::Point(2, 2)); + event_generator->MoveTouch(gfx::Point(3, 3)); + event_generator->MoveTouch(gfx::Point(4, 4)); + event_generator->MoveTouch(gfx::Point(5, 5)); + EXPECT_FALSE(controller_test_api_->IsShowingLaserPointer()); + + // Verify pressing the finger will show the laser pointer and add a point + // but will not activate fading out. + event_generator->PressTouch(); + EXPECT_TRUE(controller_test_api_->IsShowingLaserPointer()); + EXPECT_FALSE(controller_test_api_->IsFadingAway()); + EXPECT_EQ(1, controller_test_api_->laser_points().GetNumberOfPoints()); + + // Verify dragging the finger 2 times will add 2 more points. + event_generator->MoveTouch(gfx::Point(6, 6)); + event_generator->MoveTouch(gfx::Point(7, 7)); + EXPECT_EQ(3, controller_test_api_->laser_points().GetNumberOfPoints()); + + // Verify releasing the finger still shows the laser pointer, which is + // fading away. + event_generator->ReleaseTouch(); + EXPECT_TRUE(controller_test_api_->IsShowingLaserPointer()); + EXPECT_TRUE(controller_test_api_->IsFadingAway()); + + // Verify that disabling the mode does not display the laser pointer. + controller_test_api_->SetEnabled(false); + EXPECT_FALSE(controller_test_api_->IsShowingLaserPointer()); + EXPECT_FALSE(controller_test_api_->IsFadingAway()); + + // Verify that disabling the mode while laser pointer is displayed does not + // display the laser pointer. + controller_test_api_->SetEnabled(true); + event_generator->PressTouch(); + event_generator->MoveTouch(gfx::Point(6, 6)); + EXPECT_TRUE(controller_test_api_->IsShowingLaserPointer()); + controller_test_api_->SetEnabled(false); + EXPECT_FALSE(controller_test_api_->IsShowingLaserPointer()); + + // Verify that the laser pointer does not add points while disabled. + event_generator->PressTouch(); + event_generator->MoveTouch(gfx::Point(8, 8)); + event_generator->ReleaseTouch(); + event_generator->MoveTouch(gfx::Point(9, 9)); + EXPECT_FALSE(controller_test_api_->IsShowingLaserPointer()); + } + TestLaserPointerObserver* observer() { return observer_.get(); } std::unique_ptr<LaserPointerController> controller_; @@ -66,64 +126,10 @@ // Test to ensure the class responsible for drawing the laser pointer receives // points from stylus movements as expected. TEST_F(LaserPointerControllerTest, LaserPointerRenderer) { - // The laser pointer mode only works with stylus. + stylus_utils::SetHasStylusInputForTesting(); ui::test::EventGenerator* event_generator = GetEventGenerator(); event_generator->EnterPenPointerMode(); - - // When disabled the laser pointer should not be showing. - event_generator->MoveTouch(gfx::Point(1, 1)); - EXPECT_FALSE(controller_test_api_->IsShowingLaserPointer()); - - // Verify that by enabling the mode, the laser pointer should still not be - // showing. - controller_test_api_->SetEnabled(true); - EXPECT_FALSE(controller_test_api_->IsShowingLaserPointer()); - - // Verify moving the stylus 4 times will not display the laser pointer. - event_generator->MoveTouch(gfx::Point(2, 2)); - event_generator->MoveTouch(gfx::Point(3, 3)); - event_generator->MoveTouch(gfx::Point(4, 4)); - event_generator->MoveTouch(gfx::Point(5, 5)); - EXPECT_FALSE(controller_test_api_->IsShowingLaserPointer()); - - // Verify pressing the stylus will show the laser pointer and add a point but - // will not activate fading out. - event_generator->PressTouch(); - EXPECT_TRUE(controller_test_api_->IsShowingLaserPointer()); - EXPECT_FALSE(controller_test_api_->IsFadingAway()); - EXPECT_EQ(1, controller_test_api_->laser_points().GetNumberOfPoints()); - - // Verify dragging the stylus 2 times will add 2 more points. - event_generator->MoveTouch(gfx::Point(6, 6)); - event_generator->MoveTouch(gfx::Point(7, 7)); - EXPECT_EQ(3, controller_test_api_->laser_points().GetNumberOfPoints()); - - // Verify releasing the stylus still shows the laser pointer, which is fading - // away. - event_generator->ReleaseTouch(); - EXPECT_TRUE(controller_test_api_->IsShowingLaserPointer()); - EXPECT_TRUE(controller_test_api_->IsFadingAway()); - - // Verify that disabling the mode does not display the laser pointer. - controller_test_api_->SetEnabled(false); - EXPECT_FALSE(controller_test_api_->IsShowingLaserPointer()); - EXPECT_FALSE(controller_test_api_->IsFadingAway()); - - // Verify that disabling the mode while laser pointer is displayed does not - // display the laser pointer. - controller_test_api_->SetEnabled(true); - event_generator->PressTouch(); - event_generator->MoveTouch(gfx::Point(6, 6)); - EXPECT_TRUE(controller_test_api_->IsShowingLaserPointer()); - controller_test_api_->SetEnabled(false); - EXPECT_FALSE(controller_test_api_->IsShowingLaserPointer()); - - // Verify that the laser pointer does not add points while disabled. - event_generator->PressTouch(); - event_generator->MoveTouch(gfx::Point(8, 8)); - event_generator->ReleaseTouch(); - event_generator->MoveTouch(gfx::Point(9, 9)); - EXPECT_FALSE(controller_test_api_->IsShowingLaserPointer()); + VerifyLaserPointerRendererTouchEvent(); // Verify that the laser pointer does not get shown if points are not coming // from the stylus, even when enabled. @@ -148,6 +154,73 @@ static_cast<ui::EventHandler*>(controller_.get())->OnTouchEvent(&touch); } +// Test to ensure the class responsible for drawing the laser pointer receives +// points from mouse movements as expected. +TEST_F(LaserPointerControllerTest, LaserPointerRendererTouchEvent) { + stylus_utils::SetNoStylusInputForTesting(); + VerifyLaserPointerRendererTouchEvent(); + + // Make sure that event can be sent after the pointer widget is destroyed + // by release. This can happen if the touch event causes the deletion of + // the pointer event in an earlier event handler. + ui::PointerDetails pointer_details; + + ui::TouchEvent touch(ui::ET_TOUCH_MOVED, gfx::PointF(), gfx::PointF(), + base::TimeTicks(), pointer_details, 0); + ui::Event::DispatcherApi api(&touch); + api.set_target(Shell::GetPrimaryRootWindow()); + static_cast<ui::EventHandler*>(controller_.get())->OnTouchEvent(&touch); +} + +// Test to ensure the class responsible for drawing the laser pointer receives +// points from mouse movements as expected. +TEST_F(LaserPointerControllerTest, LaserPointerRendererMouseEvent) { + stylus_utils::SetNoStylusInputForTesting(); + + ui::test::EventGenerator* event_generator = GetEventGenerator(); + + // When disabled the laser pointer should not be showing. + event_generator->MoveMouseTo(gfx::Point(1, 1)); + EXPECT_FALSE(controller_test_api_->IsShowingLaserPointer()); + + // Verify that by enabling the mode, the laser pointer should still not be + // showing. + controller_test_api_->SetEnabled(true); + EXPECT_FALSE(controller_test_api_->IsShowingLaserPointer()); + + // Verify moving the cursor 4 times will display the laser pointer. + event_generator->MoveMouseTo(gfx::Point(2, 2)); + event_generator->MoveMouseTo(gfx::Point(3, 3)); + event_generator->MoveMouseTo(gfx::Point(4, 4)); + event_generator->MoveMouseTo(gfx::Point(5, 5)); + EXPECT_TRUE(controller_test_api_->IsShowingLaserPointer()); + EXPECT_FALSE(controller_test_api_->IsFadingAway()); + EXPECT_EQ(4, controller_test_api_->laser_points().GetNumberOfPoints()); + + // Verify moving the cursor 2 times will add 2 more points. + event_generator->MoveMouseTo(gfx::Point(6, 6)); + event_generator->MoveMouseTo(gfx::Point(7, 7)); + EXPECT_EQ(6, controller_test_api_->laser_points().GetNumberOfPoints()); + + // Verify that disabling the mode does not display the laser pointer. + controller_test_api_->SetEnabled(false); + EXPECT_FALSE(controller_test_api_->IsShowingLaserPointer()); + EXPECT_FALSE(controller_test_api_->IsFadingAway()); + + // Verify that disabling the mode while laser pointer is displayed does not + // display the laser pointer. + controller_test_api_->SetEnabled(true); + event_generator->MoveMouseTo(gfx::Point(6, 6)); + EXPECT_TRUE(controller_test_api_->IsShowingLaserPointer()); + controller_test_api_->SetEnabled(false); + EXPECT_FALSE(controller_test_api_->IsShowingLaserPointer()); + + // Verify that the laser pointer does not add points while disabled. + event_generator->MoveMouseTo(gfx::Point(8, 8)); + event_generator->MoveMouseTo(gfx::Point(9, 9)); + EXPECT_FALSE(controller_test_api_->IsShowingLaserPointer()); +} + // Test to ensure the class responsible for drawing the laser pointer handles // prediction as expected when it receives points from stylus movements. TEST_F(LaserPointerControllerTest, LaserPointerPrediction) {
diff --git a/ash/highlighter/highlighter_controller.cc b/ash/highlighter/highlighter_controller.cc index f2d78dd..bffce0b5 100644 --- a/ash/highlighter/highlighter_controller.cc +++ b/ash/highlighter/highlighter_controller.cc
@@ -232,7 +232,7 @@ DestroyResultView(); } -bool HighlighterController::CanStartNewGesture(ui::TouchEvent* event) { +bool HighlighterController::CanStartNewGesture(ui::LocatedEvent* event) { // Ignore events over the palette. if (palette_utils::PaletteContainsPointInScreen(event->root_location())) return false;
diff --git a/ash/highlighter/highlighter_controller.h b/ash/highlighter/highlighter_controller.h index 7bf9347..3e3f46c 100644 --- a/ash/highlighter/highlighter_controller.h +++ b/ash/highlighter/highlighter_controller.h
@@ -90,7 +90,7 @@ aura::Window* root_window) override; void UpdatePointerView(ui::TouchEvent* event) override; void DestroyPointerView() override; - bool CanStartNewGesture(ui::TouchEvent* event) override; + bool CanStartNewGesture(ui::LocatedEvent* event) override; // Performs gesture recognition, initiates appropriate visual effects, // notifies the observer if necessary.
diff --git a/ash/highlighter/highlighter_controller_unittest.cc b/ash/highlighter/highlighter_controller_unittest.cc index afb84bd..3e1d2f1 100644 --- a/ash/highlighter/highlighter_controller_unittest.cc +++ b/ash/highlighter/highlighter_controller_unittest.cc
@@ -9,6 +9,7 @@ #include "ash/assistant/test/assistant_ash_test_base.h" #include "ash/fast_ink/fast_ink_points.h" #include "ash/highlighter/highlighter_controller_test_api.h" +#include "ash/public/cpp/stylus_utils.h" #include "ash/shell.h" #include "ash/system/palette/mock_palette_tool_delegate.h" #include "ash/system/palette/palette_tool.h" @@ -120,6 +121,8 @@ // Test to ensure the class responsible for drawing the highlighter pointer // receives points from stylus movements as expected. TEST_F(HighlighterControllerTest, HighlighterRenderer) { + ash::stylus_utils::SetHasStylusInputForTesting(); + // The highlighter pointer mode only works with stylus. ui::test::EventGenerator* event_generator = GetEventGenerator(); event_generator->EnterPenPointerMode();
diff --git a/ash/projector/projector_ui_controller.cc b/ash/projector/projector_ui_controller.cc index ee62479d..168a880 100644 --- a/ash/projector/projector_ui_controller.cc +++ b/ash/projector/projector_ui_controller.cc
@@ -10,6 +10,7 @@ #include "ash/shell.h" #include "ash/strings/grit/ash_strings.h" #include "ash/system/toast/toast_manager_impl.h" +#include "ui/aura/window.h" #include "ui/base/l10n/l10n_util.h" #include "ui/views/widget/widget.h" @@ -32,6 +33,11 @@ Shell::Get()->toast_manager()->Show(toast); } +void AddExcludedWindowToFastInkController(aura::Window* window) { + DCHECK(window); + Shell::Get()->laser_pointer_controller()->AddExcludedWindow(window); +} + } // namespace ProjectorUiController::ProjectorUiController( @@ -44,6 +50,8 @@ if (!projector_bar_widget_) { // Create the toolbar. projector_bar_widget_ = ProjectorBarView::Create(projector_controller_); + AddExcludedWindowToFastInkController( + projector_bar_widget_->GetNativeWindow()); } projector_bar_widget_->ShowInactive();
diff --git a/ash/public/cpp/external_arc/message_center/arc_notification_content_view.cc b/ash/public/cpp/external_arc/message_center/arc_notification_content_view.cc index 0136db0..63aa89981 100644 --- a/ash/public/cpp/external_arc/message_center/arc_notification_content_view.cc +++ b/ash/public/cpp/external_arc/message_center/arc_notification_content_view.cc
@@ -555,7 +555,8 @@ auto mask_painter = std::make_unique<message_center::NotificationBackgroundPainter>( top_radius_, bottom_radius_, - message_center::kNotificationBackgroundColor); + GetNativeTheme()->GetSystemColor( + ui::NativeTheme::kColorId_NotificationBackground)); // Set insets to round visible notification corners. https://crbug.com/866777 mask_painter->set_insets(new_insets); @@ -723,6 +724,13 @@ notification_view->OnContentBlurred(); } +void ArcNotificationContentView::OnThemeChanged() { + View::OnThemeChanged(); + // OnThemeChanged may be called before container is set. + if (GetWidget() && GetNativeViewContainer()) + UpdateMask(true); +} + void ArcNotificationContentView::OnRemoteInputActivationChanged( bool activated) { // Remove the focus from the currently focused view-control in the message
diff --git a/ash/public/cpp/external_arc/message_center/arc_notification_content_view.h b/ash/public/cpp/external_arc/message_center/arc_notification_content_view.h index 18b8f97..95340f84 100644 --- a/ash/public/cpp/external_arc/message_center/arc_notification_content_view.h +++ b/ash/public/cpp/external_arc/message_center/arc_notification_content_view.h
@@ -55,7 +55,6 @@ ArcNotificationContentView& operator=(const ArcNotificationContentView&) = delete; ~ArcNotificationContentView() override; - void Update(const message_center::Notification& notification); message_center::NotificationControlButtonsView* GetControlButtonsView(); void UpdateControlButtonsVisibility(); @@ -105,6 +104,7 @@ void OnMouseExited(const ui::MouseEvent& event) override; void OnFocus() override; void OnBlur() override; + void OnThemeChanged() override; views::FocusTraversable* GetFocusTraversable() override; void GetAccessibleNodeData(ui::AXNodeData* node_data) override; void OnAccessibilityEvent(ax::mojom::Event event) override;
diff --git a/ash/public/cpp/external_arc/message_center/arc_notification_view.cc b/ash/public/cpp/external_arc/message_center/arc_notification_view.cc index e1274fb14..af5c67cc 100644 --- a/ash/public/cpp/external_arc/message_center/arc_notification_view.cc +++ b/ash/public/cpp/external_arc/message_center/arc_notification_view.cc
@@ -56,9 +56,6 @@ content_view_->background()->get_color()); } - focus_painter_ = views::Painter::CreateSolidFocusPainter( - message_center::kFocusBorderColor, gfx::Insets(0, 1, 3, 2)); - UpdateCornerRadius(message_center::kNotificationCornerRadius, message_center::kNotificationCornerRadius); } @@ -162,6 +159,14 @@ return item_->OpenSnooze(); } +void ArcNotificationView::OnThemeChanged() { + message_center::MessageView::OnThemeChanged(); + focus_painter_ = views::Painter::CreateSolidFocusPainter( + GetNativeTheme()->GetSystemColor( + ui::NativeTheme::kColorId_FocusedBorderColor), + gfx::Insets(0, 1, 3, 2)); +} + void ArcNotificationView::OnContainerAnimationEnded() { content_view_->OnContainerAnimationEnded(); }
diff --git a/ash/public/cpp/external_arc/message_center/arc_notification_view.h b/ash/public/cpp/external_arc/message_center/arc_notification_view.h index 19720f14..84e643f8 100644 --- a/ash/public/cpp/external_arc/message_center/arc_notification_view.h +++ b/ash/public/cpp/external_arc/message_center/arc_notification_view.h
@@ -58,6 +58,7 @@ void OnContainerAnimationEnded() override; void OnSettingsButtonPressed(const ui::Event& event) override; void OnSnoozeButtonPressed(const ui::Event& event) override; + void OnThemeChanged() override; void UpdateCornerRadius(int top_radius, int bottom_radius) override; // views::SlideOutControllerDelegate:
diff --git a/ash/public/cpp/stylus_utils.cc b/ash/public/cpp/stylus_utils.cc index cfd9010..2a5fe39 100644 --- a/ash/public/cpp/stylus_utils.cc +++ b/ash/public/cpp/stylus_utils.cc
@@ -60,5 +60,9 @@ g_has_stylus_input_for_testing = true; } +void SetNoStylusInputForTesting() { + g_has_stylus_input_for_testing = false; +} + } // namespace stylus_utils } // namespace ash
diff --git a/ash/public/cpp/stylus_utils.h b/ash/public/cpp/stylus_utils.h index b3a92aa5..6d39c61 100644 --- a/ash/public/cpp/stylus_utils.h +++ b/ash/public/cpp/stylus_utils.h
@@ -24,9 +24,12 @@ // Returns true if the device has an internal stylus. ASH_PUBLIC_EXPORT bool HasInternalStylus(); -// Forcibly say the device is has stylus input for testing purposes. +// Forcibly say the device has stylus input for testing purposes. ASH_PUBLIC_EXPORT void SetHasStylusInputForTesting(); +// Forcibly say the device doesn't have stylus input for testing purposes. +ASH_PUBLIC_EXPORT void SetNoStylusInputForTesting(); + } // namespace stylus_utils } // namespace ash
diff --git a/ash/system/message_center/stacked_notification_bar.cc b/ash/system/message_center/stacked_notification_bar.cc index 70b3295..09e3b70 100644 --- a/ash/system/message_center/stacked_notification_bar.cc +++ b/ash/system/message_center/stacked_notification_bar.cc
@@ -124,12 +124,18 @@ void OnThemeChanged() override { views::ImageView::OnThemeChanged(); + auto* theme = GetNativeTheme(); + auto* notification = message_center::MessageCenter::Get()->FindVisibleNotificationById(id_); SkColor accent_color = GetNativeTheme()->GetSystemColor( ui::NativeTheme::kColorId_NotificationDefaultAccentColor); gfx::Image masked_small_icon = notification->GenerateMaskedSmallIcon( - kStackedNotificationIconSize, accent_color); + kStackedNotificationIconSize, accent_color, + theme->GetSystemColor( + ui::NativeTheme::kColorId_MessageCenterSmallImageMaskBackground), + theme->GetSystemColor( + ui::NativeTheme::kColorId_MessageCenterSmallImageMaskForeground)); if (masked_small_icon.IsEmpty()) { SetImage(gfx::CreateVectorIcon(message_center::kProductIcon,
diff --git a/ash/system/unified/notification_icons_controller.cc b/ash/system/unified/notification_icons_controller.cc index cd49564..bc69d455 100644 --- a/ash/system/unified/notification_icons_controller.cc +++ b/ash/system/unified/notification_icons_controller.cc
@@ -79,10 +79,15 @@ message_center::Notification* notification) { notification_id_ = notification->id(); + auto* theme = GetNativeTheme(); gfx::Image masked_small_icon = notification->GenerateMaskedSmallIcon( kUnifiedTrayIconSize, AshColorProvider::Get()->GetContentLayerColor( - AshColorProvider::ContentLayerType::kIconColorPrimary)); + AshColorProvider::ContentLayerType::kIconColorPrimary), + theme->GetSystemColor( + ui::NativeTheme::kColorId_MessageCenterSmallImageMaskBackground), + theme->GetSystemColor( + ui::NativeTheme::kColorId_MessageCenterSmallImageMaskForeground)); if (!masked_small_icon.IsEmpty()) { image_view()->SetImage(masked_small_icon.AsImageSkia()); } else {
diff --git a/ash/wm/desks/desk_animation_impl_unittest.cc b/ash/wm/desks/desk_animation_impl_unittest.cc index 398d30d2..de78d325 100644 --- a/ash/wm/desks/desk_animation_impl_unittest.cc +++ b/ash/wm/desks/desk_animation_impl_unittest.cc
@@ -15,6 +15,19 @@ namespace ash { +namespace { + +void WaitEndingScreenshotTaken(DeskActivationAnimation* animation) { + base::RunLoop run_loop; + auto* desk_switch_animator = + animation->GetDeskSwitchAnimatorAtIndexForTesting(0); + RootWindowDeskSwitchAnimatorTestApi(desk_switch_animator) + .SetOnEndingScreenshotTakenCallback(run_loop.QuitClosure()); + run_loop.Run(); +} + +} // namespace + using DeskActivationAnimationTest = AshTestBase; // Tests that there is no crash when ending a swipe animation before the @@ -86,32 +99,26 @@ desks_controller->NewDesk(DesksCreationRemovalSource::kButton); desks_controller->NewDesk(DesksCreationRemovalSource::kButton); + base::test::ScopedFeatureList feature_list; + feature_list.InitAndEnableFeature(features::kEnhancedDeskAnimations); + DeskActivationAnimation animation(desks_controller, 0, 1, DesksSwitchSource::kDeskSwitchTouchpad, /*update_window_activation=*/false); animation.set_skip_notify_controller_on_animation_finished_for_testing(true); animation.Launch(); - auto wait_ending_screenshot_taken = [](DeskActivationAnimation* animation) { - base::RunLoop run_loop; - auto* desk_switch_animator = - animation->GetDeskSwitchAnimatorAtIndexForTesting(0); - RootWindowDeskSwitchAnimatorTestApi(desk_switch_animator) - .SetOnEndingScreenshotTakenCallback(run_loop.QuitClosure()); - run_loop.Run(); - }; - - wait_ending_screenshot_taken(&animation); + WaitEndingScreenshotTaken(&animation); EXPECT_EQ(0, animation.visible_desk_changes()); // Swipe enough so that our third and fourth desk screenshots are taken, and // then swipe so that the fourth desk is fully shown. There should be 3 // visible desk changes in total. animation.UpdateSwipeAnimation(-kTouchpadSwipeLengthForDeskChange); - wait_ending_screenshot_taken(&animation); + WaitEndingScreenshotTaken(&animation); animation.UpdateSwipeAnimation(-kTouchpadSwipeLengthForDeskChange); - wait_ending_screenshot_taken(&animation); + WaitEndingScreenshotTaken(&animation); animation.UpdateSwipeAnimation(-3 * kTouchpadSwipeLengthForDeskChange); EXPECT_EQ(3, animation.visible_desk_changes()); @@ -131,4 +138,24 @@ EXPECT_EQ(7, animation.visible_desk_changes()); } +// Tests that closing windows during a desk animation does not cause a crash. +TEST_F(DeskActivationAnimationTest, CloseWindowDuringAnimation) { + auto* desks_controller = DesksController::Get(); + desks_controller->NewDesk(DesksCreationRemovalSource::kButton); + + std::unique_ptr<aura::Window> window = CreateAppWindow(gfx::Rect(250, 100)); + + base::test::ScopedFeatureList feature_list; + feature_list.InitAndEnableFeature(features::kEnhancedDeskAnimations); + + DeskActivationAnimation animation(desks_controller, 0, 1, + DesksSwitchSource::kDeskSwitchTouchpad, + /*update_window_activation=*/false); + animation.set_skip_notify_controller_on_animation_finished_for_testing(true); + animation.Launch(); + + window.reset(); + WaitEndingScreenshotTaken(&animation); +} + } // namespace ash
diff --git a/ash/wm/overview/overview_controller_unittest.cc b/ash/wm/overview/overview_controller_unittest.cc index d8fe0f69..fdb6943 100644 --- a/ash/wm/overview/overview_controller_unittest.cc +++ b/ash/wm/overview/overview_controller_unittest.cc
@@ -620,6 +620,34 @@ EXPECT_TRUE(WindowState::Get(window.get())->IsMinimized()); } +// Tests that overview animations continue even if a window gets destroyed +// during the animation. +TEST_F(OverviewControllerTest, CloseWindowDuringAnimation) { + // Create two windows. They should both be visible so that they both get + // animated. + std::unique_ptr<aura::Window> window1 = CreateAppWindow(gfx::Rect(250, 100)); + std::unique_ptr<aura::Window> window2 = + CreateAppWindow(gfx::Rect(250, 250, 250, 100)); + + ui::ScopedAnimationDurationScaleMode non_zero( + ui::ScopedAnimationDurationScaleMode::NON_ZERO_DURATION); + Shell::Get()->overview_controller()->StartOverview(); + + // Destroy a window during the enter animation. + window1.reset(); + ShellTestApi().WaitForOverviewAnimationState( + OverviewAnimationState::kEnterAnimationComplete); + ASSERT_TRUE(Shell::Get()->overview_controller()->InOverviewSession()); + + Shell::Get()->overview_controller()->EndOverview(); + + // Destroy a window during the exit animation. + window2.reset(); + ShellTestApi().WaitForOverviewAnimationState( + OverviewAnimationState::kExitAnimationComplete); + EXPECT_FALSE(Shell::Get()->overview_controller()->InOverviewSession()); +} + // A subclass of DeskSwitchAnimationWaiter that additionally attempts to start // overview after the desk animation screenshots have been taken. Using the // regular DeskSwitchAnimatorWaiter and attempting to start overview before
diff --git a/ash/wm/tablet_mode/tablet_mode_controller_unittest.cc b/ash/wm/tablet_mode/tablet_mode_controller_unittest.cc index a5c0ce2d..4d993c0 100644 --- a/ash/wm/tablet_mode/tablet_mode_controller_unittest.cc +++ b/ash/wm/tablet_mode/tablet_mode_controller_unittest.cc
@@ -1632,6 +1632,31 @@ histogram_tester.ExpectTotalCount(kExitHistogram, 0); } +// Tests that closing a window during the tablet mode enter animation does not +// cause a crash. +TEST_F(TabletModeControllerTest, CloseWindowDuringEnterAnimation) { + std::unique_ptr<aura::Window> window = CreateAppWindow(gfx::Rect(250, 100)); + + ui::ScopedAnimationDurationScaleMode test_duration_mode( + ui::ScopedAnimationDurationScaleMode::NON_ZERO_DURATION); + + tablet_mode_controller()->SetEnabledForTest(true); + window.reset(); +} + +// Tests that closing a window during the tablet mode exit animation does not +// cause a crash. +TEST_F(TabletModeControllerTest, CloseWindowDuringExitAnimation) { + std::unique_ptr<aura::Window> window = CreateAppWindow(gfx::Rect(250, 100)); + tablet_mode_controller()->SetEnabledForTest(true); + + ui::ScopedAnimationDurationScaleMode test_duration_mode( + ui::ScopedAnimationDurationScaleMode::NON_ZERO_DURATION); + + tablet_mode_controller()->SetEnabledForTest(false); + window.reset(); +} + class TabletModeControllerOnDeviceTest : public TabletModeControllerTest { public: TabletModeControllerOnDeviceTest() = default;
diff --git a/base/BUILD.gn b/base/BUILD.gn index 092acd0..5288a27 100644 --- a/base/BUILD.gn +++ b/base/BUILD.gn
@@ -598,8 +598,6 @@ "strings/escape.h", "strings/latin1_string_conversions.cc", "strings/latin1_string_conversions.h", - "strings/nullable_string16.cc", - "strings/nullable_string16.h", "strings/pattern.cc", "strings/pattern.h", "strings/safe_sprintf.cc", @@ -3009,7 +3007,6 @@ "strings/char_traits_unittest.cc", "strings/escape_unittest.cc", "strings/no_trigraphs_unittest.cc", - "strings/nullable_string16_unittest.cc", "strings/pattern_unittest.cc", "strings/safe_sprintf_unittest.cc", "strings/strcat_unittest.cc",
diff --git a/base/allocator/partition_allocator/pcscan.cc b/base/allocator/partition_allocator/pcscan.cc index 21388347..43d20dc 100644 --- a/base/allocator/partition_allocator/pcscan.cc +++ b/base/allocator/partition_allocator/pcscan.cc
@@ -200,7 +200,10 @@ V(Sweep) \ V(Overall) -#define FOR_ALL_PCSCAN_MUTATOR_SCOPES(V) V(Scan) +#define FOR_ALL_PCSCAN_MUTATOR_SCOPES(V) \ + V(Clear) \ + V(Scan) \ + V(Overall) class StatsCollector final { public: @@ -208,12 +211,14 @@ #define DECLARE_ENUM(name) k##name, FOR_ALL_PCSCAN_SCANNER_SCOPES(DECLARE_ENUM) #undef DECLARE_ENUM + kNumIds, }; enum class MutatorId { #define DECLARE_ENUM(name) k##name, FOR_ALL_PCSCAN_MUTATOR_SCOPES(DECLARE_ENUM) #undef DECLARE_ENUM + kNumIds, }; enum class Context { @@ -255,12 +260,22 @@ return "PCScan.Scanner.Sweep"; case ScannerId::kOverall: return "PCScan.Scanner"; + case ScannerId::kNumIds: + __builtin_unreachable(); } } static constexpr const char* ToTracingString(MutatorId id) { - PA_DCHECK(id == MutatorId::kScan); - return "PCScan.Mutator.Sweep"; + switch (id) { + case MutatorId::kClear: + return "PCScan.Mutator.Clear"; + case MutatorId::kScan: + return "PCScan.Mutator.Scan"; + case MutatorId::kOverall: + return "PCScan.Mutator"; + case MutatorId::kNumIds: + __builtin_unreachable(); + } } StatsCollector& stats_; @@ -278,28 +293,48 @@ StatsCollector& operator=(const StatsCollector&) = delete; void IncreaseScopeTime(ScannerId type, base::TimeDelta duration) { - scanner_scopes_[static_cast<size_t>(type)] += duration; + const int64_t ms = duration.InMicroseconds(); + PA_DCHECK(ms <= std::numeric_limits<uint32_t>::max()); + scanner_scopes_[static_cast<size_t>(type)].fetch_add( + ms, std::memory_order_relaxed); } void IncreaseScopeTime(MutatorId type, base::TimeDelta duration) { - mutator_scopes_[static_cast<size_t>(type)] += duration; + const int64_t ms = duration.InMicroseconds(); + PA_DCHECK(ms <= std::numeric_limits<uint32_t>::max()); + mutator_scopes_[static_cast<size_t>(type)].fetch_add( + ms, std::memory_order_relaxed); } + void IncreaseSurvivedQuarantineSize(size_t size) { + survived_quarantine_size_.fetch_add(size, std::memory_order_relaxed); + } + size_t survived_quarantine_size() const { + return survived_quarantine_size_.load(std::memory_order_relaxed); + } + + void IncreaseSweptSize(size_t size) { swept_size_ += size; } + size_t swept_size() const { return swept_size_; } + void UpdateHistograms() { if (!process_name_) { // Don't update histograms if |process_name_| is not set. return; } -#define UPDATE_UMA(name) \ - UMA_HISTOGRAM_TIMES( \ - ToUMAString(ScannerId::k##name).c_str(), \ - scanner_scopes_[static_cast<size_t>(ScannerId::k##name)]); +#define UPDATE_UMA(name) \ + UMA_HISTOGRAM_TIMES( \ + ToUMAString(ScannerId::k##name).c_str(), \ + base::TimeDelta::FromMicroseconds( \ + scanner_scopes_[static_cast<size_t>(ScannerId::k##name)].load( \ + std::memory_order_relaxed))); FOR_ALL_PCSCAN_SCANNER_SCOPES(UPDATE_UMA) #undef UPDATE_UMA -#define UPDATE_UMA(name) \ - UMA_HISTOGRAM_TIMES( \ - ToUMAString(MutatorId::k##name).c_str(), \ - mutator_scopes_[static_cast<size_t>(MutatorId::k##name)]); +#define UPDATE_UMA(name) \ + UMA_HISTOGRAM_TIMES( \ + ToUMAString(MutatorId::k##name).c_str(), \ + base::TimeDelta::FromMicroseconds( \ + mutator_scopes_[static_cast<size_t>(MutatorId::k##name)].load( \ + std::memory_order_relaxed))); FOR_ALL_PCSCAN_MUTATOR_SCOPES(UPDATE_UMA) #undef UPDATE_UMA } @@ -320,24 +355,57 @@ return "PA.PCScan." + process_name + ".Scanner.Sweep"; case ScannerId::kOverall: return "PA.PCScan." + process_name + ".Scanner"; + case ScannerId::kNumIds: + __builtin_unreachable(); } } MetadataString ToUMAString(MutatorId id) const { PA_DCHECK(process_name_); - PA_DCHECK(id == MutatorId::kScan); - return "PA.PCScan." + MetadataString(process_name_) + ".Mutator.Scan"; + const MetadataString process_name = process_name_; + switch (id) { + case MutatorId::kClear: + return "PA.PCScan." + process_name + ".Mutator.Clear"; + case MutatorId::kScan: + return "PA.PCScan." + process_name + ".Mutator.Scan"; + case MutatorId::kOverall: + return "PA.PCScan." + process_name + ".Mutator"; + case MutatorId::kNumIds: + __builtin_unreachable(); + } } + std::array<std::atomic<uint32_t>, static_cast<size_t>(ScannerId::kNumIds)> + scanner_scopes_; + std::array<std::atomic<uint32_t>, static_cast<size_t>(MutatorId::kNumIds)> + mutator_scopes_; + std::atomic<size_t> survived_quarantine_size_{0u}; + size_t swept_size_ = 0u; const char* process_name_ = nullptr; - std::array<base::TimeDelta, 4> scanner_scopes_; - std::array<base::TimeDelta, 1> mutator_scopes_; }; #undef FOR_ALL_PCSCAN_MUTATOR_SCOPES #undef FOR_ALL_PCSCAN_SCANNER_SCOPES -// Internal singleton that keeps cold data. +enum class SimdSupport : uint8_t { + kUnvectorized, + kSSE3, + kAVX2, + // TODO(bikineev): Add support for Neon. +}; + +SimdSupport DetectSimdSupport() { + base::CPU cpu; + if (cpu.has_avx2()) + return SimdSupport::kAVX2; + if (cpu.has_sse3()) + return SimdSupport::kSSE3; + return SimdSupport::kUnvectorized; +} + +// Internal PCScan singleton. The separation between frontend and backend is +// needed to keep access to the hot data (quarantine) in the frontend fast, +// whereas the backend can hold cold data. class PCScanInternal final { public: using Root = PCScan::Root; @@ -373,8 +441,8 @@ static PCScanInternal& Instance() { // Since the data that PCScanInternal holds is cold, it's fine to have the // runtime check for thread-safe local static initialization. - static PCScanInternal instance; - return instance; + static base::NoDestructor<PCScanInternal> instance; + return *instance; } PCScanInternal(const PCScanInternal&) = delete; @@ -395,14 +463,19 @@ // Get size of all committed pages from scannable and nonscannable roots. size_t CalculateTotalHeapSize() const; + SimdSupport simd_support() const { return simd_support_; } + void ClearRootsForTesting(); // IN-TEST private: + friend base::NoDestructor<PCScanInternal>; + PCScanInternal(); Roots scannable_roots_{}; Roots nonscannable_roots_{}; const char* process_name_ = nullptr; + const SimdSupport simd_support_; }; void PCScanInternal::Roots::Add(Root* root) { @@ -418,7 +491,7 @@ current_ = 0; } -PCScanInternal::PCScanInternal() { +PCScanInternal::PCScanInternal() : simd_support_(DetectSimdSupport()) { #if defined(PA_HAS_64_BITS_POINTERS) if (features::IsPartitionAllocGigaCageEnabled()) { PartitionAddressSpace::Init(); @@ -493,8 +566,8 @@ struct ScanArea { ScanArea(uintptr_t* begin, uintptr_t* end) : begin(begin), end(end) {} - uintptr_t* begin; - uintptr_t* end; + uintptr_t* begin = nullptr; + uintptr_t* end = nullptr; }; using ScanAreas = std::vector<ScanArea, MetadataAllocator<ScanArea>>; @@ -548,8 +621,8 @@ typename Root::ScopedGuard guard(root->lock_); // Take a snapshot of all super pages and scannable slot spans. - // TODO(bikineev): Consider making current_extent lock-free and moving it to - // the concurrent thread. + // TODO(bikineev): Consider making current_extent lock-free and moving it + // to the concurrent thread. for (auto* super_page_extent = root->first_extent; super_page_extent; super_page_extent = super_page_extent->next) { for (char* super_page = super_page_extent->super_page_base; @@ -607,7 +680,8 @@ } // namespace // This class is responsible for performing the entire PCScan task. -class PCScan::PCScanTask final { +// TODO(bikineev): Move PCScan algorithm out of PCScanTask. +class PCScanTask final { public: static void* operator new(size_t size) { return PCScanMetadataAllocator().AllocFlagsNoHooks(0, size); @@ -623,12 +697,14 @@ PCScanTask(PCScanTask&&) noexcept = delete; PCScanTask& operator=(PCScanTask&&) noexcept = delete; - // Execute PCScan. Must be executed only once. - void RunOnce() &&; + // Execute PCScan from the scanner thread. Must be called only once from the + // scanner thread. + void RunFromScanner(); private: class ScanLoop; + using Root = PCScan::Root; using SlotSpan = SlotSpanMetadata<ThreadSafe>; struct GigaCageLookupPolicy { @@ -669,15 +745,17 @@ // Scans all registeres partitions and marks reachable quarantined objects. // Returns the size of marked objects. - size_t ScanPartitions(); + void ScanPartitions(); - // Clear quarantined objects and filter out super pages that don't contain - // quarantine. - void ClearQuarantinedObjectsAndFilterSuperPages(); + // Clear quarantined objects and prepare card table for fast lookup + void ClearQuarantinedObjectsAndPrepareCardTable(); // Sweeps (frees) unreachable quarantined entries. Returns the size of swept // objects. - size_t SweepQuarantine(); + void SweepQuarantine(); + + // Finishes the scanner (updates limits, UMA, etc). + void FinishScanner(); // Cache the pcscan epoch to avoid the compiler loading the atomic // QuarantineData::epoch_ on each access. @@ -688,8 +766,8 @@ }; template <typename LookupPolicy> -ALWAYS_INLINE QuarantineBitmap* -PCScan::PCScanTask::TryFindScannerBitmapForPointer(uintptr_t maybe_ptr) const { +ALWAYS_INLINE QuarantineBitmap* PCScanTask::TryFindScannerBitmapForPointer( + uintptr_t maybe_ptr) const { // First, check if |maybe_ptr| points to a valid super page or a quarantined // card. LookupPolicy lookup{snapshot_}; @@ -717,7 +795,7 @@ // entries in the scanner bitmap correspond to unreachable objects. template <typename LookupPolicy> ALWAYS_INLINE size_t -PCScan::PCScanTask::TryMarkObjectInNormalBucketPool(uintptr_t maybe_ptr) const { +PCScanTask::TryMarkObjectInNormalBucketPool(uintptr_t maybe_ptr) const { using AccessType = QuarantineBitmap::AccessType; // Check if maybe_ptr points somewhere to the heap. auto* scanner_bitmap = @@ -755,22 +833,17 @@ return target_slot_span->bucket->slot_size; } -void PCScan::PCScanTask::ClearQuarantinedObjectsAndFilterSuperPages() { +void PCScanTask::ClearQuarantinedObjectsAndPrepareCardTable() { using AccessType = QuarantineBitmap::AccessType; - StatsCollector::ScannerScope clear_scope(stats_, - StatsCollector::ScannerId::kClear); - const bool giga_cage_enabled = features::IsPartitionAllocGigaCageEnabled(); - PCScanSnapshot::SuperPages filtered_super_pages; for (auto super_page : snapshot_.quarantinable_super_pages()) { auto* bitmap = QuarantineBitmapFromPointer( QuarantineBitmapType::kScanner, pcscan_epoch_, reinterpret_cast<char*>(super_page)); auto* root = Root::FromSuperPage(reinterpret_cast<char*>(super_page)); - bool visited = false; bitmap->template Iterate<AccessType::kNonAtomic>( - [root, giga_cage_enabled, &visited](uintptr_t ptr) { + [root, giga_cage_enabled](uintptr_t ptr) { auto* object = reinterpret_cast<void*>(ptr); auto* slot_span = SlotSpan::FromSlotInnerPtr(object); // Use zero as a zapping value to speed up the fast bailout check in @@ -785,21 +858,13 @@ #else (void)giga_cage_enabled; #endif - visited = true; }); - if (visited) { - // Filter out super pages that don't contain quarantined objects to bail - // out earlier in the fast path (and avoid expensive cache-misses while - // checking the quarantine bit). - filtered_super_pages.insert(super_page); - } } - snapshot_.quarantinable_super_pages() = std::move(filtered_super_pages); } // Class used to perform actual scanning. Dispatches at runtime based on // supported SIMD extensions. -class PCScan::PCScanTask::ScanLoop final { +class PCScanTask::ScanLoop final { public: explicit ScanLoop(const PCScanTask& pcscan_task) : scan_function_(GetScanFunction()), @@ -832,16 +897,16 @@ if (UNLIKELY(!features::IsPartitionAllocGigaCageEnabled())) { return &ScanLoop::RunUnvectorizedNoGigaCage; } - // We define vectorized versions of the scanning loop only for 64bit since - // they require support of the 64bit GigaCage, and only for x86 because - // a special instruction set is required. +// We allow vectorization only for 64bit since they require support of the +// 64bit GigaCage, and only for x86 because a special instruction set is +// required. #if defined(ARCH_CPU_X86_64) - base::CPU cpu; - if (cpu.has_avx2()) + const SimdSupport simd = PCScanInternal::Instance().simd_support(); + if (simd == SimdSupport::kAVX2) return &ScanLoop::RunAVX2; - if (cpu.has_sse3()) + if (simd == SimdSupport::kSSE3) return &ScanLoop::RunSSE3; -#endif // defined(ARCH_CPU_X86_64) +#endif return &ScanLoop::RunUnvectorized; } @@ -993,16 +1058,16 @@ #endif }; -size_t PCScan::PCScanTask::ScanPartitions() { - StatsCollector::ScannerScope scan_scope(stats_, - StatsCollector::ScannerId::kScan); +PCScanTask::PCScanTask(PCScan& pcscan) + : pcscan_epoch_(pcscan.quarantine_data_.epoch()), + stats_(PCScanInternal::Instance().process_name()), + pcscan_(pcscan) {} +void PCScanTask::ScanPartitions() { const ScanLoop scan_loop(*this); - - size_t new_quarantine_size = 0; - // For scanning large areas, it's worthwhile checking whether the range that // is scanned contains quarantined objects. + size_t quarantine_size = 0; for (auto scan_area : snapshot_.large_scan_areas()) { // The bitmap is (a) always guaranteed to exist and (b) the same for all // objects in a given slot span. @@ -1022,25 +1087,23 @@ uintptr_t* current_slot_end = current_slot + (scan_area.slot_size / sizeof(uintptr_t)); PA_DCHECK(current_slot_end <= scan_area.end); - new_quarantine_size += scan_loop.Run(current_slot, current_slot_end); + quarantine_size += scan_loop.Run(current_slot, current_slot_end); } } + // Scan areas with regular size slots. for (auto scan_area : snapshot_.scan_areas()) { - new_quarantine_size += scan_loop.Run(scan_area.begin, scan_area.end); + quarantine_size += scan_loop.Run(scan_area.begin, scan_area.end); } - return new_quarantine_size; + stats_.IncreaseSurvivedQuarantineSize(quarantine_size); } -size_t PCScan::PCScanTask::SweepQuarantine() { +void PCScanTask::SweepQuarantine() { using AccessType = QuarantineBitmap::AccessType; - StatsCollector::ScannerScope sweep_scope(stats_, - StatsCollector::ScannerId::kSweep); + const bool giga_cage_enabled = features::IsPartitionAllocGigaCageEnabled(); size_t swept_bytes = 0; - const bool giga_cage_enabled = features::IsPartitionAllocGigaCageEnabled(); - - for (auto super_page : snapshot_.quarantinable_super_pages()) { + for (uintptr_t super_page : snapshot_.quarantinable_super_pages()) { auto* bitmap = QuarantineBitmapFromPointer( QuarantineBitmapType::kScanner, pcscan_epoch_, reinterpret_cast<char*>(super_page)); @@ -1067,46 +1130,54 @@ }); } - return swept_bytes; + stats_.IncreaseSweptSize(swept_bytes); } -PCScan::PCScanTask::PCScanTask(PCScan& pcscan) - : pcscan_epoch_(pcscan.quarantine_data_.epoch()), - stats_(PCScanInternal::Instance().process_name()), - pcscan_(pcscan) {} - -void PCScan::PCScanTask::RunOnce() && { - size_t new_quarantine_size = 0; - size_t swept_bytes = 0; - - // Take snapshot of partition-alloc heap. - snapshot_.Take(pcscan_epoch_); - - { - StatsCollector::ScannerScope overall_scope( - stats_, StatsCollector::ScannerId::kOverall); - - // Clear all quarantined objects and filter out super pages that - // don't contain quarantined objects. - ClearQuarantinedObjectsAndFilterSuperPages(); - - // Mark and sweep the quarantine list. - new_quarantine_size = ScanPartitions(); - swept_bytes = SweepQuarantine(); - } - +void PCScanTask::FinishScanner() { stats_.UpdateHistograms(); - LogStats(swept_bytes, pcscan_.quarantine_data_.last_size(), - new_quarantine_size); + LogStats(stats_.swept_size(), pcscan_.quarantine_data_.last_size(), + stats_.survived_quarantine_size()); const size_t total_pa_heap_size = PCScanInternal::Instance().CalculateTotalHeapSize(); - pcscan_.quarantine_data_.Account(new_quarantine_size); + pcscan_.quarantine_data_.Account(stats_.survived_quarantine_size()); pcscan_.quarantine_data_.GrowLimitIfNeeded(total_pa_heap_size); // Check that concurrent task can't be scheduled twice. - PA_CHECK(pcscan_.in_progress_.exchange(false, std::memory_order_acq_rel)); + PA_CHECK(pcscan_.state_.exchange(PCScan::State::kNotRunning, + std::memory_order_acq_rel) == + PCScan::State::kSweepingAndFinishing); +} + +void PCScanTask::RunFromScanner() { + { + StatsCollector::ScannerScope overall_scope( + stats_, StatsCollector::ScannerId::kOverall); + // Take snapshot of partition-alloc heap. + snapshot_.Take(pcscan_epoch_); + { + // Clear all quarantined objects and prepare the card table. + StatsCollector::ScannerScope clear_scope( + stats_, StatsCollector::ScannerId::kClear); + ClearQuarantinedObjectsAndPrepareCardTable(); + } + { + // Scan heap for dangling references. + StatsCollector::ScannerScope scan_scope(stats_, + StatsCollector::ScannerId::kScan); + ScanPartitions(); + } + pcscan_.state_.store(PCScan::State::kSweepingAndFinishing, + std::memory_order_relaxed); + { + // Sweep unreachable quarantined objects. + StatsCollector::ScannerScope sweep_scope( + stats_, StatsCollector::ScannerId::kSweep); + SweepQuarantine(); + } + } + FinishScanner(); } class PCScan::PCScanThread final { @@ -1149,7 +1220,7 @@ condvar_.wait(lock, [this] { return posted_task_.get(); }); std::swap(current_task, posted_task_); } - std::move(*current_task).RunOnce(); + std::move(*current_task).RunFromScanner(); } } @@ -1188,23 +1259,26 @@ [](Root* root) { return root->IsQuarantineEnabled(); })); #endif - if (in_progress_.exchange(true, std::memory_order_acq_rel)) { + if (state_.exchange(State::kScheduled, std::memory_order_acq_rel) != + State::kNotRunning) { // Bail out if PCScan is already in progress. return; } quarantine_data_.ResetAndAdvanceEpoch(); - // Initialize PCScan task. + // Create PCScan task. auto task = std::make_unique<PCScanTask>(*this); + state_.store(State::kScanning, std::memory_order_release); + // Post PCScan task. if (LIKELY(invocation_mode == InvocationMode::kNonBlocking)) { PCScanThread::Instance().PostTask(std::move(task)); } else { PA_DCHECK(InvocationMode::kBlocking == invocation_mode || InvocationMode::kForcedBlocking == invocation_mode); - std::move(*task).RunOnce(); + std::move(*task).RunFromScanner(); } }
diff --git a/base/allocator/partition_allocator/pcscan.h b/base/allocator/partition_allocator/pcscan.h index 816643f..1a4a813 100644 --- a/base/allocator/partition_allocator/pcscan.h +++ b/base/allocator/partition_allocator/pcscan.h
@@ -26,6 +26,8 @@ namespace base { namespace internal { +class PCScanTask; + [[noreturn]] BASE_EXPORT NOINLINE NOT_TAIL_CALLED void DoubleFreeAttempt(); // PCScan (Probabilistic Conservative Scanning) is the algorithm that eliminates @@ -74,9 +76,7 @@ void PerformScanIfNeeded(InvocationMode invocation_mode); // Checks if there is a PCScan task currently in progress. - ALWAYS_INLINE bool IsInProgress() const { - return in_progress_.load(std::memory_order_relaxed); - } + ALWAYS_INLINE bool IsInProgress() const; // Sets process name (used for histograms). |name| must be a string literal. void SetProcessName(const char* name); @@ -84,8 +84,8 @@ void ClearRootsForTesting(); private: - class PCScanTask; class PCScanThread; + friend class PCScanTask; friend class PCScanTest; class QuarantineData final { @@ -117,6 +117,17 @@ size_t last_size_ = 0; }; + enum class State : uint8_t { + // PCScan task is not scheduled. + kNotRunning, + // PCScan task is being started and about to be scheduled. + kScheduled, + // PCScan task is scheduled and can be scanning (or clearing). + kScanning, + // PCScan task is sweeping or finalizing. + kSweepingAndFinishing + }; + inline constexpr PCScan(); // Performs scanning unconditionally. @@ -126,7 +137,7 @@ static PCScan instance_ PA_CONSTINIT; QuarantineData quarantine_data_{}; - std::atomic<bool> in_progress_{false}; + std::atomic<State> state_{State::kNotRunning}; }; // To please Chromium's clang plugin. @@ -140,6 +151,10 @@ // To please Chromium's clang plugin. constexpr PCScan::PCScan() = default; +ALWAYS_INLINE bool PCScan::IsInProgress() const { + return state_.load(std::memory_order_relaxed) != State::kNotRunning; +} + ALWAYS_INLINE void PCScan::MoveToQuarantine(void* ptr, size_t slot_size) { auto* quarantine = QuarantineBitmapFromPointer(QuarantineBitmapType::kMutator, quarantine_data_.epoch(), ptr); @@ -151,7 +166,7 @@ const bool is_limit_reached = quarantine_data_.Account(slot_size); if (UNLIKELY(is_limit_reached)) { // Perform a quick check if another scan is already in progress. - if (in_progress_.load(std::memory_order_relaxed)) + if (IsInProgress()) return; // Avoid blocking the current thread for regular scans. PerformScan(InvocationMode::kNonBlocking);
diff --git a/base/strings/nullable_string16.cc b/base/strings/nullable_string16.cc deleted file mode 100644 index 118e817..0000000 --- a/base/strings/nullable_string16.cc +++ /dev/null
@@ -1,33 +0,0 @@ -// Copyright (c) 2013 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/strings/nullable_string16.h" - -#include <ostream> -#include <utility> - -namespace base { -NullableString16::NullableString16() = default; -NullableString16::NullableString16(const NullableString16& other) = default; -NullableString16::NullableString16(NullableString16&& other) = default; - -NullableString16::NullableString16(const std::u16string& string, bool is_null) { - if (!is_null) - string_.emplace(string); -} - -NullableString16::NullableString16(Optional<std::u16string> optional_string16) - : string_(std::move(optional_string16)) {} - -NullableString16::~NullableString16() = default; -NullableString16& NullableString16::operator=(const NullableString16& other) = - default; -NullableString16& NullableString16::operator=(NullableString16&& other) = - default; - -std::ostream& operator<<(std::ostream& out, const NullableString16& value) { - return value.is_null() ? out << "(null)" : out << value.string(); -} - -} // namespace base
diff --git a/base/strings/nullable_string16.h b/base/strings/nullable_string16.h deleted file mode 100644 index fdce54b4..0000000 --- a/base/strings/nullable_string16.h +++ /dev/null
@@ -1,57 +0,0 @@ -// Copyright (c) 2010 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 BASE_STRINGS_NULLABLE_STRING16_H_ -#define BASE_STRINGS_NULLABLE_STRING16_H_ - -#include <iosfwd> -#include <string> - -#include "base/base_export.h" -#include "base/optional.h" -#include "base/strings/string_util.h" - -namespace base { - -// This class is a simple wrapper for std::u16string which also contains a null -// state. This should be used only where the difference between null and -// empty is meaningful. -class BASE_EXPORT NullableString16 { - public: - NullableString16(); - NullableString16(const NullableString16& other); - NullableString16(NullableString16&& other); - NullableString16(const std::u16string& string, bool is_null); - explicit NullableString16(Optional<std::u16string> optional_string16); - ~NullableString16(); - - NullableString16& operator=(const NullableString16& other); - NullableString16& operator=(NullableString16&& other); - - const std::u16string& string() const { - return string_ ? *string_ : EmptyString16(); - } - bool is_null() const { return !string_; } - const Optional<std::u16string>& as_optional_string16() const { - return string_; - } - - private: - Optional<std::u16string> string_; -}; - -inline bool operator==(const NullableString16& a, const NullableString16& b) { - return a.as_optional_string16() == b.as_optional_string16(); -} - -inline bool operator!=(const NullableString16& a, const NullableString16& b) { - return !(a == b); -} - -BASE_EXPORT std::ostream& operator<<(std::ostream& out, - const NullableString16& value); - -} // namespace base - -#endif // BASE_STRINGS_NULLABLE_STRING16_H_
diff --git a/base/strings/nullable_string16_unittest.cc b/base/strings/nullable_string16_unittest.cc deleted file mode 100644 index 3654e38..0000000 --- a/base/strings/nullable_string16_unittest.cc +++ /dev/null
@@ -1,35 +0,0 @@ -// Copyright 2013 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/strings/nullable_string16.h" -#include "base/strings/utf_string_conversions.h" -#include "testing/gtest/include/gtest/gtest.h" - -namespace base { - -TEST(NullableString16Test, DefaultConstructor) { - NullableString16 s; - EXPECT_TRUE(s.is_null()); - EXPECT_EQ(std::u16string(), s.string()); -} - -TEST(NullableString16Test, Equals) { - NullableString16 a(ASCIIToUTF16("hello"), false); - NullableString16 b(ASCIIToUTF16("hello"), false); - EXPECT_EQ(a, b); -} - -TEST(NullableString16Test, NotEquals) { - NullableString16 a(ASCIIToUTF16("hello"), false); - NullableString16 b(ASCIIToUTF16("world"), false); - EXPECT_NE(a, b); -} - -TEST(NullableString16Test, NotEqualsNull) { - NullableString16 a(ASCIIToUTF16("hello"), false); - NullableString16 b; - EXPECT_NE(a, b); -} - -} // namespace base
diff --git a/base/threading/thread_restrictions.h b/base/threading/thread_restrictions.h index aa30785..dc5dacc0 100644 --- a/base/threading/thread_restrictions.h +++ b/base/threading/thread_restrictions.h
@@ -123,6 +123,7 @@ class RTCVideoEncoder; class SourceStream; class VideoFrameResourceProvider; +class WebRtcVideoFrameAdapter; class LegacyWebRtcVideoFrameAdapter; class WorkerThread; namespace scheduler { @@ -537,6 +538,7 @@ friend class base::StackSamplingProfiler; friend class blink::RTCVideoDecoderAdapter; friend class blink::RTCVideoEncoder; + friend class blink::WebRtcVideoFrameAdapter; friend class blink::LegacyWebRtcVideoFrameAdapter; friend class cc::TileTaskManagerImpl; friend class content::CategorizedWorkerPool;
diff --git a/cc/base/devtools_instrumentation.cc b/cc/base/devtools_instrumentation.cc index b65c154..c3cc859 100644 --- a/cc/base/devtools_instrumentation.cc +++ b/cc/base/devtools_instrumentation.cc
@@ -4,6 +4,8 @@ #include "cc/base/devtools_instrumentation.h" +#include <string> + namespace cc { namespace devtools_instrumentation { namespace { @@ -36,6 +38,7 @@ const char kLayerId[] = "layerId"; const char kLayerTreeId[] = "layerTreeId"; const char kPixelRefId[] = "pixelRefId"; +const char kPresentationTimestamp[] = "presentationTimestamp"; const char kImageUploadTask[] = "ImageUploadTask"; const char kImageDecodeTask[] = "ImageDecodeTask";
diff --git a/cc/base/devtools_instrumentation.h b/cc/base/devtools_instrumentation.h index 02f0254..b27c77c 100644 --- a/cc/base/devtools_instrumentation.h +++ b/cc/base/devtools_instrumentation.h
@@ -8,6 +8,7 @@ #include <stdint.h> #include <memory> +#include <utility> #include "base/metrics/histogram_functions.h" #include "base/metrics/histogram_macros.h" @@ -32,6 +33,7 @@ CC_BASE_EXPORT extern const char kLayerId[]; CC_BASE_EXPORT extern const char kLayerTreeId[]; CC_BASE_EXPORT extern const char kPixelRefId[]; +CC_BASE_EXPORT extern const char kPresentationTimestamp[]; CC_BASE_EXPORT extern const char kImageDecodeTask[]; CC_BASE_EXPORT extern const char kBeginFrame[]; @@ -181,10 +183,31 @@ internal::kLayerTreeId, layer_tree_host_id); } -inline void CC_BASE_EXPORT DidDrawFrame(int layer_tree_host_id) { - TRACE_EVENT_INSTANT1(internal::CategoryName::kTimelineFrame, - internal::kDrawFrame, TRACE_EVENT_SCOPE_THREAD, - internal::kLayerTreeId, layer_tree_host_id); +constexpr uint64_t GetUniqueIDFromLayerTreeHostIdAndFrameToken( + int layer_tree_host_id, + uint32_t frame_token) { + return static_cast<uint64_t>(layer_tree_host_id) << 32 | + static_cast<uint64_t>(frame_token); +} + +inline void CC_BASE_EXPORT DidDrawFrame(int layer_tree_host_id, + uint32_t frame_token) { + TRACE_EVENT_NESTABLE_ASYNC_BEGIN1(internal::CategoryName::kTimelineFrame, + internal::kDrawFrame, + GetUniqueIDFromLayerTreeHostIdAndFrameToken( + layer_tree_host_id, frame_token), + internal::kLayerTreeId, layer_tree_host_id); +} + +inline void CC_BASE_EXPORT +DidPresentFrame(int layer_tree_host_id, + uint32_t frame_token, + base::TimeTicks presentation_timestamp) { + TRACE_EVENT_NESTABLE_ASYNC_END1( + internal::CategoryName::kTimelineFrame, internal::kDrawFrame, + GetUniqueIDFromLayerTreeHostIdAndFrameToken(layer_tree_host_id, + frame_token), + internal::kPresentationTimestamp, presentation_timestamp); } inline void CC_BASE_EXPORT DidRequestMainThreadFrame(int layer_tree_host_id) {
diff --git a/cc/metrics/compositor_frame_reporter.cc b/cc/metrics/compositor_frame_reporter.cc index fcad7a8c..93be01c4 100644 --- a/cc/metrics/compositor_frame_reporter.cc +++ b/cc/metrics/compositor_frame_reporter.cc
@@ -46,6 +46,12 @@ constexpr int kFrameSequenceTrackerTypeCount = static_cast<int>(FrameSequenceTrackerType::kMaxType) + 1; +// Maximum number of partial update dependents a reporter can own. When a +// reporter with too many dependents is terminated, it will terminate all its +// dependents which will block the pipeline for a long time. Too many dependents +// also means too much memory usage. +constexpr size_t kMaxOwnedPartialUpdateDependents = 300u; + // Names for the viz breakdowns that are shown in trace as substages under // PipelineReporter -> SubmitCompositorFrameToPresentationCompositorFrame or // EventLatency -> SubmitCompositorFrameToPresentationCompositorFrame. @@ -576,7 +582,7 @@ // Set up the new reporter so that it depends on |this| for partial update // information. - new_reporter->SetPartialUpdateDecider(weak_factory_.GetWeakPtr()); + new_reporter->SetPartialUpdateDecider(this); return new_reporter; } @@ -1255,10 +1261,6 @@ return false; } -base::WeakPtr<CompositorFrameReporter> CompositorFrameReporter::GetWeakPtr() { - return weak_factory_.GetWeakPtr(); -} - void CompositorFrameReporter::AdoptReporter( std::unique_ptr<CompositorFrameReporter> reporter) { // If |this| reporter is dependent on another reporter to decide about partial @@ -1270,32 +1272,36 @@ } void CompositorFrameReporter::SetPartialUpdateDecider( - base::WeakPtr<CompositorFrameReporter> decider) { + CompositorFrameReporter* decider) { DCHECK(decider); - has_partial_update_ = true; - partial_update_decider_ = decider; - decider->partial_update_dependents_.push(GetWeakPtr()); DCHECK(partial_update_dependents_.empty()); + has_partial_update_ = true; + partial_update_decider_ = decider->GetWeakPtr(); + decider->partial_update_dependents_.push(GetWeakPtr()); } void CompositorFrameReporter::DiscardOldPartialUpdateReporters() { DCHECK_LE(owned_partial_update_dependents_.size(), partial_update_dependents_.size()); - while (owned_partial_update_dependents_.size() > 300u) { + // Remove old owned partial update dependents if there are too many. + while (owned_partial_update_dependents_.size() > + kMaxOwnedPartialUpdateDependents) { auto& dependent = owned_partial_update_dependents_.front(); dependent->set_has_partial_update(false); - partial_update_dependents_.pop(); owned_partial_update_dependents_.pop(); discarded_partial_update_dependents_count_++; } + + // Remove dependent reporters from the front of `partial_update_dependents_` + // queue if they are already destroyed. + while (!partial_update_dependents_.empty() && + !partial_update_dependents_.front()) { + partial_update_dependents_.pop(); + } } -bool CompositorFrameReporter::MightHavePartialUpdate() const { - return !!partial_update_decider_; -} - -size_t CompositorFrameReporter::GetPartialUpdateDependentsCount() const { - return partial_update_dependents_.size(); +base::WeakPtr<CompositorFrameReporter> CompositorFrameReporter::GetWeakPtr() { + return weak_factory_.GetWeakPtr(); } } // namespace cc
diff --git a/cc/metrics/compositor_frame_reporter.h b/cc/metrics/compositor_frame_reporter.h index 0e9f000..67c2614 100644 --- a/cc/metrics/compositor_frame_reporter.h +++ b/cc/metrics/compositor_frame_reporter.h
@@ -256,7 +256,7 @@ void SetVizBreakdown(const viz::FrameTimingDetails& viz_breakdown); void SetEventsMetrics(EventMetrics::List events_metrics); - int StageHistorySizeForTesting() { return stage_history_.size(); } + int stage_history_size_for_testing() const { return stage_history_.size(); } void OnFinishImplFrame(base::TimeTicks timestamp); void OnAbortBeginMainFrame(base::TimeTicks timestamp); @@ -288,10 +288,15 @@ tick_clock_ = tick_clock; } - void SetPartialUpdateDecider(base::WeakPtr<CompositorFrameReporter> decider); + void SetPartialUpdateDecider(CompositorFrameReporter* decider); - bool MightHavePartialUpdate() const; - size_t GetPartialUpdateDependentsCount() const; + size_t partial_update_dependents_size_for_testing() const { + return partial_update_dependents_.size(); + } + + size_t owned_partial_update_dependents_size_for_testing() const { + return owned_partial_update_dependents_.size(); + } const viz::BeginFrameId& frame_id() const { return args_.frame_id; } @@ -303,12 +308,10 @@ // If this is a cloned reporter, then this returns a weak-ptr to the original // reporter this was cloned from (using |CopyReporterAtBeginImplStage()|). - base::WeakPtr<CompositorFrameReporter> partial_update_decider() { - return partial_update_decider_; + CompositorFrameReporter* partial_update_decider() const { + return partial_update_decider_.get(); } - base::WeakPtr<CompositorFrameReporter> GetWeakPtr(); - protected: void set_has_partial_update(bool has_partial_update) { has_partial_update_ = has_partial_update; @@ -354,6 +357,8 @@ bool IsDroppedFrameAffectingSmoothness() const; + base::WeakPtr<CompositorFrameReporter> GetWeakPtr(); + const bool should_report_metrics_; const viz::BeginFrameArgs args_;
diff --git a/cc/metrics/compositor_frame_reporter_unittest.cc b/cc/metrics/compositor_frame_reporter_unittest.cc index e7a38ba..a7b9cdc 100644 --- a/cc/metrics/compositor_frame_reporter_unittest.cc +++ b/cc/metrics/compositor_frame_reporter_unittest.cc
@@ -30,16 +30,7 @@ class CompositorFrameReporterTest : public testing::Test { public: - CompositorFrameReporterTest() - : pipeline_reporter_(std::make_unique<CompositorFrameReporter>( - CompositorFrameReporter::ActiveTrackers(), - viz::BeginFrameArgs(), - nullptr, - /*should_report_metrics=*/true, - CompositorFrameReporter::SmoothThread::kSmoothBoth, - /*layer_tree_host_id=*/1, - &dropped_frame_counter_)) { - pipeline_reporter_->set_tick_clock(&test_tick_clock_); + CompositorFrameReporterTest() : pipeline_reporter_(CreatePipelineReporter()) { AdvanceNowByMs(1); dropped_frame_counter_.set_total_counter(&total_frame_counter_); } @@ -115,6 +106,17 @@ return event_times; } + std::unique_ptr<CompositorFrameReporter> CreatePipelineReporter() { + auto reporter = std::make_unique<CompositorFrameReporter>( + CompositorFrameReporter::ActiveTrackers(), viz::BeginFrameArgs(), + /*latency_ukm_reporter=*/nullptr, + /*should_report_metrics=*/true, + CompositorFrameReporter::SmoothThread::kSmoothBoth, + /*layer_tree_host_id=*/1, &dropped_frame_counter_); + reporter->set_tick_clock(&test_tick_clock_); + return reporter; + } + // This should be defined before |pipeline_reporter_| so it is created before // and destroyed after that. base::SimpleTestTickClock test_tick_clock_; @@ -130,30 +132,30 @@ pipeline_reporter_->StartStage( CompositorFrameReporter::StageType::kBeginImplFrameToSendBeginMainFrame, Now()); - EXPECT_EQ(0, pipeline_reporter_->StageHistorySizeForTesting()); + EXPECT_EQ(0, pipeline_reporter_->stage_history_size_for_testing()); AdvanceNowByMs(3); pipeline_reporter_->StartStage( CompositorFrameReporter::StageType::kSendBeginMainFrameToCommit, Now()); - EXPECT_EQ(1, pipeline_reporter_->StageHistorySizeForTesting()); + EXPECT_EQ(1, pipeline_reporter_->stage_history_size_for_testing()); AdvanceNowByMs(3); pipeline_reporter_->StartStage( CompositorFrameReporter::StageType::kEndActivateToSubmitCompositorFrame, Now()); - EXPECT_EQ(2, pipeline_reporter_->StageHistorySizeForTesting()); + EXPECT_EQ(2, pipeline_reporter_->stage_history_size_for_testing()); AdvanceNowByMs(3); pipeline_reporter_->StartStage( CompositorFrameReporter::StageType:: kSubmitCompositorFrameToPresentationCompositorFrame, Now()); - EXPECT_EQ(3, pipeline_reporter_->StageHistorySizeForTesting()); + EXPECT_EQ(3, pipeline_reporter_->stage_history_size_for_testing()); AdvanceNowByMs(3); pipeline_reporter_->TerminateFrame( CompositorFrameReporter::FrameTerminationStatus::kPresentedFrame, Now()); - EXPECT_EQ(4, pipeline_reporter_->StageHistorySizeForTesting()); + EXPECT_EQ(4, pipeline_reporter_->stage_history_size_for_testing()); pipeline_reporter_ = nullptr; histogram_tester.ExpectTotalCount( @@ -175,18 +177,18 @@ pipeline_reporter_->StartStage(CompositorFrameReporter::StageType::kCommit, Now()); - EXPECT_EQ(0, pipeline_reporter_->StageHistorySizeForTesting()); + EXPECT_EQ(0, pipeline_reporter_->stage_history_size_for_testing()); AdvanceNowByMs(3); pipeline_reporter_->StartStage( CompositorFrameReporter::StageType::kEndCommitToActivation, Now()); - EXPECT_EQ(1, pipeline_reporter_->StageHistorySizeForTesting()); + EXPECT_EQ(1, pipeline_reporter_->stage_history_size_for_testing()); AdvanceNowByMs(2); pipeline_reporter_->TerminateFrame( CompositorFrameReporter::FrameTerminationStatus::kReplacedByNewReporter, Now()); - EXPECT_EQ(2, pipeline_reporter_->StageHistorySizeForTesting()); + EXPECT_EQ(2, pipeline_reporter_->stage_history_size_for_testing()); pipeline_reporter_ = nullptr; histogram_tester.ExpectTotalCount("CompositorLatency.Commit", 0); @@ -199,18 +201,18 @@ pipeline_reporter_->StartStage( CompositorFrameReporter::StageType::kActivation, Now()); - EXPECT_EQ(0, pipeline_reporter_->StageHistorySizeForTesting()); + EXPECT_EQ(0, pipeline_reporter_->stage_history_size_for_testing()); AdvanceNowByMs(3); pipeline_reporter_->StartStage( CompositorFrameReporter::StageType::kEndActivateToSubmitCompositorFrame, Now()); - EXPECT_EQ(1, pipeline_reporter_->StageHistorySizeForTesting()); + EXPECT_EQ(1, pipeline_reporter_->stage_history_size_for_testing()); AdvanceNowByMs(2); pipeline_reporter_->TerminateFrame( CompositorFrameReporter::FrameTerminationStatus::kPresentedFrame, Now()); - EXPECT_EQ(2, pipeline_reporter_->StageHistorySizeForTesting()); + EXPECT_EQ(2, pipeline_reporter_->stage_history_size_for_testing()); pipeline_reporter_ = nullptr; histogram_tester.ExpectTotalCount("CompositorLatency.Activation", 1); @@ -235,18 +237,18 @@ pipeline_reporter_->StartStage( CompositorFrameReporter::StageType::kSendBeginMainFrameToCommit, Now()); - EXPECT_EQ(0, pipeline_reporter_->StageHistorySizeForTesting()); + EXPECT_EQ(0, pipeline_reporter_->stage_history_size_for_testing()); AdvanceNowByMs(3); pipeline_reporter_->StartStage(CompositorFrameReporter::StageType::kCommit, Now()); - EXPECT_EQ(1, pipeline_reporter_->StageHistorySizeForTesting()); + EXPECT_EQ(1, pipeline_reporter_->stage_history_size_for_testing()); AdvanceNowByMs(2); pipeline_reporter_->TerminateFrame( CompositorFrameReporter::FrameTerminationStatus::kDidNotPresentFrame, Now()); - EXPECT_EQ(2, pipeline_reporter_->StageHistorySizeForTesting()); + EXPECT_EQ(2, pipeline_reporter_->stage_history_size_for_testing()); pipeline_reporter_ = nullptr; histogram_tester.ExpectTotalCount( @@ -480,5 +482,122 @@ IsEmpty()); } +// Verifies that partial update dependent queues are working as expected when +// they reach their maximum capacity. +TEST_F(CompositorFrameReporterTest, PartialUpdateDependentQueues) { + // This constant should match the constant with the same name in + // compositor_frame_reporter.cc. + const size_t kMaxOwnedPartialUpdateDependents = 300u; + + // The first three dependent reporters for the front of the queue. + std::unique_ptr<CompositorFrameReporter> deps[] = { + CreatePipelineReporter(), + CreatePipelineReporter(), + CreatePipelineReporter(), + }; + + // Set `deps[0]` as a dependent of the main reporter and adopt it at the same + // time. This should enqueue it in both non-owned and owned dependents queues. + deps[0]->SetPartialUpdateDecider(pipeline_reporter_.get()); + pipeline_reporter_->AdoptReporter(std::move(deps[0])); + DCHECK_EQ(1u, + pipeline_reporter_->partial_update_dependents_size_for_testing()); + DCHECK_EQ( + 1u, + pipeline_reporter_->owned_partial_update_dependents_size_for_testing()); + + // Set `deps[1]` as a dependent of the main reporter, but don't adopt it yet. + // This should enqueue it in non-owned dependents queue only. + deps[1]->SetPartialUpdateDecider(pipeline_reporter_.get()); + DCHECK_EQ(2u, + pipeline_reporter_->partial_update_dependents_size_for_testing()); + DCHECK_EQ( + 1u, + pipeline_reporter_->owned_partial_update_dependents_size_for_testing()); + + // Set `deps[2]` as a dependent of the main reporter and adopt it at the same + // time. This should enqueue it in both non-owned and owned dependents queues. + deps[2]->SetPartialUpdateDecider(pipeline_reporter_.get()); + pipeline_reporter_->AdoptReporter(std::move(deps[2])); + DCHECK_EQ(3u, + pipeline_reporter_->partial_update_dependents_size_for_testing()); + DCHECK_EQ( + 2u, + pipeline_reporter_->owned_partial_update_dependents_size_for_testing()); + + // Now adopt `deps[1]` to enqueue it in the owned dependents queue. + pipeline_reporter_->AdoptReporter(std::move(deps[1])); + DCHECK_EQ(3u, + pipeline_reporter_->partial_update_dependents_size_for_testing()); + DCHECK_EQ( + 3u, + pipeline_reporter_->owned_partial_update_dependents_size_for_testing()); + + // Fill the queues with more dependent reporters until the capacity is + // reached. After this, the queues should look like this (assuming n equals + // `kMaxOwnedPartialUpdateDependents`): + // Partial Update Dependents: [0, 1, 2, 3, 4, ..., n-1] + // Owned Partial Update Dependents: [0, 2, 1, 3, 4, ..., n-1] + while ( + pipeline_reporter_->owned_partial_update_dependents_size_for_testing() < + kMaxOwnedPartialUpdateDependents) { + std::unique_ptr<CompositorFrameReporter> dependent = + CreatePipelineReporter(); + dependent->SetPartialUpdateDecider(pipeline_reporter_.get()); + pipeline_reporter_->AdoptReporter(std::move(dependent)); + } + DCHECK_EQ(kMaxOwnedPartialUpdateDependents, + pipeline_reporter_->partial_update_dependents_size_for_testing()); + DCHECK_EQ( + kMaxOwnedPartialUpdateDependents, + pipeline_reporter_->owned_partial_update_dependents_size_for_testing()); + + // Enqueue a new dependent reporter. This should pop `deps[0]` from the front + // of the owned dependents queue and destroy it. Since the same one is in + // front of the non-owned dependents queue, it will be popped out of that + // queue, too. The queues will look like this: + // Partial Update Dependents: [1, 2, 3, 4, ..., n] + // Owned Partial Update Dependents: [2, 1, 3, 4, ..., n] + auto new_dep = CreatePipelineReporter(); + new_dep->SetPartialUpdateDecider(pipeline_reporter_.get()); + pipeline_reporter_->AdoptReporter(std::move(new_dep)); + DCHECK_EQ(kMaxOwnedPartialUpdateDependents, + pipeline_reporter_->partial_update_dependents_size_for_testing()); + DCHECK_EQ( + kMaxOwnedPartialUpdateDependents, + pipeline_reporter_->owned_partial_update_dependents_size_for_testing()); + + // Enqueue another new dependent reporter. This should pop `deps[2]` from the + // front of the owned dependents queue and destroy it. Since another reporter + // is in front of the non-owned dependents queue it won't be popped out of + // that queue. The queues will look like this: + // Partial Update Dependents: [2, 3, 4, ..., n+1] + // Owned Partial Update Dependents: [2, nullptr, 3, 4, ..., n+1] + new_dep = CreatePipelineReporter(); + new_dep->SetPartialUpdateDecider(pipeline_reporter_.get()); + pipeline_reporter_->AdoptReporter(std::move(new_dep)); + DCHECK_EQ(kMaxOwnedPartialUpdateDependents + 1, + pipeline_reporter_->partial_update_dependents_size_for_testing()); + DCHECK_EQ( + kMaxOwnedPartialUpdateDependents, + pipeline_reporter_->owned_partial_update_dependents_size_for_testing()); + + // Enqueue yet another new dependent reporter. This should pop `deps[1]` from + // the front of the owned dependents queue and destroy it. Since the same one + // is in front of the non-owned dependents queue followed by `deps[2]` which + // was destroyed in the previous step, they will be popped out of that queue, + // too. The queues will look like this: + // Partial Update Dependents: [3, 4, ..., n+2] + // Owned Partial Update Dependents: [3, 4, ..., n+2] + new_dep = CreatePipelineReporter(); + new_dep->SetPartialUpdateDecider(pipeline_reporter_.get()); + pipeline_reporter_->AdoptReporter(std::move(new_dep)); + DCHECK_EQ(kMaxOwnedPartialUpdateDependents, + pipeline_reporter_->partial_update_dependents_size_for_testing()); + DCHECK_EQ( + kMaxOwnedPartialUpdateDependents, + pipeline_reporter_->owned_partial_update_dependents_size_for_testing()); +} + } // namespace } // namespace cc
diff --git a/cc/metrics/compositor_frame_reporting_controller.cc b/cc/metrics/compositor_frame_reporting_controller.cc index dec573d1..d6103ee 100644 --- a/cc/metrics/compositor_frame_reporting_controller.cc +++ b/cc/metrics/compositor_frame_reporting_controller.cc
@@ -222,8 +222,8 @@ AdvanceReporterStage(PipelineStage::kBeginImplFrame, PipelineStage::kActivate); impl_reporter = std::move(reporters_[PipelineStage::kActivate]); - auto partial_update_decider = - HasOutstandingUpdatesFromMain(current_frame_id); + CompositorFrameReporter* partial_update_decider = + GetOutstandingUpdatesFromMain(current_frame_id); if (partial_update_decider) impl_reporter->SetPartialUpdateDecider(partial_update_decider); } else if (CanSubmitMainFrame(current_frame_id)) { @@ -333,8 +333,8 @@ } else { // The stage_reporter in this case was waiting for main, so needs to // be adopted by the reporter which is waiting on Main thread's work - auto partial_update_decider = - HasOutstandingUpdatesFromMain(stage_reporter->frame_id()); + CompositorFrameReporter* partial_update_decider = + GetOutstandingUpdatesFromMain(stage_reporter->frame_id()); if (partial_update_decider) { stage_reporter->SetPartialUpdateDecider(partial_update_decider); stage_reporter->OnDidNotProduceFrame(FrameSkippedReason::kWaitingOnMain); @@ -383,11 +383,11 @@ // the original reporter, so that the cloned reporter stays alive until the // original reporter is terminated, and the cloned reporter's 'partial // update' flag can be unset if necessary. - if (reporter->MightHavePartialUpdate()) { - auto orig_reporter = reporter->partial_update_decider(); - if (orig_reporter) - orig_reporter->AdoptReporter(std::move(reporter)); + if (CompositorFrameReporter* orig_reporter = + reporter->partial_update_decider()) { + orig_reporter->AdoptReporter(std::move(reporter)); } + submitted_compositor_frames_.erase(submitted_frame); } } @@ -532,8 +532,8 @@ return smooth_thread_history_.lower_bound(timestamp)->second; } -base::WeakPtr<CompositorFrameReporter> -CompositorFrameReportingController::HasOutstandingUpdatesFromMain( +CompositorFrameReporter* +CompositorFrameReportingController::GetOutstandingUpdatesFromMain( const viz::BeginFrameId& id) const { // Any unterminated reporter in the 'main frame', or 'commit' stages, then // that indicates some pending updates from the main thread. @@ -541,17 +541,17 @@ const auto& reporter = reporters_[PipelineStage::kBeginMainFrame]; if (reporter && reporter->frame_id() < id && !reporter->did_abort_main_frame()) { - return reporter->GetWeakPtr(); + return reporter.get(); } } { const auto& reporter = reporters_[PipelineStage::kCommit]; if (reporter && reporter->frame_id() < id) { DCHECK(!reporter->did_abort_main_frame()); - return reporter->GetWeakPtr(); + return reporter.get(); } } - return {}; + return nullptr; } void CompositorFrameReportingController::CreateReportersForDroppedFrames(
diff --git a/cc/metrics/compositor_frame_reporting_controller.h b/cc/metrics/compositor_frame_reporting_controller.h index 9054c0a..46eb58c 100644 --- a/cc/metrics/compositor_frame_reporting_controller.h +++ b/cc/metrics/compositor_frame_reporting_controller.h
@@ -126,9 +126,9 @@ base::TimeTicks timestamp) const; // Checks whether there are reporters containing updates from the main - // thread, and returns a weak-ptr to that reporter (if any). Otherwise returns - // null. - base::WeakPtr<CompositorFrameReporter> HasOutstandingUpdatesFromMain( + // thread, and returns a pointer to that reporter (if any). Otherwise returns + // nullptr. + CompositorFrameReporter* GetOutstandingUpdatesFromMain( const viz::BeginFrameId& id) const; // If the display-compositor skips over some frames (e.g. when the gpu is
diff --git a/cc/metrics/compositor_frame_reporting_controller_unittest.cc b/cc/metrics/compositor_frame_reporting_controller_unittest.cc index 8f13ac70..c2d5297e 100644 --- a/cc/metrics/compositor_frame_reporting_controller_unittest.cc +++ b/cc/metrics/compositor_frame_reporting_controller_unittest.cc
@@ -66,7 +66,8 @@ }; for (auto stage : kStages) { auto& reporter = reporters()[stage]; - if (reporter && reporter->GetPartialUpdateDependentsCount() > 0) { + if (reporter && + reporter->partial_update_dependents_size_for_testing() > 0) { ++count; } } @@ -84,7 +85,7 @@ for (auto stage : kStages) { auto& reporter = reporters()[stage]; if (reporter) - count += reporter->GetPartialUpdateDependentsCount(); + count += reporter->partial_update_dependents_size_for_testing(); } return count; }
diff --git a/cc/trees/layer_tree_host_impl.cc b/cc/trees/layer_tree_host_impl.cc index 6423a98..b184cb2 100644 --- a/cc/trees/layer_tree_host_impl.cc +++ b/cc/trees/layer_tree_host_impl.cc
@@ -2035,6 +2035,8 @@ void LayerTreeHostImpl::DidPresentCompositorFrame( uint32_t frame_token, const viz::FrameTimingDetails& details) { + devtools_instrumentation::DidPresentFrame( + id_, frame_token, details.presentation_feedback.timestamp); PresentationTimeCallbackBuffer::PendingCallbacks activated_callbacks = presentation_time_callbacks_.PopPendingCallbacks(frame_token); @@ -2425,7 +2427,7 @@ active_tree_->ResetAllChangeTracking(); active_tree_->set_has_ever_been_drawn(true); - devtools_instrumentation::DidDrawFrame(id_); + devtools_instrumentation::DidDrawFrame(id_, frame_token); benchmark_instrumentation::IssueImplThreadRenderingStatsEvent( rendering_stats_instrumentation_->TakeImplThreadRenderingStats());
diff --git a/chrome/android/BUILD.gn b/chrome/android/BUILD.gn index ae5d38a2..b6dd28cf 100644 --- a/chrome/android/BUILD.gn +++ b/chrome/android/BUILD.gn
@@ -1243,7 +1243,7 @@ "//components/media_router/browser/android:java", "//components/media_router/browser/android:test_support_java", "//components/messages/android:java", - "//components/messages/android:javatests", + "//components/messages/android/internal:javatests", "//components/metrics:metrics_java", "//components/minidump_uploader:minidump_uploader_java", "//components/minidump_uploader:minidump_uploader_javatests",
diff --git a/chrome/android/chrome_java_resources.gni b/chrome/android/chrome_java_resources.gni index 0830596..a62e8cc 100644 --- a/chrome/android/chrome_java_resources.gni +++ b/chrome/android/chrome_java_resources.gni
@@ -703,7 +703,6 @@ "java/res/layout/bookmark_widget.xml", "java/res/layout/bookmark_widget_icons_only.xml", "java/res/layout/bookmark_widget_item.xml", - "java/res/layout/chip_view_menu_item.xml", "java/res/layout/clear_browsing_data_button.xml", "java/res/layout/clear_browsing_data_tabs.xml", "java/res/layout/clear_browsing_important_dialog_listview.xml",
diff --git a/chrome/android/chrome_java_sources.gni b/chrome/android/chrome_java_sources.gni index 2d0ead4..0701085 100644 --- a/chrome/android/chrome_java_sources.gni +++ b/chrome/android/chrome_java_sources.gni
@@ -68,7 +68,6 @@ "java/src/org/chromium/chrome/browser/announcement/AnnouncementNotificationManager.java", "java/src/org/chromium/chrome/browser/app/ChromeActivity.java", "java/src/org/chromium/chrome/browser/app/appmenu/AppMenuPropertiesDelegateImpl.java", - "java/src/org/chromium/chrome/browser/app/appmenu/ChipViewMenuItemViewBinder.java", "java/src/org/chromium/chrome/browser/app/appmenu/DividerLineMenuItemViewBinder.java", "java/src/org/chromium/chrome/browser/app/appmenu/IncognitoMenuItemViewBinder.java", "java/src/org/chromium/chrome/browser/app/appmenu/ManagedByMenuItemViewBinder.java",
diff --git a/chrome/android/chrome_test_java_sources.gni b/chrome/android/chrome_test_java_sources.gni index daebb01..58a93508 100644 --- a/chrome/android/chrome_test_java_sources.gni +++ b/chrome/android/chrome_test_java_sources.gni
@@ -593,6 +593,7 @@ "javatests/src/org/chromium/chrome/browser/toolbar/ToolbarSecurityIconTest.java", "javatests/src/org/chromium/chrome/browser/toolbar/ToolbarTest.java", "javatests/src/org/chromium/chrome/browser/toolbar/VoiceToolbarButtonControllerTest.java", + "javatests/src/org/chromium/chrome/browser/toolbar/adaptive/OptionalNewTabButtonControllerTabletTest.java", "javatests/src/org/chromium/chrome/browser/toolbar/adaptive/OptionalNewTabButtonControllerTest.java", "javatests/src/org/chromium/chrome/browser/toolbar/top/AdaptiveToolbarTest.java", "javatests/src/org/chromium/chrome/browser/toolbar/top/BrandColorTest.java",
diff --git a/chrome/android/features/start_surface/internal/java/src/org/chromium/chrome/features/start_surface/ExploreSurfaceCoordinator.java b/chrome/android/features/start_surface/internal/java/src/org/chromium/chrome/features/start_surface/ExploreSurfaceCoordinator.java index 41dd9e1..10558b8 100644 --- a/chrome/android/features/start_surface/internal/java/src/org/chromium/chrome/features/start_surface/ExploreSurfaceCoordinator.java +++ b/chrome/android/features/start_surface/internal/java/src/org/chromium/chrome/features/start_surface/ExploreSurfaceCoordinator.java
@@ -12,7 +12,6 @@ import org.chromium.base.supplier.Supplier; import org.chromium.chrome.browser.app.ChromeActivity; import org.chromium.chrome.browser.feed.FeedSurfaceCoordinator; -import org.chromium.chrome.browser.feed.FeedV1ActionOptions; import org.chromium.chrome.browser.feed.StreamLifecycleManager; import org.chromium.chrome.browser.feed.shared.FeedSurfaceDelegate; import org.chromium.chrome.browser.feed.shared.stream.Stream; @@ -111,17 +110,10 @@ } } - FeedV1ActionOptions feedActionOptions = new FeedV1ActionOptions(); - feedActionOptions.inhibitDownload = true; - feedActionOptions.inhibitOpenInIncognito = true; - feedActionOptions.inhibitOpenInNewTab = true; - feedActionOptions.inhibitLearnMore = true; - FeedSurfaceCoordinator feedSurfaceCoordinator = new FeedSurfaceCoordinator(mActivity, - mActivity.getSnackbarManager(), mActivity.getTabModelSelector(), - mActivity.getWindowAndroid(), null, null, sectionHeaderView, feedActionOptions, - isInNightMode, this, mExploreSurfaceNavigationDelegate, profile, isPlaceholderShown, - bottomSheetController, mActivity.getShareDelegateSupplier(), + mActivity.getSnackbarManager(), mActivity.getWindowAndroid(), null, null, + sectionHeaderView, isInNightMode, this, mExploreSurfaceNavigationDelegate, profile, + isPlaceholderShown, bottomSheetController, mActivity.getShareDelegateSupplier(), scrollableContainerDelegate); feedSurfaceCoordinator.getView().setId(R.id.start_surface_explore_view); return feedSurfaceCoordinator;
diff --git a/chrome/android/features/start_surface/internal/java/src/org/chromium/chrome/features/start_surface/FeedLoadingLayout.java b/chrome/android/features/start_surface/internal/java/src/org/chromium/chrome/features/start_surface/FeedLoadingLayout.java index ce823bb..4e73eb1 100644 --- a/chrome/android/features/start_surface/internal/java/src/org/chromium/chrome/features/start_surface/FeedLoadingLayout.java +++ b/chrome/android/features/start_surface/internal/java/src/org/chromium/chrome/features/start_surface/FeedLoadingLayout.java
@@ -18,7 +18,6 @@ import android.widget.LinearLayout; import org.chromium.chrome.browser.feed.FeedSurfaceCoordinator; -import org.chromium.chrome.browser.feed.shared.FeedFeatures; import org.chromium.chrome.start_surface.R; import org.chromium.components.browser_ui.widget.displaystyle.UiConfig; import org.chromium.components.browser_ui.widget.displaystyle.ViewResizer; @@ -73,21 +72,10 @@ private void setHeader() { LinearLayout headerView = findViewById(R.id.feed_placeholder_header); ViewGroup.LayoutParams lp = headerView.getLayoutParams(); - // FeedFeatures.cachedIsReportingUserActions uses CachedFeatureFlags for checking feature - // states, but these same features are checked directly with ChromeFeatureList in other - // places. Using the cached check here is deliberate for pre-native usage. This - // inconsistency is fine because the check here is for the Feed header blank size, the - // mismatch is bearable and only once for every change. - if (FeedFeatures.cachedIsReportingUserActions()) { // Header blank size should be consistent with // R.layout.new_tab_page_snippets_expandable_header_with_menu. lp.height = getResources().getDimensionPixelSize(R.dimen.snippets_article_header_menu_size); - } else { - // Header blank size should be consistent with R.layout.ss_feed_header. - lp.height = - getResources().getDimensionPixelSize(R.dimen.snippets_article_header_height); - } headerView.setLayoutParams(lp); } @@ -99,9 +87,8 @@ LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams( ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT); - int contentPadding = FeedFeatures.cachedIsV2Enabled() ? mResources.getDimensionPixelSize( - R.dimen.content_suggestions_card_modern_padding_v2) - : 0; + int contentPadding = + mResources.getDimensionPixelSize(R.dimen.content_suggestions_card_modern_padding); lp.setMargins(contentPadding, 0, contentPadding, dpToPx(CARD_MARGIN_DP)); // Set the First placeholder container - an image-right card. @@ -235,12 +222,9 @@ * is resized by {@link ViewResizer} in {@link FeedSurfaceCoordinator} */ private void setPadding() { - int defaultPadding = mResources.getDimensionPixelSize(FeedFeatures.cachedIsV2Enabled() - ? R.dimen.content_suggestions_card_modern_margin_v2 - : R.dimen.content_suggestions_card_modern_margin); - int widePadding = mResources.getDimensionPixelSize(FeedFeatures.cachedIsV2Enabled() - ? R.dimen.ntp_wide_card_lateral_margins_v2 - : R.dimen.ntp_wide_card_lateral_margins); + int defaultPadding = + mResources.getDimensionPixelSize(R.dimen.content_suggestions_card_modern_margin); + int widePadding = mResources.getDimensionPixelSize(R.dimen.ntp_wide_card_lateral_margins); ViewResizer.createAndAttach(this, mUiConfig, defaultPadding, widePadding); }
diff --git a/chrome/android/features/start_surface/internal/javatests/src/org/chromium/chrome/features/start_surface/InstantStartTest.java b/chrome/android/features/start_surface/internal/javatests/src/org/chromium/chrome/features/start_surface/InstantStartTest.java index 8dcc584..1e0517ba 100644 --- a/chrome/android/features/start_surface/internal/javatests/src/org/chromium/chrome/features/start_surface/InstantStartTest.java +++ b/chrome/android/features/start_surface/internal/javatests/src/org/chromium/chrome/features/start_surface/InstantStartTest.java
@@ -78,10 +78,6 @@ import org.chromium.base.StreamUtil; import org.chromium.base.SysUtils; import org.chromium.base.library_loader.LibraryLoader; -import org.chromium.base.test.params.ParameterAnnotations; -import org.chromium.base.test.params.ParameterProvider; -import org.chromium.base.test.params.ParameterSet; -import org.chromium.base.test.params.ParameterizedRunner; import org.chromium.base.test.util.CommandLineFlags; import org.chromium.base.test.util.Criteria; import org.chromium.base.test.util.CriteriaHelper; @@ -97,7 +93,6 @@ import org.chromium.chrome.browser.compositor.layouts.StaticLayout; import org.chromium.chrome.browser.compositor.layouts.content.TabContentManager; import org.chromium.chrome.browser.device.DeviceClassManager; -import org.chromium.chrome.browser.feed.FeedV2; import org.chromium.chrome.browser.flags.CachedFeatureFlags; import org.chromium.chrome.browser.flags.ChromeFeatureList; import org.chromium.chrome.browser.flags.ChromeSwitches; @@ -128,7 +123,7 @@ import org.chromium.chrome.browser.toolbar.top.TopToolbarCoordinator; import org.chromium.chrome.browser.util.ChromeAccessibilityUtil; import org.chromium.chrome.test.ChromeActivityTestRule; -import org.chromium.chrome.test.ChromeJUnit4RunnerDelegate; +import org.chromium.chrome.test.ChromeJUnit4ClassRunner; import org.chromium.chrome.test.ChromeTabbedActivityTestRule; import org.chromium.chrome.test.R; import org.chromium.chrome.test.util.ActivityUtils; @@ -161,8 +156,7 @@ /** * Integration tests of Instant Start which requires 2-stage initialization for Clank startup. */ -@RunWith(ParameterizedRunner.class) -@ParameterAnnotations.UseRunnerDelegate(ChromeJUnit4RunnerDelegate.class) +@RunWith(ChromeJUnit4ClassRunner.class) // clang-format off @CommandLineFlags.Add({ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE}) @EnableFeatures({ChromeFeatureList.TAB_GRID_LAYOUT_ANDROID, @@ -198,24 +192,6 @@ } /** - * Parameter set controlling whether Feed v2 is enabled. - */ - public static class FeedParams implements ParameterProvider { - @Override - public List<ParameterSet> getParameters() { - List<ParameterSet> feedParams = new ArrayList<ParameterSet>(); - if (FeedV2.IS_AVAILABLE) { - feedParams.add(new ParameterSet().value(true).name("FeedV2")); - } - return feedParams; - } - } - - private void setFeedVersion(boolean isFeedV2) { - CachedFeatureFlags.setForTesting(ChromeFeatureList.INTEREST_FEED_V2, isFeedV2); - } - - /** * Only launch Chrome without waiting for a current tab. * This test could not use {@link ChromeActivityTestRule#startMainActivityFromLauncher()} * because of its {@link org.chromium.chrome.browser.tab.Tab} dependency. @@ -774,10 +750,9 @@ "/exclude_mv_tiles/true" + "/hide_switch_when_no_incognito_tabs/true" + "/show_last_active_tab_only/true"}) - @ParameterAnnotations.UseMethodParameter(FeedParams.class) - public void renderSingleAsHomepage_SingleTabNoMVTiles(boolean isFeedV2) throws IOException { + public void renderSingleAsHomepage_SingleTabNoMVTiles() + throws IOException { // clang-format on - setFeedVersion(isFeedV2); createTabStateFile(new int[] {0}); createThumbnailBitmapAndWriteToFile(0); @@ -806,15 +781,13 @@ @SmallTest @Restriction({UiRestriction.RESTRICTION_TYPE_PHONE}) @EnableFeatures({ChromeFeatureList.TAB_SWITCHER_ON_RETURN + "<Study,", - ChromeFeatureList.START_SURFACE_ANDROID + "<Study", ChromeFeatureList.INTEREST_FEED_V2}) + ChromeFeatureList.START_SURFACE_ANDROID + "<Study"}) // clang-format off @CommandLineFlags.Add({ChromeSwitches.DISABLE_NATIVE_INITIALIZATION, "force-fieldtrials=Study/Group", IMMEDIATE_RETURN_PARAMS + "/start_surface_variation/single"}) - @ParameterAnnotations.UseMethodParameter(FeedParams.class) - public void testFeedPlaceholderFromColdStart(boolean isFeedV2) { + public void testFeedPlaceholderFromColdStart() { // clang-format on - setFeedVersion(isFeedV2); startMainActivityFromLauncher(); Assert.assertFalse(mActivityTestRule.getActivity().isTablet()); Assert.assertTrue(CachedFeatureFlags.isEnabled(ChromeFeatureList.INSTANT_START)); @@ -838,14 +811,12 @@ @SmallTest @Restriction({UiRestriction.RESTRICTION_TYPE_PHONE}) @EnableFeatures({ChromeFeatureList.TAB_SWITCHER_ON_RETURN + "<Study,", - ChromeFeatureList.START_SURFACE_ANDROID + "<Study", ChromeFeatureList.INTEREST_FEED_V2}) + ChromeFeatureList.START_SURFACE_ANDROID + "<Study"}) // clang-format off @CommandLineFlags.Add({"force-fieldtrials=Study/Group", IMMEDIATE_RETURN_PARAMS + "/start_surface_variation/single"}) - @ParameterAnnotations.UseMethodParameter(FeedParams.class) - public void testCachedFeedVisibility(boolean isFeedV2) { + public void testCachedFeedVisibility() { // clang-format on - setFeedVersion(isFeedV2); startMainActivityFromLauncher(); mActivityTestRule.waitForActivityNativeInitializationComplete(); // FEED_ARTICLES_LIST_VISIBLE should equal to ARTICLES_LIST_VISIBLE. @@ -879,15 +850,13 @@ @SmallTest @Restriction({UiRestriction.RESTRICTION_TYPE_PHONE}) @EnableFeatures({ChromeFeatureList.TAB_SWITCHER_ON_RETURN + "<Study,", - ChromeFeatureList.START_SURFACE_ANDROID + "<Study", ChromeFeatureList.INTEREST_FEED_V2}) + ChromeFeatureList.START_SURFACE_ANDROID + "<Study"}) // clang-format off @CommandLineFlags.Add({ChromeSwitches.DISABLE_NATIVE_INITIALIZATION, "force-fieldtrials=Study/Group", IMMEDIATE_RETURN_PARAMS + "/start_surface_variation/single"}) - @ParameterAnnotations.UseMethodParameter(FeedParams.class) - public void testHidePlaceholder(boolean isFeedV2) { + public void testHidePlaceholder() { // clang-format on - setFeedVersion(isFeedV2); StartSurfaceConfiguration.setFeedVisibilityForTesting(false); startMainActivityFromLauncher(); @@ -899,15 +868,13 @@ @SmallTest @Restriction({UiRestriction.RESTRICTION_TYPE_PHONE}) @EnableFeatures({ChromeFeatureList.TAB_SWITCHER_ON_RETURN + "<Study,", - ChromeFeatureList.START_SURFACE_ANDROID + "<Study", ChromeFeatureList.INTEREST_FEED_V2}) + ChromeFeatureList.START_SURFACE_ANDROID + "<Study"}) // clang-format off @CommandLineFlags.Add({ChromeSwitches.DISABLE_NATIVE_INITIALIZATION, "force-fieldtrials=Study/Group", IMMEDIATE_RETURN_PARAMS + "/start_surface_variation/single"}) - @ParameterAnnotations.UseMethodParameter(FeedParams.class) - public void testShowPlaceholder(boolean isFeedV2) { + public void testShowPlaceholder() { // clang-format on - setFeedVersion(isFeedV2); StartSurfaceConfiguration.setFeedVisibilityForTesting(true); startMainActivityFromLauncher(); @@ -1137,11 +1104,8 @@ @CommandLineFlags.Add({ChromeSwitches.DISABLE_NATIVE_INITIALIZATION, "force-fieldtrials=Study/Group", IMMEDIATE_RETURN_PARAMS + "/start_surface_variation/single"}) - @ParameterAnnotations.UseMethodParameter(FeedParams.class) - public void renderSingleAsHomepage_Landscape(boolean isFeedV2) throws IOException { + public void renderSingleAsHomepage_Landscape() throws IOException { // clang-format on - setFeedVersion(isFeedV2); - createTabStateFile(new int[] {0, 1, 2}); startMainActivityFromLauncher();
diff --git a/chrome/android/features/start_surface/internal/javatests/src/org/chromium/chrome/features/start_surface/StartSurfaceLayoutTest.java b/chrome/android/features/start_surface/internal/javatests/src/org/chromium/chrome/features/start_surface/StartSurfaceLayoutTest.java index d8ded71f..61d4812 100644 --- a/chrome/android/features/start_surface/internal/javatests/src/org/chromium/chrome/features/start_surface/StartSurfaceLayoutTest.java +++ b/chrome/android/features/start_surface/internal/javatests/src/org/chromium/chrome/features/start_surface/StartSurfaceLayoutTest.java
@@ -94,7 +94,6 @@ import org.chromium.chrome.browser.compositor.layouts.Layout; import org.chromium.chrome.browser.compositor.layouts.StaticLayout; import org.chromium.chrome.browser.compositor.layouts.content.TabContentManager; -import org.chromium.chrome.browser.feed.shared.FeedFeatures; import org.chromium.chrome.browser.flags.CachedFeatureFlags; import org.chromium.chrome.browser.flags.ChromeFeatureList; import org.chromium.chrome.browser.flags.ChromeSwitches; @@ -1892,6 +1891,7 @@ @Test @MediumTest + @DisabledTest(message = "crbug.com/1187320 This doesn't work with FeedV2.") public void testActivityCanBeGarbageCollectedAfterFinished() throws Exception { prepareTabs(1, 0, "about:blank"); @@ -1906,12 +1906,9 @@ mTabListDelegate = null; mActivityTestRule.setActivity(activity); - // TODO(crbug.com/1129187): Looks like this doesn't work with FeedV2. - if (!FeedFeatures.isV2Enabled()) { - // A longer timeout is needed. Achieve that by using the CriteriaHelper.pollUiThread. - CriteriaHelper.pollUiThread( - () -> GarbageCollectionTestUtils.canBeGarbageCollected(activityRef)); - } + // A longer timeout is needed. Achieve that by using the CriteriaHelper.pollUiThread. + CriteriaHelper.pollUiThread( + () -> GarbageCollectionTestUtils.canBeGarbageCollected(activityRef)); } /**
diff --git a/chrome/android/features/start_surface/internal/javatests/src/org/chromium/chrome/features/start_surface/StartSurfaceTest.java b/chrome/android/features/start_surface/internal/javatests/src/org/chromium/chrome/features/start_surface/StartSurfaceTest.java index 04f9503..964a755 100644 --- a/chrome/android/features/start_surface/internal/javatests/src/org/chromium/chrome/features/start_surface/StartSurfaceTest.java +++ b/chrome/android/features/start_surface/internal/javatests/src/org/chromium/chrome/features/start_surface/StartSurfaceTest.java
@@ -1106,7 +1106,7 @@ SingleTabSwitcherMediator.SINGLE_TAB_TITLE_AVAILABLE_TIME_UMA, isInstantStart))); - // TODO(crbug.com/1129187): Looks like this doesn't work with FeedV2. + // TODO(crbug.com/1187320): Looks like this doesn't work with FeedV2. if (!(FeedFeatures.isV2Enabled() && mImmediateReturn)) { Assert.assertEquals(expectedRecordCount, RecordHistogram.getHistogramTotalCountForTesting( @@ -1114,7 +1114,7 @@ FeedSurfaceMediator.FEED_CONTENT_FIRST_LOADED_TIME_MS_UMA, isInstantStart))); } - // TODO(crbug.com/1129187): Looks like this doesn't work with FeedV2. + // TODO(crbug.com/1187320): Looks like this doesn't work with FeedV2. if (!(FeedFeatures.isV2Enabled() && mImmediateReturn && mUseInstantStart)) { Assert.assertEquals(expectedRecordCount, RecordHistogram.getHistogramTotalCountForTesting(
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/TasksView.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/TasksView.java index 00677dd4..cae9d6d 100644 --- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/TasksView.java +++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/TasksView.java
@@ -30,7 +30,6 @@ import org.chromium.base.ApiCompatibilityUtils; import org.chromium.base.MathUtils; import org.chromium.chrome.browser.feed.FeedSurfaceCoordinator; -import org.chromium.chrome.browser.feed.shared.FeedFeatures; import org.chromium.chrome.browser.lifecycle.ActivityLifecycleDispatcher; import org.chromium.chrome.browser.ntp.IncognitoDescriptionView; import org.chromium.chrome.browser.ntp.search.SearchBoxCoordinator; @@ -100,7 +99,7 @@ public void onConfigurationChanged(Configuration newConfig) { super.onConfigurationChanged(newConfig); mUiConfig.updateDisplayStyle(); - alignHeaderForFeedV2(); + alignHeaderForFeed(); } private void adjustScrollMode(AppBarLayout.LayoutParams layoutParams) { @@ -132,21 +131,13 @@ // ExploreSurfaceCoordinator. TextView titleDescription = (TextView) findViewById(R.id.tab_switcher_title_description); TextView moreTabs = (TextView) findViewById(R.id.more_tabs); - if (FeedFeatures.cachedIsReportingUserActions()) { - ApiCompatibilityUtils.setTextAppearance( - titleDescription, R.style.TextAppearance_TextSmall_Secondary); - ApiCompatibilityUtils.setTextAppearance( - moreTabs, R.style.TextAppearance_TextSmall_Blue); - ViewCompat.setPaddingRelative(titleDescription, - mContext.getResources().getDimensionPixelSize(R.dimen.card_padding), - titleDescription.getPaddingTop(), titleDescription.getPaddingEnd(), - titleDescription.getPaddingBottom()); - } else { - ApiCompatibilityUtils.setTextAppearance( - titleDescription, R.style.TextAppearance_TextMediumThick_Primary); - ApiCompatibilityUtils.setTextAppearance( - moreTabs, R.style.TextAppearance_TextMedium_Blue); - } + ApiCompatibilityUtils.setTextAppearance( + titleDescription, R.style.TextAppearance_TextSmall_Secondary); + ApiCompatibilityUtils.setTextAppearance(moreTabs, R.style.TextAppearance_TextSmall_Blue); + ViewCompat.setPaddingRelative(titleDescription, + mContext.getResources().getDimensionPixelSize(R.dimen.card_padding), + titleDescription.getPaddingTop(), titleDescription.getPaddingEnd(), + titleDescription.getPaddingBottom()); } ViewGroup getCarouselTabSwitcherContainer() { @@ -463,25 +454,20 @@ */ private void setHeaderPadding() { int defaultPadding = 0; - int widePadding = getResources().getDimensionPixelSize(FeedFeatures.cachedIsV2Enabled() - ? R.dimen.ntp_wide_card_lateral_margins_v2 - : R.dimen.ntp_wide_card_lateral_margins); + int widePadding = + getResources().getDimensionPixelSize(R.dimen.ntp_wide_card_lateral_margins); ViewResizer.createAndAttach(mHeaderView, mUiConfig, defaultPadding, widePadding); - alignHeaderForFeedV2(); + alignHeaderForFeed(); } /** - * Feed v2 has extra content padding, we need to align the header with it. However, the padding + * Feed has extra content padding, we need to align the header with it. However, the padding * of the header is already bound with ViewResizer in setHeaderPadding(), so we update the left * & right margins of MV tiles container and carousel tab switcher container. */ - private void alignHeaderForFeedV2() { - if (!FeedFeatures.cachedIsV2Enabled()) { - return; - } - - MarginLayoutParams MVParams = + private void alignHeaderForFeed() { + MarginLayoutParams mostVisitedLayoutParams = (MarginLayoutParams) mHeaderView.findViewById(R.id.mv_tiles_container) .getLayoutParams(); @@ -489,15 +475,15 @@ (MarginLayoutParams) mCarouselTabSwitcherContainer.getLayoutParams(); int margin = getResources().getDimensionPixelSize( - R.dimen.content_suggestions_card_modern_padding_v2); + R.dimen.content_suggestions_card_modern_padding); if (getResources().getConfiguration().orientation == ORIENTATION_LANDSCAPE) { - MVParams.leftMargin = margin; - MVParams.rightMargin = margin; + mostVisitedLayoutParams.leftMargin = margin; + mostVisitedLayoutParams.rightMargin = margin; carouselTabSwitcherParams.leftMargin = margin; carouselTabSwitcherParams.rightMargin = margin; } else { - MVParams.leftMargin = 0; - MVParams.rightMargin = 0; + mostVisitedLayoutParams.leftMargin = 0; + mostVisitedLayoutParams.rightMargin = 0; carouselTabSwitcherParams.leftMargin = getResources().getDimensionPixelSize(R.dimen.tab_carousel_start_margin); carouselTabSwitcherParams.rightMargin = 0;
diff --git a/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/DEPS b/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/DEPS deleted file mode 100644 index 28f8ef6..0000000 --- a/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/DEPS +++ /dev/null
@@ -1,9 +0,0 @@ -include_rules = [ - "-chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/v2", -] - -specific_include_rules = { - "FeedV2\.java": [ - "+chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/v2", - ], -}
diff --git a/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedSurfaceCoordinator.java b/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedSurfaceCoordinator.java index 0c73af5..fe0f6d63 100644 --- a/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedSurfaceCoordinator.java +++ b/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedSurfaceCoordinator.java
@@ -26,12 +26,12 @@ import org.chromium.base.supplier.Supplier; import org.chromium.chrome.R; import org.chromium.chrome.browser.feature_engagement.TrackerFactory; -import org.chromium.chrome.browser.feed.shared.FeedFeatures; import org.chromium.chrome.browser.feed.shared.FeedSurfaceDelegate; import org.chromium.chrome.browser.feed.shared.FeedSurfaceProvider; import org.chromium.chrome.browser.feed.shared.stream.Header; import org.chromium.chrome.browser.feed.shared.stream.NonDismissibleHeader; import org.chromium.chrome.browser.feed.shared.stream.Stream; +import org.chromium.chrome.browser.feed.v2.FeedStream; import org.chromium.chrome.browser.flags.ChromeFeatureList; import org.chromium.chrome.browser.native_page.ContextMenuManager; import org.chromium.chrome.browser.native_page.NativePageNavigationDelegate; @@ -47,7 +47,6 @@ import org.chromium.chrome.browser.share.ShareDelegate; import org.chromium.chrome.browser.signin.services.IdentityServicesProvider; import org.chromium.chrome.browser.signin.ui.PersonalizedSigninPromoView; -import org.chromium.chrome.browser.tabmodel.TabModelSelector; import org.chromium.chrome.browser.ui.messages.snackbar.SnackbarManager; import org.chromium.chrome.browser.user_education.UserEducationHelper; import org.chromium.chrome.features.start_surface.StartSurfaceConfiguration; @@ -75,34 +74,6 @@ @VisibleForTesting public static final String FEED_STREAM_CREATED_TIME_MS_UMA = "FeedStreamCreatedTime"; - /** - * Provides an interface that can be implemented by both Feed v1 and v2, allowing us to compile - * without Feed v1. Once v1 is removed, this interface should be removed. - */ - public interface StreamWrapper { - int defaultMarginPixels(Activity activity); - int wideMarginPixels(Activity activity); - - /** - * Creates the stream. Only called once unless doneWithStream is called. - */ - Stream createStream(Profile profile, Activity activity, boolean showDarkBackground, - SnackbarManager snackbarManager, - NativePageNavigationDelegate pageNavigationDelegate, UiConfig uiConfig, - boolean placeholderShown, BottomSheetController bottomSheetController, - WindowAndroid windowAndroid, FeedV1ActionOptions v1ActionOptions, - Supplier<ShareDelegate> shareDelegateSupplier); - - /** - * Called after the stream returned by createStream() is no longer needed. - */ - void doneWithStream(); - boolean isPlaceholderShown(); - void addScrollListener(); - } - - StreamWrapper mStreamWrapper; - protected final Activity mActivity; private final SnackbarManager mSnackbarManager; @Nullable @@ -114,13 +85,11 @@ private final int mWideMarginPixels; private final FeedSurfaceMediator mMediator; private final BottomSheetController mBottomSheetController; - private final FeedV1ActionOptions mV1ActionOptions; private final WindowAndroid mWindowAndroid; private final Supplier<ShareDelegate> mShareSupplier; private UiConfig mUiConfig; private FrameLayout mRootView; - private ContextMenuManager mContextMenuManager; private Tracker mTracker; private long mStreamCreatedTimeMs; @@ -147,16 +116,11 @@ private @Nullable ScrollView mScrollViewForPolicy; private @Nullable ViewResizer mScrollViewResizer; - // Used for the feed header menu. - private UserEducationHelper mUserEducationHelper; - // Used to handle things related to the main scrollable container of NTP surface. private @Nullable ScrollableContainerDelegate mScrollableContainerDelegate; private @Nullable HeaderIphScrollListener mHeaderIphScrollListener; - private final Handler mHandler = new Handler(); - private class SignInPromoHeader implements Header { @Override public View getView() { @@ -268,32 +232,27 @@ * Constructs a new FeedSurfaceCoordinator. * @param activity The containing {@link Activity}. * @param snackbarManager The {@link SnackbarManager} displaying Snackbar UI. - * @param tabModelSelector {@link TabModelSelector} object. * @param windowAndroid The window of the page. * @param snapScrollHelper The {@link SnapScrollHelper} for the New Tab Page. * @param ntpHeader The extra header on top of the feeds for the New Tab Page. * @param sectionHeaderView The {@link SectionHeaderView} for the feed. - * @param actionOptions Configures feed v1 actions. * @param showDarkBackground Whether is shown on dark background. * @param delegate The constructing {@link FeedSurfaceDelegate}. * @param pageNavigationDelegate The {@link NativePageNavigationDelegate} * that handles page navigation. * @param profile The current user profile. * @param isPlaceholderShownInitially Whether the placeholder is shown initially. - * @param bottomSheetController The bottom sheet controller, used in v2. + * @param bottomSheetController The bottom sheet controller. * @param shareDelegateSupplier The supplier for the share delegate used to share articles. */ public FeedSurfaceCoordinator(Activity activity, SnackbarManager snackbarManager, - TabModelSelector tabModelSelector, WindowAndroid windowAndroid, - @Nullable SnapScrollHelper snapScrollHelper, @Nullable View ntpHeader, - @Nullable SectionHeaderView sectionHeaderView, FeedV1ActionOptions actionOptions, + WindowAndroid windowAndroid, @Nullable SnapScrollHelper snapScrollHelper, + @Nullable View ntpHeader, @Nullable SectionHeaderView sectionHeaderView, boolean showDarkBackground, FeedSurfaceDelegate delegate, @Nullable NativePageNavigationDelegate pageNavigationDelegate, Profile profile, boolean isPlaceholderShownInitially, BottomSheetController bottomSheetController, Supplier<ShareDelegate> shareDelegateSupplier, @Nullable ScrollableContainerDelegate externalScrollableContainerDelegate) { - mStreamWrapper = FeedV2.createStreamWrapper(); - mActivity = activity; mSnackbarManager = snackbarManager; mNtpHeader = ntpHeader; @@ -303,20 +262,20 @@ mPageNavigationDelegate = pageNavigationDelegate; mBottomSheetController = bottomSheetController; mProfile = profile; - mV1ActionOptions = actionOptions; mWindowAndroid = windowAndroid; mShareSupplier = shareDelegateSupplier; mScrollableContainerDelegate = externalScrollableContainerDelegate; Resources resources = mActivity.getResources(); - mDefaultMarginPixels = mStreamWrapper.defaultMarginPixels(activity); - mWideMarginPixels = mStreamWrapper.wideMarginPixels(activity); + mDefaultMarginPixels = mActivity.getResources().getDimensionPixelSize( + R.dimen.content_suggestions_card_modern_margin); + mWideMarginPixels = mActivity.getResources().getDimensionPixelSize( + R.dimen.ntp_wide_card_lateral_margins); mRootView = new RootView(mActivity); mRootView.setPadding(0, resources.getDimensionPixelOffset(R.dimen.tab_strip_height), 0, 0); mUiConfig = new UiConfig(mRootView); - mTracker = TrackerFactory.getTrackerForProfile(profile); if (isEnhancedProtectionPromoEnabled()) { mEnhancedProtectionPromoController = @@ -340,8 +299,6 @@ // Mediator should be created before any Stream changes. mMediator = new FeedSurfaceMediator( this, snapScrollHelper, mPageNavigationDelegate, mSectionHeaderModel); - - mUserEducationHelper = new UserEducationHelper(mActivity, mHandler); } @Override @@ -350,7 +307,6 @@ mMediator.destroy(); if (mStreamLifecycleManager != null) mStreamLifecycleManager.destroy(); mStreamLifecycleManager = null; - mStreamWrapper.doneWithStream(); if (mEnhancedProtectionPromoController != null) { mEnhancedProtectionPromoController.destroy(); } @@ -407,7 +363,7 @@ /** @return Whether the placeholder is shown. */ public boolean isPlaceholderShown() { - return mStreamWrapper.isPlaceholderShown(); + return mStream.isPlaceholderShown(); } /** @@ -422,9 +378,9 @@ } mStreamCreatedTimeMs = SystemClock.elapsedRealtime(); - mStream = mStreamWrapper.createStream(mProfile, mActivity, mShowDarkBackground, - mSnackbarManager, mPageNavigationDelegate, mUiConfig, mIsPlaceholderShownInitially, - mBottomSheetController, mWindowAndroid, mV1ActionOptions, mShareSupplier); + mStream = new FeedStream(mActivity, mShowDarkBackground, mSnackbarManager, + mPageNavigationDelegate, mBottomSheetController, mIsPlaceholderShownInitially, + mWindowAndroid, mShareSupplier); mStreamLifecycleManager = mDelegate.createStreamLifecycleManager(mStream, mActivity); @@ -453,8 +409,6 @@ mStream.setHeaderViews(Arrays.asList(new NonDismissibleHeader(mSectionHeaderView))); } - mStreamWrapper.addScrollListener(); - // Work around https://crbug.com/943873 where default focus highlight shows up after // toggling dark mode. if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { @@ -498,7 +452,6 @@ mEnhancedProtectionPromoController.destroy(); mEnhancedProtectionPromoController = null; } - mStreamWrapper.doneWithStream(); } mScrollViewForPolicy = new PolicyScrollView(mActivity); @@ -542,13 +495,6 @@ mSigninPromoView = (PersonalizedSigninPromoView) inflater.inflate( R.layout.personalized_signin_promo_view_modern_content_suggestions, mRootView, false); - - // If the placeholder is shown in Feed v1, delay to show the sign-in view until the - // articles are shown. Feed v2's articles don't have fade-in animations, so sign-in view - // is already shown together with v2 articles. - if (isPlaceholderShown() && !FeedFeatures.isV2Enabled()) { - mSigninPromoView.setVisibility(View.INVISIBLE); - } } return mSigninPromoView; } @@ -600,14 +546,6 @@ mStreamCreatedTimeMs - activityCreationTimeMs, mIsPlaceholderShownInitially); } - Tracker getFeatureEngagementTracker() { - return mTracker; - } - - UserEducationHelper getUserEducationHelper() { - return mUserEducationHelper; - } - EnhancedProtectionPromoController getEnhancedProtectionPromoController() { return mEnhancedProtectionPromoController; } @@ -661,11 +599,12 @@ HeaderIphScrollListener.Delegate delegate = new HeaderIphScrollListener.Delegate() { @Override public Tracker getFeatureEngagementTracker() { - return mTracker; + return TrackerFactory.getTrackerForProfile(mProfile); } @Override public void showMenuIph() { - mSectionHeaderView.showMenuIph(mUserEducationHelper); + UserEducationHelper helper = new UserEducationHelper(mActivity, new Handler()); + mSectionHeaderView.showMenuIph(helper); } @Override public boolean isFeedExpanded() {
diff --git a/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedSurfaceMediator.java b/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedSurfaceMediator.java index c29639aa..fe24fb4 100644 --- a/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedSurfaceMediator.java +++ b/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedSurfaceMediator.java
@@ -221,24 +221,17 @@ mStreamContentChanged = true; if (mSnapScrollHelper != null) mSnapScrollHelper.resetSearchBoxOnScroll(true); - // Feed v2's background is set to be transparent in {@link + // Feed's background is set to be transparent in {@link // FeedSurfaceCoordinator#createStream} to show the Feed placeholder. When first // batch of articles are about to show, set recyclerView back to non-transparent. - // Feed v2 doesn't call onAddFinished(), so we hide placeholder here. - if (FeedFeatures.isV2Enabled() && mCoordinator.isPlaceholderShown()) { + if (mCoordinator.isPlaceholderShown()) { stream.hidePlaceholder(); } } @Override public void onAddFinished() { - // Feed v1's background is set to be transparent in {@link - // FeedSurfaceCoordinator#createStream} to show the Feed placeholder. After first - // batch of articles finish fade-in animation, set recyclerView back to - // non-transparent. - if (!FeedFeatures.isV2Enabled() && mCoordinator.isPlaceholderShown()) { - stream.hidePlaceholder(); - } + // TODO(crbug.com/1187320): Remove this method altogether when bug fixed. if (mContentFirstAvailableTimeMs == 0) { mContentFirstAvailableTimeMs = SystemClock.elapsedRealtime(); if (mHasPendingUmaRecording) { @@ -248,16 +241,6 @@ } mIsLoadingFeed = false; } - - @Override - public void onAddStarting() { - // Feed v1's sign-in view is set to be invisible in {@link - // FeedSurfaceCoordinator#getSigninPromoView} if the Feed placeholder is shown. Set - // sign-in box visible back when Feed articles are about to show. - if (!FeedFeatures.isV2Enabled() && mCoordinator.isPlaceholderShown()) { - mCoordinator.fadeInSigninView(); - } - } }; stream.addOnContentChangedListener(mStreamContentChangedListener); @@ -272,7 +255,7 @@ mFeedMenuModel = buildMenuItems(); PropertyModel interestFeedHeader = SectionHeaderProperties.createSectionHeader( - getSectionHeaderText(suggestionsVisible)); + getInterestFeedHeaderText(suggestionsVisible)); mSectionHeaderModel.get(SectionHeaderListProperties.SECTION_HEADERS_KEY) .add(interestFeedHeader); @@ -393,7 +376,7 @@ mSectionHeaderModel.get(SectionHeaderListProperties.SECTION_HEADERS_KEY) .get(INTEREST_FEED_HEADER_POSITION) .set(SectionHeaderProperties.HEADER_TEXT_KEY, - getSectionHeaderText(suggestionsVisible)); + getInterestFeedHeaderText(suggestionsVisible)); } // Update toggleswitch item, which is last item in list. @@ -425,8 +408,8 @@ SuggestionsMetrics.recordArticlesListVisible(); } - /** Returns the section header text based on the selected default search engine */ - private String getSectionHeaderText(boolean isExpanded) { + /** Returns the interest feed header text based on the selected default search engine */ + private String getInterestFeedHeaderText(boolean isExpanded) { Resources res = mCoordinator.getSectionHeaderView().getResources(); final boolean isDefaultSearchEngineGoogle = TemplateUrlServiceFactory.get().isDefaultSearchEngineGoogle();
diff --git a/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedV1ActionOptions.java b/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedV1ActionOptions.java deleted file mode 100644 index acd81df..0000000 --- a/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedV1ActionOptions.java +++ /dev/null
@@ -1,16 +0,0 @@ -// Copyright 2020 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -package org.chromium.chrome.browser.feed; - -/** - * Options for handling actions in Feed V1. - * TODO(crbug.com/1165828): V1 was removed, these do nothing. - */ -public class FeedV1ActionOptions { - public boolean inhibitDownload; - public boolean inhibitOpenInIncognito; - public boolean inhibitOpenInNewTab; - public boolean inhibitLearnMore; -}
diff --git a/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedV2.java b/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedV2.java deleted file mode 100644 index 208a77b..0000000 --- a/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedV2.java +++ /dev/null
@@ -1,24 +0,0 @@ -// Copyright 2020 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -package org.chromium.chrome.browser.feed; - -import org.chromium.chrome.browser.feed.v2.FeedStreamSurface; -import org.chromium.chrome.browser.feed.v2.FeedStreamWrapper; - -/** - * Provides access to FeedV2 with a similar interface as FeedV1. - */ -public class FeedV2 { - // Whether FeedV2 is compiled in. - public static final boolean IS_AVAILABLE = FeedV2BuildFlag.IS_AVAILABLE; - - public static void startup() { - FeedStreamSurface.startup(); - } - - public static FeedSurfaceCoordinator.StreamWrapper createStreamWrapper() { - return new FeedStreamWrapper(); - } -}
diff --git a/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedV2BuildFlag.java b/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedV2BuildFlag.java deleted file mode 100644 index 7dad06f..0000000 --- a/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedV2BuildFlag.java +++ /dev/null
@@ -1,12 +0,0 @@ -// Copyright 2020 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -package org.chromium.chrome.browser.feed; - -/** - * Provides the value of the feed_v2_enabled build flag. - */ -class FeedV2BuildFlag { - public static final boolean IS_AVAILABLE = true; -}
diff --git a/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/shared/FeedFeatures.java b/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/shared/FeedFeatures.java index 380c417..2d549d9 100644 --- a/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/shared/FeedFeatures.java +++ b/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/shared/FeedFeatures.java
@@ -5,7 +5,6 @@ package org.chromium.chrome.browser.feed.shared; import org.chromium.base.Log; -import org.chromium.chrome.browser.flags.CachedFeatureFlags; import org.chromium.chrome.browser.flags.ChromeFeatureList; import org.chromium.chrome.browser.preferences.Pref; import org.chromium.chrome.browser.preferences.PrefChangeRegistrar; @@ -26,32 +25,11 @@ private static PrefChangeRegistrar sPrefChangeRegistrar; - /** - * @return Whether implicit Feed user actions are being reported based on feature states. Can be - * used for both Feed v1 and v2. - */ - public static boolean isReportingUserActions() { - return isV2Enabled() - || ChromeFeatureList.isEnabled(ChromeFeatureList.REPORT_FEED_USER_ACTIONS); - } - - /** - * Identical to {@link isReportingUserActions} but uses {@link CachedFeatureFlags} for checking - * feature states. - */ - public static boolean cachedIsReportingUserActions() { - return cachedIsV2Enabled() - || CachedFeatureFlags.isEnabled(ChromeFeatureList.REPORT_FEED_USER_ACTIONS); - } - + /** TODO(crbug.com/1187320): Remove when tests are fixed. */ public static boolean isV2Enabled() { return true; } - public static boolean cachedIsV2Enabled() { - return true; - } - /** * @return Whether the feed is allowed to be used. The feed is disabled if supervised user or * enterprise policy has once been added within the current session. The value returned by
diff --git a/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/shared/stream/Stream.java b/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/shared/stream/Stream.java index 05a78ba..fa45708 100644 --- a/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/shared/stream/Stream.java +++ b/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/shared/stream/Stream.java
@@ -179,11 +179,5 @@ * {@link androidx.recyclerview.widget.SimpleItemAnimator#onAddFinished} event is received. */ default void onAddFinished(){}; - - /** - * Called by Stream when an - * {@link androidx.recyclerview.widget.SimpleItemAnimator#onAddStarting} event is received. - */ - default void onAddStarting(){}; } }
diff --git a/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/v2/FeedStreamWrapper.java b/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/v2/FeedStreamWrapper.java deleted file mode 100644 index 8a7d324..0000000 --- a/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/v2/FeedStreamWrapper.java +++ /dev/null
@@ -1,61 +0,0 @@ -// Copyright 2020 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -package org.chromium.chrome.browser.feed.v2; - -import android.app.Activity; - -import org.chromium.base.supplier.Supplier; -import org.chromium.chrome.R; -import org.chromium.chrome.browser.feed.FeedSurfaceCoordinator; -import org.chromium.chrome.browser.feed.FeedV1ActionOptions; -import org.chromium.chrome.browser.feed.shared.stream.Stream; -import org.chromium.chrome.browser.native_page.NativePageNavigationDelegate; -import org.chromium.chrome.browser.profiles.Profile; -import org.chromium.chrome.browser.share.ShareDelegate; -import org.chromium.chrome.browser.ui.messages.snackbar.SnackbarManager; -import org.chromium.components.browser_ui.bottomsheet.BottomSheetController; -import org.chromium.components.browser_ui.widget.displaystyle.UiConfig; -import org.chromium.ui.base.WindowAndroid; - -/** - * Wraps management of the Feed V2 stream. - */ -public class FeedStreamWrapper implements FeedSurfaceCoordinator.StreamWrapper { - private Stream mStream; - @Override - public int defaultMarginPixels(Activity activity) { - return activity.getResources().getDimensionPixelSize( - R.dimen.content_suggestions_card_modern_margin_v2); - } - - @Override - public int wideMarginPixels(Activity activity) { - return activity.getResources().getDimensionPixelSize( - R.dimen.ntp_wide_card_lateral_margins_v2); - } - - @Override - public Stream createStream(Profile profile, Activity activity, boolean showDarkBackground, - SnackbarManager snackbarManager, NativePageNavigationDelegate pageNavigationDelegate, - UiConfig uiConfig, boolean placeholderShown, - BottomSheetController bottomSheetController, WindowAndroid windowAndroid, - FeedV1ActionOptions v1ActionOptions, Supplier<ShareDelegate> shareDelegateSupplier) { - mStream = new FeedStream(activity, showDarkBackground, snackbarManager, - pageNavigationDelegate, bottomSheetController, placeholderShown, windowAndroid, - shareDelegateSupplier); - return mStream; - } - - @Override - public boolean isPlaceholderShown() { - return mStream.isPlaceholderShown(); - } - - @Override - public void doneWithStream() {} - - @Override - public void addScrollListener() {} -}
diff --git a/chrome/android/feed/feed_java_sources.gni b/chrome/android/feed/feed_java_sources.gni index bf843b3..fe7dfcd 100644 --- a/chrome/android/feed/feed_java_sources.gni +++ b/chrome/android/feed/feed_java_sources.gni
@@ -23,8 +23,6 @@ "//chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedSurfaceCoordinator.java", "//chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedSurfaceMediator.java", "//chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedUma.java", - "//chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedV1ActionOptions.java", - "//chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedV2.java", "//chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/HeaderIphScrollListener.java", "//chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/NtpStreamLifecycleManager.java", "//chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/StreamLifecycleManager.java", @@ -45,50 +43,33 @@ "//chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/v2/FeedSliceViewTracker.java", "//chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/v2/FeedStream.java", "//chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/v2/FeedStreamSurface.java", - "//chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/v2/FeedStreamWrapper.java", "//chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/v2/NativeViewListRenderer.java", ] -if (enable_feed_v2) { - feed_java_sources += [ "//chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedV2BuildFlag.java" ] -} else { - feed_java_sources += [ "//chrome/android/feed/dummy/java/src/org/chromium/chrome/browser/feed/FeedV2BuildFlag.java" ] -} - feed_srcjar_deps = [] -feed_junit_test_java_sources = [] +feed_junit_test_java_sources = [ + "junit/src/org/chromium/chrome/browser/feed/v2/FakeLinearLayoutManager.java", + "junit/src/org/chromium/chrome/browser/feed/v2/FeedListContentManagerTest.java", + "junit/src/org/chromium/chrome/browser/feed/v2/FeedProcessScopeDependencyProviderTest.java", + "junit/src/org/chromium/chrome/browser/feed/v2/FeedSliceViewTrackerTest.java", + "junit/src/org/chromium/chrome/browser/feed/v2/FeedStreamSurfaceTest.java", + "junit/src/org/chromium/chrome/browser/feed/v2/FeedStreamTest.java", + "junit/src/org/chromium/chrome/browser/feed/v2/NativeViewListRendererTest.java", +] -if (enable_feed_v2) { - feed_junit_test_java_sources += [ - "junit/src/org/chromium/chrome/browser/feed/v2/FakeLinearLayoutManager.java", - "junit/src/org/chromium/chrome/browser/feed/v2/FeedListContentManagerTest.java", - "junit/src/org/chromium/chrome/browser/feed/v2/FeedProcessScopeDependencyProviderTest.java", - "junit/src/org/chromium/chrome/browser/feed/v2/FeedSliceViewTrackerTest.java", - "junit/src/org/chromium/chrome/browser/feed/v2/FeedStreamSurfaceTest.java", - "junit/src/org/chromium/chrome/browser/feed/v2/FeedStreamTest.java", - "junit/src/org/chromium/chrome/browser/feed/v2/NativeViewListRendererTest.java", - ] -} +feed_test_java_sources = [ + "//chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/HeaderIphScrollListenerTest.java", + "//chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/v2/FeedProcessScopeDependencyProviderNativeTest.java", + "//chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/v2/FeedV2NewTabPageTest.java", + "//chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/v2/FeedV2TestHelper.java", + "//chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/v2/TestFeedServer.java", +] -feed_test_java_sources = [ "//chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/HeaderIphScrollListenerTest.java" ] - -if (enable_feed_v2) { - feed_test_java_sources += [ - "//chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/v2/FeedProcessScopeDependencyProviderNativeTest.java", - "//chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/v2/FeedV2NewTabPageTest.java", - "//chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/v2/FeedV2TestHelper.java", - "//chrome/android/feed/core/javatests/src/org/chromium/chrome/browser/feed/v2/TestFeedServer.java", - ] -} - -feed_test_deps = [] -if (enable_feed_v2) { - feed_test_deps += feed_deps + [ - "//chrome/browser/privacy:java", - "//chrome/browser/user_education:java", - "//chrome/browser/xsurface:java", - "//third_party/android_deps:guava_android_java", - "//third_party/google-truth:google_truth_java", - ] -} +feed_test_deps = feed_deps + [ + "//chrome/browser/privacy:java", + "//chrome/browser/user_education:java", + "//chrome/browser/xsurface:java", + "//third_party/android_deps:guava_android_java", + "//third_party/google-truth:google_truth_java", + ]
diff --git a/chrome/android/java/res/layout/chip_view_menu_item.xml b/chrome/android/java/res/layout/chip_view_menu_item.xml deleted file mode 100644 index dab94325..0000000 --- a/chrome/android/java/res/layout/chip_view_menu_item.xml +++ /dev/null
@@ -1,33 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- Copyright 2020 The Chromium Authors. All rights reserved. - Use of this source code is governed by a BSD-style license that can be - found in the LICENSE file. ---> - -<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:app="http://schemas.android.com/apk/res-auto" - android:layout_width="match_parent" - android:layout_height="?android:attr/listPreferredItemHeightSmall" - android:gravity="center_vertical" - android:orientation="horizontal" > - - <org.chromium.components.browser_ui.widget.text.TextViewWithCompoundDrawables - android:id="@+id/title" - style="@style/AppMenuItemTextViewWithCompoundDrawables" - android:layout_weight="1" - android:layout_width="wrap_content" - android:layout_height="match_parent" - android:paddingStart="16dp" - android:background="?attr/listChoiceBackgroundIndicator"/> - - <org.chromium.ui.widget.ChipView - android:id="@+id/chip_view" - android:layout_weight="0" - android:layout_width="wrap_content" - android:layout_height="match_parent" - android:layout_gravity="end" - android:layout_marginEnd="16dp" - style="@style/MenuChip" - android:visibility="gone" /> - -</LinearLayout> \ No newline at end of file
diff --git a/chrome/android/java/res/menu/main_menu.xml b/chrome/android/java/res/menu/main_menu.xml index b955ddd..357b392 100644 --- a/chrome/android/java/res/menu/main_menu.xml +++ b/chrome/android/java/res/menu/main_menu.xml
@@ -52,28 +52,12 @@ <item android:id="@+id/open_history_menu_id" android:title="@string/menu_history" android:icon="@drawable/ic_history_googblue_24dp" /> - <item android:id="@+id/downloads_row_menu_id" - android:title="@null"> - <menu> - <item android:id="@+id/downloads_menu_id" - android:title="@string/menu_downloads" - android:icon="@drawable/infobar_download_complete" /> - <item android:id="@+id/offline_page_chip_id" - android:title="@string/menu_download" - android:icon="@drawable/ic_file_download_white_24dp" /> - </menu> - </item> - <item android:id="@+id/all_bookmarks_row_menu_id" - android:title="@null"> - <menu> - <item android:id="@+id/all_bookmarks_menu_id" - android:title="@string/menu_bookmarks" - android:icon="@drawable/btn_star_filled" /> - <item android:id="@+id/bookmark_this_page_chip_id" - android:title="@string/menu_bookmark" - android:icon="@drawable/btn_star" /> - </menu> - </item> + <item android:id="@+id/downloads_menu_id" + android:title="@string/menu_downloads" + android:icon="@drawable/infobar_download_complete" /> + <item android:id="@+id/all_bookmarks_menu_id" + android:title="@string/menu_bookmarks" + android:icon="@drawable/btn_star_filled" /> <item android:id="@+id/recent_tabs_menu_id" android:title="@string/menu_recent_tabs" android:icon="@drawable/devices_black_24dp" />
diff --git a/chrome/android/java/res/values/colors.xml b/chrome/android/java/res/values/colors.xml index 499a312..226b4ef 100644 --- a/chrome/android/java/res/values/colors.xml +++ b/chrome/android/java/res/values/colors.xml
@@ -108,6 +108,9 @@ <color name="offline_indicator_offline_color">@android:color/black</color> <color name="offline_indicator_back_online_color">@color/default_bg_color_blue</color> + <!-- Material colorSurface See See https://crbug.com/1186712 --> + <color name="material_color_surface">@color/default_bg_color_elev_4</color> + <!-- Other colors --> <color name="media_viewer_bg">#000000</color> <color name="image_viewer_bg">#0E0E0E</color>
diff --git a/chrome/android/java/res/values/dimens.xml b/chrome/android/java/res/values/dimens.xml index 4912c3c..26f588f 100644 --- a/chrome/android/java/res/values/dimens.xml +++ b/chrome/android/java/res/values/dimens.xml
@@ -295,8 +295,7 @@ to both the top and bottom bounds to bring the 48dp modern_toolbar_background_size to 56dp (matches toolbar_height_no_shadow). --> <dimen name="ntp_search_box_bounds_vertical_inset_modern">-4dp</dimen> - <dimen name="ntp_wide_card_lateral_margins">48dp</dimen> - <dimen name="ntp_wide_card_lateral_margins_v2">36dp</dimen> + <dimen name="ntp_wide_card_lateral_margins">36dp</dimen> <dimen name="snippets_article_header_height">40dp</dimen> <dimen name="snippets_article_header_menu_size">48dp</dimen> <dimen name="feed_v2_header_menu_width">18dp</dimen> @@ -305,9 +304,9 @@ <!-- This should match |ntp_header_lateral_margins_v2|. --> <dimen name="ntp_header_lateral_margins_v2">16dp</dimen> <!-- This is in sp because we want the icon to scale with the TextView it sits alongside. --> - <dimen name="content_suggestions_card_modern_margin">12dp</dimen> - <dimen name="content_suggestions_card_modern_margin_v2">0dp</dimen> - <dimen name="content_suggestions_card_modern_padding_v2">16dp</dimen> + <dimen name="content_suggestions_card_modern_margin">0dp</dimen> + <dimen name="content_suggestions_card_modern_padding">16dp</dimen> + <dimen name="content_suggestions_card_bottom_margin">12dp</dimen> <dimen name="md_incognito_ntp_line_spacing">6sp</dimen> <dimen name="md_incognito_ntp_padding_left">16dp</dimen> <dimen name="cryptid_height_in_logo_wrapper">60dp</dimen>
diff --git a/chrome/android/java/res/values/styles.xml b/chrome/android/java/res/values/styles.xml index 961a3b16..9e555df 100644 --- a/chrome/android/java/res/values/styles.xml +++ b/chrome/android/java/res/values/styles.xml
@@ -66,7 +66,10 @@ navigation bar colors from being applied --> <style name="Theme.Chromium.SearchActivity" parent="Base.Theme.Chromium.WithWindowAnimation" /> - <style name="Base.Theme.Chromium.TabbedMode" parent="Theme.Chromium.WithWindowAnimation" /> + <style name="Base.Theme.Chromium.TabbedMode" parent="Theme.Chromium.WithWindowAnimation"> + <!-- Attributes for material design component. See https://crbug.com/1186712 --> + <item name="colorSurface">@color/material_color_surface</item> + </style> <style name="Theme.Chromium.TabbedMode" parent="Base.Theme.Chromium.TabbedMode" /> <!-- Web app themes --> @@ -548,7 +551,7 @@ <!-- Content and Site Suggestions --> <style name="SuggestionCardModern" parent="Card"> - <item name="android:layout_marginBottom">@dimen/content_suggestions_card_modern_margin</item> + <item name="android:layout_marginBottom">@dimen/content_suggestions_card_bottom_margin</item> </style> <!-- Password manager settings page -->
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java index 3af5ba67..581b085 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java
@@ -86,7 +86,7 @@ import org.chromium.chrome.browser.download.DownloadOpenSource; import org.chromium.chrome.browser.download.DownloadUtils; import org.chromium.chrome.browser.feature_engagement.TrackerFactory; -import org.chromium.chrome.browser.feed.FeedV2; +import org.chromium.chrome.browser.feed.v2.FeedStreamSurface; import org.chromium.chrome.browser.firstrun.FirstRunSignInProcessor; import org.chromium.chrome.browser.flags.ActivityType; import org.chromium.chrome.browser.flags.ChromeFeatureList; @@ -770,7 +770,7 @@ private void maybeGetFeedAppLifecycleAndMaybeCreatePageViewObserver() { try (TraceEvent e = TraceEvent.scoped("ChromeTabbedActivity." + "maybeGetFeedAppLifecycleAndMaybeCreatePageViewObserver")) { - FeedV2.startup(); + FeedStreamSurface.startup(); if (UsageStatsService.isEnabled()) { UsageStatsService.getInstance().createPageViewObserver(
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java index 5c0257a..ae46341 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java
@@ -2264,13 +2264,13 @@ return false; } - if (id == R.id.bookmark_this_page_id || id == R.id.bookmark_this_page_chip_id) { + if (id == R.id.bookmark_this_page_id) { addOrEditBookmark(currentTab); RecordUserAction.record("MobileMenuAddToBookmarks"); return true; } - if (id == R.id.offline_page_id || id == R.id.offline_page_chip_id) { + if (id == R.id.offline_page_id) { DownloadUtils.downloadOfflinePage(this, currentTab); RecordUserAction.record("MobileMenuDownloadPage"); return true;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/app/appmenu/AppMenuPropertiesDelegateImpl.java b/chrome/android/java/src/org/chromium/chrome/browser/app/appmenu/AppMenuPropertiesDelegateImpl.java index 3e639eb9..ceb95ead 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/app/appmenu/AppMenuPropertiesDelegateImpl.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/app/appmenu/AppMenuPropertiesDelegateImpl.java
@@ -11,7 +11,6 @@ import android.os.Bundle; import android.os.SystemClock; import android.text.TextUtils; -import android.util.Pair; import android.view.Menu; import android.view.MenuItem; import android.view.SubMenu; @@ -42,7 +41,6 @@ import org.chromium.chrome.browser.flags.CachedFeatureFlags; import org.chromium.chrome.browser.flags.ChromeFeatureList; import org.chromium.chrome.browser.flags.ChromeSwitches; -import org.chromium.chrome.browser.flags.StringCachedFieldTrialParameter; import org.chromium.chrome.browser.image_descriptions.ImageDescriptionsController; import org.chromium.chrome.browser.incognito.IncognitoUtils; import org.chromium.chrome.browser.multiwindow.MultiWindowModeStateDispatcher; @@ -69,23 +67,13 @@ import org.chromium.ui.base.DeviceFormFactor; import java.util.ArrayList; -import java.util.Arrays; -import java.util.HashSet; -import java.util.LinkedHashMap; import java.util.List; -import java.util.Map; -import java.util.Set; /** * Base implementation of {@link AppMenuPropertiesDelegate} that handles hiding and showing menu * items based on activity state. */ public class AppMenuPropertiesDelegateImpl implements AppMenuPropertiesDelegate { - public static final StringCachedFieldTrialParameter THREE_BUTTON_ACTION_BAR_VARIATION = - new StringCachedFieldTrialParameter( - ChromeFeatureList.TABBED_APP_OVERFLOW_MENU_THREE_BUTTON_ACTIONBAR, - "three_button_action_bar", ""); - private static Boolean sItemBookmarkedForTesting; protected MenuItem mReloadMenuItem; @@ -105,13 +93,6 @@ // Keeps track of which menu item was shown when installable app is detected. private int mAddAppTitleShown; - // The keys of the Map are menuitem ids, the first elements in the Pair are menuitem ids, - // and the second elements in the Pair are AppMenuSimilarSelectionType. If users first - // selected the menuitems in the Pair.first, and then selected a menuitem which is the key - // if the Map, then users' selection match the pattern Pair.second. - private static final Map<Integer, Pair<Set<Integer>, Integer>> sSimilarSelectedMenuItemMap = - createSimilarSelectedMap(); - @VisibleForTesting @IntDef({MenuGroup.INVALID, MenuGroup.PAGE_MENU, MenuGroup.OVERVIEW_MODE_MENU, MenuGroup.START_SURFACE_MODE_MENU, MenuGroup.TABLET_EMPTY_MODE_MENU}) @@ -123,33 +104,6 @@ int TABLET_EMPTY_MODE_MENU = 3; } - @IntDef({ThreeButtonActionBarType.DISABLED, ThreeButtonActionBarType.ACTION_CHIP_VIEW, - ThreeButtonActionBarType.DESTINATION_CHIP_VIEW, - ThreeButtonActionBarType.DEPRECATED_ADD_TO_OPTION}) - @interface ThreeButtonActionBarType { - int DISABLED = 0; - int ACTION_CHIP_VIEW = 1; - int DESTINATION_CHIP_VIEW = 2; - int DEPRECATED_ADD_TO_OPTION = 3; - } - - /** - * Keep this list sync with AppMenuSimilarSelectionType in enums.xml. - */ - @IntDef({AppMenuSimilarSelectionType.NO_MATCH, - AppMenuSimilarSelectionType.BOOKMARK_PAGE_THEN_ALL_BOOKMARKS, - AppMenuSimilarSelectionType.ALL_BOOKMARKS_THEN_BOOKMARK_PAGE, - AppMenuSimilarSelectionType.DOWNLOAD_PAGE_THEN_ALL_DOWNLOADS, - AppMenuSimilarSelectionType.ALL_DOWNLOADS_THEN_DOWNLOAD_PAGE}) - @interface AppMenuSimilarSelectionType { - int NO_MATCH = -1; - int BOOKMARK_PAGE_THEN_ALL_BOOKMARKS = 0; - int ALL_BOOKMARKS_THEN_BOOKMARK_PAGE = 1; - int DOWNLOAD_PAGE_THEN_ALL_DOWNLOADS = 2; - int ALL_DOWNLOADS_THEN_DOWNLOAD_PAGE = 3; - int NUM_ENTRIES = 4; - } - protected @Nullable OverviewModeBehavior mOverviewModeBehavior; protected BookmarkBridge mBookmarkBridge; protected Runnable mAppMenuInvalidator; @@ -214,7 +168,6 @@ customViewBinders.add(new ManagedByMenuItemViewBinder()); customViewBinders.add(new IncognitoMenuItemViewBinder()); customViewBinders.add(new DividerLineMenuItemViewBinder()); - customViewBinders.add(new ChipViewMenuItemViewBinder(getThreeButtonActionBarType())); return customViewBinders; } @@ -309,26 +262,12 @@ loadingStateChanged(currentTab.isLoading()); MenuItem bookmarkMenuItem = actionBar.findItem(R.id.bookmark_this_page_id); - if (shouldShowThreeButtonActionBar()) { - actionBar.removeItem(R.id.bookmark_this_page_id); - } else { - updateBookmarkMenuItem(bookmarkMenuItem, currentTab); - } + updateBookmarkMenuItem(bookmarkMenuItem, currentTab); MenuItem offlineMenuItem = actionBar.findItem(R.id.offline_page_id); - if (offlineMenuItem != null) { - if (shouldShowThreeButtonActionBar()) { - actionBar.removeItem(R.id.offline_page_id); - } else { - offlineMenuItem.setEnabled(shouldEnableDownloadPage(currentTab)); - } - } + offlineMenuItem.setEnabled(shouldEnableDownloadPage(currentTab)); - if (shouldShowThreeButtonActionBar()) { - assert actionBar.size() == 3; - } else { - assert actionBar.size() == 5; - } + assert actionBar.size() == 5; } mUpdateMenuItemVisible = shouldShowUpdateMenuItem(); @@ -340,41 +279,6 @@ menu.findItem(R.id.move_to_other_window_menu_id).setVisible(shouldShowMoveToOtherWindow()); - if (shouldShowThreeButtonActionBar()) { - @ThreeButtonActionBarType - int threeButtonActionBarType = getThreeButtonActionBarType(); - - MenuItem downloadMenuItem = - menu.findItem(R.id.downloads_row_menu_id).getSubMenu().getItem(1); - assert downloadMenuItem.getItemId() == R.id.offline_page_chip_id; - downloadMenuItem.setEnabled(shouldEnableDownloadPage(currentTab)); - - MenuItem bookmarkMenuItem = - menu.findItem(R.id.all_bookmarks_row_menu_id).getSubMenu().getItem(1); - assert bookmarkMenuItem.getItemId() == R.id.bookmark_this_page_chip_id; - updateBookmarkMenuItem(bookmarkMenuItem, currentTab); - - // Update titles for ChipView menu items. - if (threeButtonActionBarType == ThreeButtonActionBarType.ACTION_CHIP_VIEW) { - downloadMenuItem.setTitle(R.string.add); - if (bookmarkMenuItem.isChecked()) { - bookmarkMenuItem.setTitle(R.string.bookmark_item_edit); - } else { - bookmarkMenuItem.setTitle(R.string.add); - } - } else if (threeButtonActionBarType == ThreeButtonActionBarType.DESTINATION_CHIP_VIEW) { - MenuItem allDownloadMenuItem = - menu.findItem(R.id.downloads_row_menu_id).getSubMenu().getItem(0); - assert allDownloadMenuItem.getItemId() == R.id.downloads_menu_id; - allDownloadMenuItem.setTitle(R.string.all); - - MenuItem allBookmarkMenuItem = - menu.findItem(R.id.all_bookmarks_row_menu_id).getSubMenu().getItem(0); - assert allBookmarkMenuItem.getItemId() == R.id.all_bookmarks_menu_id; - allBookmarkMenuItem.setTitle(R.string.all); - } - } - // Don't allow either "chrome://" pages or interstitial pages to be shared. menu.findItem(R.id.share_row_menu_id).setVisible(mShareUtils.shouldEnableShare(currentTab)); @@ -456,13 +360,6 @@ && item.getItemId() != R.id.update_menu_id) { item.setIcon(null); } - // Remove icons for menu items that have submenus. - if (item.getItemId() == R.id.downloads_row_menu_id - || item.getItemId() == R.id.all_bookmarks_row_menu_id) { - for (int j = 0; j < item.getSubMenu().size(); ++j) { - item.getSubMenu().getItem(j).setIcon(null); - } - } } if (item.getItemId() == R.id.new_incognito_tab_menu_id && item.isVisible()) { @@ -841,94 +738,4 @@ static void setPageBookmarkedForTesting(Boolean bookmarked) { sItemBookmarkedForTesting = bookmarked; } - - private static boolean shouldShowThreeButtonActionBar() { - return CachedFeatureFlags.isEnabled( - ChromeFeatureList.TABBED_APP_OVERFLOW_MENU_THREE_BUTTON_ACTIONBAR); - } - - /** - * @return The type of three button action bar should be shown. - */ - private static @ThreeButtonActionBarType int getThreeButtonActionBarType() { - if (shouldShowThreeButtonActionBar()) { - if (THREE_BUTTON_ACTION_BAR_VARIATION.getValue().equals("action_chip_view")) { - return ThreeButtonActionBarType.ACTION_CHIP_VIEW; - } else if (THREE_BUTTON_ACTION_BAR_VARIATION.getValue().equals( - "destination_chip_view")) { - return ThreeButtonActionBarType.DESTINATION_CHIP_VIEW; - } - } - return ThreeButtonActionBarType.DISABLED; - } - - /** - * @return The "download" menu items id in the app menu. - */ - public static int getOfflinePageId() { - @ThreeButtonActionBarType - int type = getThreeButtonActionBarType(); - if (type == ThreeButtonActionBarType.ACTION_CHIP_VIEW - || type == ThreeButtonActionBarType.DESTINATION_CHIP_VIEW) { - return R.id.offline_page_chip_id; - } - return R.id.offline_page_id; - } - - @Override - public boolean recordAppMenuSimilarSelectionIfNeeded( - int previousMenuItemId, int currentMenuItemId) { - @AppMenuSimilarSelectionType - int pattern = findSimilarSelectionPattern(previousMenuItemId, currentMenuItemId); - if (pattern == AppMenuSimilarSelectionType.NO_MATCH) { - return false; - } - - RecordHistogram.recordEnumeratedHistogram("Mobile.AppMenu.SimilarSelection", pattern, - AppMenuSimilarSelectionType.NUM_ENTRIES); - return true; - } - - @VisibleForTesting(otherwise = VisibleForTesting.PACKAGE_PRIVATE) - public @AppMenuSimilarSelectionType int findSimilarSelectionPattern( - int previousMenuItemId, int currentMenuItemId) { - Pair<Set<Integer>, Integer> menuItemToSelectType = - sSimilarSelectedMenuItemMap.get(currentMenuItemId); - if (menuItemToSelectType != null - && menuItemToSelectType.first.contains(previousMenuItemId)) { - return menuItemToSelectType.second; - } - - return AppMenuSimilarSelectionType.NO_MATCH; - } - - private static Map<Integer, Pair<Set<Integer>, Integer>> createSimilarSelectedMap() { - Map<Integer, Pair<Set<Integer>, Integer>> map = new LinkedHashMap<>(); - map.put(R.id.all_bookmarks_menu_id, - new Pair<Set<Integer>, Integer>( - new HashSet<>(Arrays.asList( - R.id.bookmark_this_page_id, R.id.bookmark_this_page_chip_id)), - AppMenuSimilarSelectionType.BOOKMARK_PAGE_THEN_ALL_BOOKMARKS)); - map.put(R.id.bookmark_this_page_id, - new Pair<Set<Integer>, Integer>( - new HashSet<>(Arrays.asList(R.id.all_bookmarks_menu_id)), - AppMenuSimilarSelectionType.ALL_BOOKMARKS_THEN_BOOKMARK_PAGE)); - map.put(R.id.bookmark_this_page_chip_id, - new Pair<Set<Integer>, Integer>( - new HashSet<>(Arrays.asList(R.id.all_bookmarks_menu_id)), - AppMenuSimilarSelectionType.ALL_BOOKMARKS_THEN_BOOKMARK_PAGE)); - map.put(R.id.downloads_menu_id, - new Pair<Set<Integer>, Integer>(new HashSet<>(Arrays.asList(R.id.offline_page_id, - R.id.offline_page_chip_id)), - AppMenuSimilarSelectionType.DOWNLOAD_PAGE_THEN_ALL_DOWNLOADS)); - map.put(R.id.offline_page_id, - new Pair<Set<Integer>, Integer>( - new HashSet<>(Arrays.asList(R.id.downloads_menu_id)), - AppMenuSimilarSelectionType.ALL_DOWNLOADS_THEN_DOWNLOAD_PAGE)); - map.put(R.id.offline_page_chip_id, - new Pair<Set<Integer>, Integer>( - new HashSet<>(Arrays.asList(R.id.downloads_menu_id)), - AppMenuSimilarSelectionType.ALL_DOWNLOADS_THEN_DOWNLOAD_PAGE)); - return map; - } }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/app/appmenu/ChipViewMenuItemViewBinder.java b/chrome/android/java/src/org/chromium/chrome/browser/app/appmenu/ChipViewMenuItemViewBinder.java deleted file mode 100644 index e4c5c1dc..0000000 --- a/chrome/android/java/src/org/chromium/chrome/browser/app/appmenu/ChipViewMenuItemViewBinder.java +++ /dev/null
@@ -1,141 +0,0 @@ -// Copyright 2020 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -package org.chromium.chrome.browser.app.appmenu; - -import android.content.Context; -import android.content.res.TypedArray; -import android.view.LayoutInflater; -import android.view.MenuItem; -import android.view.View; -import android.view.ViewGroup; - -import androidx.annotation.ColorRes; -import androidx.annotation.Nullable; -import androidx.appcompat.content.res.AppCompatResources; - -import org.chromium.chrome.R; -import org.chromium.chrome.browser.app.appmenu.AppMenuPropertiesDelegateImpl.ThreeButtonActionBarType; -import org.chromium.chrome.browser.ui.appmenu.AppMenuClickHandler; -import org.chromium.chrome.browser.ui.appmenu.CustomViewBinder; -import org.chromium.components.browser_ui.widget.highlight.ViewHighlighter; -import org.chromium.components.browser_ui.widget.text.TextViewWithCompoundDrawables; -import org.chromium.ui.widget.ChipView; - -/** - * A custom view binder used to bind a menu item with an optional chip shown after the main menu - * item text. - */ -class ChipViewMenuItemViewBinder implements CustomViewBinder { - private static final int CHIP_VIEW_ITEM_VIEW_TYPE = 0; - private final @ThreeButtonActionBarType int mThreeButtonActionBarType; - - ChipViewMenuItemViewBinder(@ThreeButtonActionBarType int threeButtonActionBarType) { - mThreeButtonActionBarType = threeButtonActionBarType; - } - - @Override - public int getViewTypeCount() { - return 1; - } - - @Override - public int getItemViewType(int id) { - return (id == R.id.downloads_row_menu_id || id == R.id.all_bookmarks_row_menu_id) - ? CHIP_VIEW_ITEM_VIEW_TYPE - : CustomViewBinder.NOT_HANDLED; - } - - @Override - public View getView(MenuItem item, @Nullable View convertView, ViewGroup parent, - LayoutInflater inflater, AppMenuClickHandler appMenuClickHandler, - @Nullable Integer highlightedItemId) { - assert item.getItemId() == R.id.downloads_row_menu_id - || item.getItemId() == R.id.all_bookmarks_row_menu_id; - - final MenuItem destinationItem = item.getSubMenu().getItem(0); - final MenuItem actionItem = item.getSubMenu().getItem(1); - - // By default, when no experiments are enabled, the menu item should go to the destination - // (downloads manager or bookmarks manager). - MenuItem mainMenuItem = destinationItem; - MenuItem chipViewMenuItem = null; - - if (mThreeButtonActionBarType == ThreeButtonActionBarType.ACTION_CHIP_VIEW) { - // If the action chip view variant is enabled, the chip view will be the add bookmark or - // add download action. - chipViewMenuItem = actionItem; - } else if (mThreeButtonActionBarType == ThreeButtonActionBarType.DESTINATION_CHIP_VIEW) { - // Else, if the destination chip view variant is enabled, the chip view will be the - // destination and the menu item text will be the action. - mainMenuItem = actionItem; - chipViewMenuItem = destinationItem; - } // else the menu item will not have chip view as other normal menu items. - - ChipViewMenuItemViewHolder holder; - if (convertView == null || !(convertView.getTag() instanceof ChipViewMenuItemViewHolder)) { - holder = new ChipViewMenuItemViewHolder(); - convertView = inflater.inflate(R.layout.chip_view_menu_item, parent, false); - holder.title = convertView.findViewById(R.id.title); - holder.chipView = convertView.findViewById(R.id.chip_view); - convertView.setTag(holder); - } else { - holder = (ChipViewMenuItemViewHolder) convertView.getTag(); - } - - holder.title.setCompoundDrawablesRelative(mainMenuItem.getIcon(), null, null, null); - holder.title.setText(mainMenuItem.getTitle()); - holder.title.setEnabled(mainMenuItem.isEnabled()); - final MenuItem finalMainMenuItem = mainMenuItem; - holder.title.setOnClickListener(v -> appMenuClickHandler.onItemClick(finalMainMenuItem)); - @ColorRes - int theme = mainMenuItem.isChecked() ? R.color.blue_mode_tint - : R.color.default_icon_color_secondary_tint_list; - holder.title.setDrawableTintColor( - AppCompatResources.getColorStateList(convertView.getContext(), theme)); - - if (chipViewMenuItem != null) { - holder.chipView.setVisibility(View.VISIBLE); - holder.chipView.getPrimaryTextView().setText(chipViewMenuItem.getTitle()); - holder.chipView.setEnabled(chipViewMenuItem.isEnabled()); - final MenuItem finalChipViewMenuItem = chipViewMenuItem; - holder.chipView.setOnClickListener( - v -> appMenuClickHandler.onItemClick(finalChipViewMenuItem)); - - if (highlightedItemId != null && chipViewMenuItem.getItemId() == highlightedItemId) { - ViewHighlighter.turnOnRectangularHighlight( - holder.chipView, holder.chipView.getCornerRadius()); - } else { - ViewHighlighter.turnOffHighlight(holder.chipView); - } - } - - if (highlightedItemId != null && mainMenuItem.getItemId() == highlightedItemId) { - ViewHighlighter.turnOnRectangularHighlight(holder.title); - } else { - ViewHighlighter.turnOffHighlight(holder.title); - } - - convertView.setEnabled(false); - - return convertView; - } - - @Override - public boolean supportsEnterAnimation(int id) { - return true; - } - - @Override - public int getPixelHeight(Context context) { - TypedArray a = context.obtainStyledAttributes( - new int[] {android.R.attr.listPreferredItemHeightSmall}); - return a.getDimensionPixelSize(0, 0); - } - - private static class ChipViewMenuItemViewHolder { - public TextViewWithCompoundDrawables title; - public ChipView chipView; - } -}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/app/flags/ChromeCachedFlags.java b/chrome/android/java/src/org/chromium/chrome/browser/app/flags/ChromeCachedFlags.java index 1d37106..1f81a422 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/app/flags/ChromeCachedFlags.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/app/flags/ChromeCachedFlags.java
@@ -7,7 +7,6 @@ import android.text.TextUtils; import org.chromium.base.annotations.RemovableInRelease; -import org.chromium.chrome.browser.app.appmenu.AppMenuPropertiesDelegateImpl; import org.chromium.chrome.browser.compositor.layouts.content.TabContentManager; import org.chromium.chrome.browser.firstrun.FirstRunUtils; import org.chromium.chrome.browser.flags.CachedFeatureFlags; @@ -81,7 +80,6 @@ ChromeFeatureList.TAB_GROUPS_ANDROID, ChromeFeatureList.TAB_GROUPS_CONTINUATION_ANDROID, ChromeFeatureList.TAB_TO_GTS_ANIMATION, - ChromeFeatureList.TABBED_APP_OVERFLOW_MENU_THREE_BUTTON_ACTIONBAR, ChromeFeatureList.THEME_REFACTOR_ANDROID, ChromeFeatureList.TOOLBAR_USE_HARDWARE_BITMAP_DRAW, ChromeFeatureList.USE_CHIME_ANDROID_SDK, @@ -94,7 +92,6 @@ // clang-format off List<CachedFieldTrialParameter> fieldTrialsToCache = Arrays.asList( AdaptiveToolbarFeatures.MODE_PARAM, - AppMenuPropertiesDelegateImpl.THREE_BUTTON_ACTION_BAR_VARIATION, ConditionalTabStripUtils.CONDITIONAL_TAB_STRIP_INFOBAR_LIMIT, ConditionalTabStripUtils.CONDITIONAL_TAB_STRIP_INFOBAR_PERIOD, ConditionalTabStripUtils.CONDITIONAL_TAB_STRIP_SESSION_TIME_MS,
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/contextmenu/ChromeContextMenuPopulator.java b/chrome/android/java/src/org/chromium/chrome/browser/contextmenu/ChromeContextMenuPopulator.java index 04b2d87..8f3e0f4 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/contextmenu/ChromeContextMenuPopulator.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/contextmenu/ChromeContextMenuPopulator.java
@@ -90,7 +90,6 @@ private final Supplier<ShareDelegate> mShareDelegateSupplier; private final ExternalAuthUtils mExternalAuthUtils; private final ContextMenuParams mParams; - private boolean mEnableLensWithSearchByImageText; private boolean mIsLensIntentInProgress; private @Nullable UkmRecorder.Bridge mUkmRecorderBridge; private ContextMenuNativeDelegate mNativeDelegate; @@ -452,12 +451,7 @@ getSearchByImageMenuItemsToShowAndRecordMetrics( mParams.getPageUrl(), mItemDelegate.isIncognito()); if (imageSearchMenuItemsToShow.get(LENS_SEARCH_MENU_ITEM_KEY)) { - if (LensUtils.useLensWithSearchByImageText()) { - mEnableLensWithSearchByImageText = true; - imageGroup.add(createListItem(Item.SEARCH_BY_IMAGE)); - } else { - imageGroup.add(createListItem(Item.SEARCH_WITH_GOOGLE_LENS, true)); - } + imageGroup.add(createListItem(Item.SEARCH_WITH_GOOGLE_LENS, true)); maybeRecordUkmLensShown(); } else if (imageSearchMenuItemsToShow.get(SEARCH_BY_IMAGE_MENU_ITEM_KEY)) { imageGroup.add(createListItem(Item.SEARCH_BY_IMAGE)); @@ -690,14 +684,8 @@ prefManager.writeBoolean( ChromePreferenceKeys.CONTEXT_MENU_SEARCH_WITH_GOOGLE_LENS_CLICKED, true); } else if (itemId == R.id.contextmenu_search_by_image) { - if (mEnableLensWithSearchByImageText) { - recordContextMenuSelection(ContextMenuUma.Action.SEARCH_WITH_GOOGLE_LENS); - searchWithGoogleLens(LensEntryPoint.CONTEXT_MENU_SEARCH_MENU_ITEM, - /*requiresConfirmation=*/false); - } else { - recordContextMenuSelection(ContextMenuUma.Action.SEARCH_BY_IMAGE); - mNativeDelegate.searchForImage(); - } + recordContextMenuSelection(ContextMenuUma.Action.SEARCH_BY_IMAGE); + mNativeDelegate.searchForImage(); } else if (itemId == R.id.contextmenu_shop_similar_products) { recordContextMenuSelection(ContextMenuUma.Action.SHOP_SIMILAR_PRODUCTS); searchWithGoogleLens(
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/messages/MessageContainerCoordinator.java b/chrome/android/java/src/org/chromium/chrome/browser/messages/MessageContainerCoordinator.java index 97c6f45e..0e1a62f3 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/messages/MessageContainerCoordinator.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/messages/MessageContainerCoordinator.java
@@ -64,9 +64,8 @@ public int getMessageMaxTranslation() { // The max translation is message height + message shadow + controls height (adjusted for // Message container offsets) - final int messageHeightWithShadow = mContainer.findViewById(R.id.message_banner).getHeight() - + mContainer.getResources().getDimensionPixelOffset( - R.dimen.message_shadow_top_margin); + final int messageHeightWithShadow = + mContainer.getMessageBannerHeight() + mContainer.getMessageShadowTopMargin(); return messageHeightWithShadow + getContainerTopOffset(); } @@ -87,6 +86,6 @@ final Resources res = mContainer.getResources(); return mControlsManager.getContentOffset() - res.getDimensionPixelOffset(R.dimen.message_bubble_inset) - - res.getDimensionPixelOffset(R.dimen.message_shadow_top_margin); + - mContainer.getMessageShadowTopMargin(); } }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ntp/NewTabPage.java b/chrome/android/java/src/org/chromium/chrome/browser/ntp/NewTabPage.java index e415c07..a001c91 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/ntp/NewTabPage.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/ntp/NewTabPage.java
@@ -35,7 +35,6 @@ import org.chromium.chrome.browser.download.DownloadManagerService; import org.chromium.chrome.browser.feature_engagement.TrackerFactory; import org.chromium.chrome.browser.feed.FeedSurfaceCoordinator; -import org.chromium.chrome.browser.feed.FeedV1ActionOptions; import org.chromium.chrome.browser.feed.NtpStreamLifecycleManager; import org.chromium.chrome.browser.feed.StreamLifecycleManager; import org.chromium.chrome.browser.feed.shared.FeedSurfaceDelegate; @@ -55,7 +54,6 @@ import org.chromium.chrome.browser.query_tiles.QueryTileSection.QueryInfo; import org.chromium.chrome.browser.search_engines.TemplateUrlServiceFactory; import org.chromium.chrome.browser.share.ShareDelegate; -import org.chromium.chrome.browser.suggestions.SuggestionsDependencyFactory; import org.chromium.chrome.browser.suggestions.SuggestionsMetrics; import org.chromium.chrome.browser.suggestions.SuggestionsNavigationDelegate; import org.chromium.chrome.browser.suggestions.SuggestionsUiDelegateImpl; @@ -297,8 +295,6 @@ mNewTabPageUma = uma; Profile profile = Profile.fromWebContents(mTab.getWebContents()); - SuggestionsDependencyFactory depsFactory = SuggestionsDependencyFactory.getInstance(); - SuggestionsNavigationDelegate navigationDelegate = new SuggestionsNavigationDelegate( activity, profile, nativePageHost, tabModelSelector, mTab); mNewTabPageManager = new NewTabPageManagerImpl( @@ -349,8 +345,8 @@ mActivityLifecycleDispatcher.register(mLifecycleObserver); updateSearchProviderHasLogo(); - initializeMainView(activity, windowAndroid, snackbarManager, tabModelSelector, uma, - isInNightMode, bottomSheetController, shareDelegateSupplier); + initializeMainView(activity, windowAndroid, snackbarManager, uma, isInNightMode, + bottomSheetController, shareDelegateSupplier); mBrowserControlsStateProvider = browserControlsStateProvider; getView().addOnAttachStateChangeListener(new View.OnAttachStateChangeListener() { @@ -395,15 +391,14 @@ * @param activity The activity used to initialize the view. * @param windowAndroid Provides the current active tab. * @param snackbarManager {@link SnackbarManager} object. - * @param tabModelSelector {@link TabModelSelector} object. * @param uma {@link NewTabPageUma} object recording user metrics. * @param isInNightMode {@code true} if the night mode setting is on. * @param bottomSheetController The controller for bottom sheets. Used by the feed. * @param shareDelegateSupplier Supplies a delegate used to open SharingHub. */ protected void initializeMainView(Activity activity, WindowAndroid windowAndroid, - SnackbarManager snackbarManager, TabModelSelector tabModelSelector, NewTabPageUma uma, - boolean isInNightMode, BottomSheetController bottomSheetController, + SnackbarManager snackbarManager, NewTabPageUma uma, boolean isInNightMode, + BottomSheetController bottomSheetController, ObservableSupplier<ShareDelegate> shareDelegateSupplier) { Profile profile = Profile.fromWebContents(mTab.getWebContents()); @@ -419,13 +414,12 @@ R.layout.new_tab_page_feed_v2_expandable_header, null, false); } - mFeedSurfaceProvider = - new FeedSurfaceCoordinator(activity, snackbarManager, tabModelSelector, - windowAndroid, new SnapScrollHelper(mNewTabPageManager, mNewTabPageLayout), - mNewTabPageLayout, sectionHeaderView, new FeedV1ActionOptions(), - isInNightMode, this, mNewTabPageManager.getNavigationDelegate(), profile, - /* isPlaceholderShownInitially= */ false, bottomSheetController, - shareDelegateSupplier, /* externalScrollableContainerDelegate= */ null); + mFeedSurfaceProvider = new FeedSurfaceCoordinator(activity, snackbarManager, windowAndroid, + new SnapScrollHelper(mNewTabPageManager, mNewTabPageLayout), mNewTabPageLayout, + sectionHeaderView, isInNightMode, this, mNewTabPageManager.getNavigationDelegate(), + profile, + /* isPlaceholderShownInitially= */ false, bottomSheetController, + shareDelegateSupplier, /* externalScrollableContainerDelegate= */ null); // Record the timestamp at which the new tab page's construction started. uma.trackTimeToFirstDraw(mFeedSurfaceProvider.getView(), mConstructedTimeNs);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/voice/VoiceRecognitionHandler.java b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/voice/VoiceRecognitionHandler.java index 7a97c475..3aa795f4 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/voice/VoiceRecognitionHandler.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/voice/VoiceRecognitionHandler.java
@@ -156,6 +156,23 @@ private final SettingsLauncher mSettingsLauncher; /** + * AudioPermissionState defined in tools/metrics/histograms/enums.xml. + * + * Do not reorder or remove items, only add new items before NUM_ENTRIES. + */ + @IntDef({AudioPermissionState.GRANTED, AudioPermissionState.DENIED_CAN_ASK_AGAIN, + AudioPermissionState.DENIED_CANNOT_ASK_AGAIN}) + public @interface AudioPermissionState { + // Permissions have been granted and won't be requested this time. + int GRANTED = 0; + int DENIED_CAN_ASK_AGAIN = 1; + int DENIED_CANNOT_ASK_AGAIN = 2; + + // Be sure to also update enums.xml when updating these values. + int NUM_ENTRIES = 3; + } + + /** * VoiceInteractionEventSource defined in tools/metrics/histograms/enums.xml. * * Do not reorder or remove items, only add new items before NUM_ENTRIES. @@ -728,24 +745,33 @@ */ private boolean ensureAudioPermissionGranted( Activity activity, WindowAndroid windowAndroid, @VoiceInteractionSource int source) { - if (windowAndroid.hasPermission(Manifest.permission.RECORD_AUDIO)) return true; - + if (windowAndroid.hasPermission(Manifest.permission.RECORD_AUDIO)) { + recordAudioPermissionStateEvent(AudioPermissionState.GRANTED); + return true; + } // If we don't have permission and also can't ask, then there's no more work left other // than telling the delegate to update the mic state. if (!windowAndroid.canRequestPermission(Manifest.permission.RECORD_AUDIO)) { + recordAudioPermissionStateEvent(AudioPermissionState.DENIED_CANNOT_ASK_AGAIN); notifyVoiceAvailabilityImpacted(); return false; } PermissionCallback callback = (permissions, grantResults) -> { if (grantResults.length != 1) { + recordAudioPermissionStateEvent(AudioPermissionState.DENIED_CAN_ASK_AGAIN); return; } if (grantResults[0] == PackageManager.PERMISSION_GRANTED) { + // Don't record granted permission here, it will get logged from + // within startSystemForVoiceSearch call. startSystemForVoiceSearch(activity, windowAndroid, source); } else if (!windowAndroid.canRequestPermission(Manifest.permission.RECORD_AUDIO)) { + recordAudioPermissionStateEvent(AudioPermissionState.DENIED_CANNOT_ASK_AGAIN); notifyVoiceAvailabilityImpacted(); + } else { + recordAudioPermissionStateEvent(AudioPermissionState.DENIED_CAN_ASK_AGAIN); } }; windowAndroid.requestPermissions(new String[] {Manifest.permission.RECORD_AUDIO}, callback); @@ -1195,6 +1221,16 @@ "VoiceInteraction.AssistantIntent.TranslateExtrasAttached", result); } + /** + * Records audio permissions state when a system voice recognition is requested. + * @param permissionsState The current RECORD_AUDIO permission state. + */ + @VisibleForTesting + protected void recordAudioPermissionStateEvent(@AudioPermissionState int permissionsState) { + RecordHistogram.recordEnumeratedHistogram("VoiceInteraction.AudioPermissionEvent", + permissionsState, AudioPermissionState.NUM_ENTRIES); + } + /** Allows for overriding the TranslateBridgeWrapper for test purposes. */ @VisibleForTesting protected void setTranslateBridgeWrapper(TranslateBridgeWrapper wrapper) {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/read_later/ReadLaterIPHController.java b/chrome/android/java/src/org/chromium/chrome/browser/read_later/ReadLaterIPHController.java index ea3c7eb..1ad3c191 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/read_later/ReadLaterIPHController.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/read_later/ReadLaterIPHController.java
@@ -43,10 +43,6 @@ */ public void onCopyContextMenuItemClicked() { if (!ChromeFeatureList.isEnabled(ChromeFeatureList.READ_LATER)) return; - if (ChromeFeatureList.isEnabled( - ChromeFeatureList.TABBED_APP_OVERFLOW_MENU_THREE_BUTTON_ACTIONBAR)) { - return; - } mUserEducationHelper.requestShowIPH( new IPHCommandBuilder(mToolbarMenuButton.getContext().getResources(), FeatureConstants.READ_LATER_APP_MENU_BOOKMARK_THIS_PAGE_FEATURE, @@ -68,10 +64,6 @@ private void showReadLaterAppMenuBookmarksIPH() { if (!ChromeFeatureList.isEnabled(ChromeFeatureList.READ_LATER)) return; - if (ChromeFeatureList.isEnabled( - ChromeFeatureList.TABBED_APP_OVERFLOW_MENU_THREE_BUTTON_ACTIONBAR)) { - return; - } mUserEducationHelper.requestShowIPH( new IPHCommandBuilder(mToolbarMenuButton.getContext().getResources(), FeatureConstants.READ_LATER_APP_MENU_BOOKMARKS_FEATURE,
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/share/LensUtils.java b/chrome/android/java/src/org/chromium/chrome/browser/share/LensUtils.java index ca72df5c..469fe5d7 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/share/LensUtils.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/share/LensUtils.java
@@ -52,8 +52,6 @@ "minAgsaVersionForDirectIntent"; private static final String MIN_AGSA_VERSION_DIRECT_INTENT_SDK_FEATURE_PARAM_NAME = "minAgsaVersionForDirectIntentSdk"; - private static final String USE_SEARCH_BY_IMAGE_TEXT_FEATURE_PARAM_NAME = - "useSearchByImageText"; private static final String LENS_SHOPPING_ALLOWLIST_ENTRIES_FEATURE_PARAM_NAME = "allowlistEntries"; private static final String LENS_SHOPPING_URL_PATTERNS_FEATURE_PARAM_NAME = @@ -503,15 +501,6 @@ } /** - * Whether to display the lens menu item with the search by image text - */ - public static boolean useLensWithSearchByImageText() { - return ChromeFeatureList.getFieldTrialParamByFeatureAsBoolean( - ChromeFeatureList.CONTEXT_MENU_SEARCH_WITH_GOOGLE_LENS, - USE_SEARCH_BY_IMAGE_TEXT_FEATURE_PARAM_NAME, false); - } - - /** * Whether to display the lens menu item shop similar products. only one of the * 3 params should be set to true: useLensWithShopSimilarProducts, * useLensWithShopImageWithGoogleLens and useLensWithShopImageWithGoogleLens.
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ToolbarButtonInProductHelpController.java b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ToolbarButtonInProductHelpController.java index 63be0dd..aa80a0d 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ToolbarButtonInProductHelpController.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ToolbarButtonInProductHelpController.java
@@ -14,7 +14,6 @@ import org.chromium.base.supplier.Supplier; import org.chromium.base.task.PostTask; import org.chromium.chrome.R; -import org.chromium.chrome.browser.app.appmenu.AppMenuPropertiesDelegateImpl; import org.chromium.chrome.browser.datareduction.DataReductionSavingsMilestonePromo; import org.chromium.chrome.browser.download.DownloadUtils; import org.chromium.chrome.browser.feature_engagement.ScreenshotMonitor; @@ -291,10 +290,7 @@ new IPHCommandBuilder(mActivity.getResources(), featureName, R.string.iph_download_page_for_offline_usage_text, R.string.iph_download_page_for_offline_usage_accessibility_text) - .setOnShowCallback( - () - -> turnOnHighlightForMenuItem( - AppMenuPropertiesDelegateImpl.getOfflinePageId())) + .setOnShowCallback(() -> turnOnHighlightForMenuItem(R.id.offline_page_id)) .setOnDismissCallback(this::turnOffHighlightForMenuItem) .setAnchorView(mMenuButtonAnchorView) .build()); @@ -336,7 +332,7 @@ } Integer menuItemId = DownloadUtils.isAllowedToDownloadPage(mCurrentTabSupplier.get()) - ? AppMenuPropertiesDelegateImpl.getOfflinePageId() + ? R.id.offline_page_id : R.id.downloads_menu_id; mUserEducationHelper.requestShowIPH(
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/app/appmenu/TabbedAppMenuTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/app/appmenu/TabbedAppMenuTest.java index a91d8a5c..9653d50 100644 --- a/chrome/android/javatests/src/org/chromium/chrome/browser/app/appmenu/TabbedAppMenuTest.java +++ b/chrome/android/javatests/src/org/chromium/chrome/browser/app/appmenu/TabbedAppMenuTest.java
@@ -10,7 +10,6 @@ import android.view.Menu; import android.view.MenuItem; import android.view.View; -import android.widget.LinearLayout; import android.widget.ListView; import androidx.test.filters.SmallTest; @@ -36,7 +35,6 @@ import org.chromium.chrome.browser.ChromeTabbedActivity; import org.chromium.chrome.browser.compositor.layouts.EmptyOverviewModeObserver; import org.chromium.chrome.browser.compositor.layouts.OverviewModeBehavior; -import org.chromium.chrome.browser.flags.ChromeFeatureList; import org.chromium.chrome.browser.flags.ChromeSwitches; import org.chromium.chrome.browser.ui.appmenu.AppMenuHandler; import org.chromium.chrome.browser.ui.appmenu.AppMenuTestSupport; @@ -45,8 +43,6 @@ import org.chromium.chrome.test.util.ActivityUtils; import org.chromium.chrome.test.util.ChromeRenderTestRule; import org.chromium.chrome.test.util.ChromeTabUtils; -import org.chromium.chrome.test.util.browser.Features.DisableFeatures; -import org.chromium.chrome.test.util.browser.Features.EnableFeatures; import org.chromium.content_public.browser.UiThreadTaskTraits; import org.chromium.content_public.browser.test.util.TestThreadUtils; import org.chromium.ui.test.util.UiRestriction; @@ -243,7 +239,6 @@ @Test @SmallTest @Feature({"Browser", "Main", "Bookmark", "RenderTest"}) - @DisableFeatures({ChromeFeatureList.TABBED_APP_OVERFLOW_MENU_THREE_BUTTON_ACTIONBAR}) @Restriction(UiRestriction.RESTRICTION_TYPE_PHONE) public void testBookmarkMenuItem() throws IOException { MenuItem bookmarkStar = @@ -275,92 +270,6 @@ @Test @SmallTest @Feature({"Browser", "Main", "RenderTest"}) - @Restriction(UiRestriction.RESTRICTION_TYPE_PHONE) - @EnableFeatures({ChromeFeatureList.TABBED_APP_OVERFLOW_MENU_THREE_BUTTON_ACTIONBAR + "<Study"}) - @CommandLineFlags.Add({"force-fieldtrials=Study/Group", - "force-fieldtrial-params=Study.Group:three_button_action_bar/action_chip_view"}) - public void - testActionChipViewMenuItem() throws IOException { - LinearLayout actionBar = (LinearLayout) getListView().getChildAt(0); - Assert.assertEquals(3, actionBar.getChildCount()); - mRenderTestRule.render( - getListView().getChildAt(0), "tinted_rounded_corner_icon_row_three_buttons"); - - int downloadRowIndex = findIndexOfMenuItemById(R.id.downloads_row_menu_id); - Assert.assertNotEquals("No download row found.", -1, downloadRowIndex); - mRenderTestRule.render(getListView().getChildAt(downloadRowIndex), - "download_row_rounded_action_chip_view"); - - MenuItem bookmarkRow = AppMenuTestSupport.getMenu(mActivityTestRule.getAppMenuCoordinator()) - .findItem(R.id.all_bookmarks_row_menu_id); - MenuItem bookmarkMenuItem = bookmarkRow.getSubMenu().getItem(1); - Assert.assertFalse("Bookmark item should not be checked.", bookmarkMenuItem.isChecked()); - int bookmarkRowIndex = findIndexOfMenuItemById(R.id.all_bookmarks_row_menu_id); - Assert.assertTrue("No bookmark row found.", bookmarkRowIndex != -1); - mRenderTestRule.render(getListView().getChildAt(bookmarkRowIndex), - "bookmark_row_rounded_action_chip_view"); - - TestThreadUtils.runOnUiThreadBlocking(() -> mAppMenuHandler.hideAppMenu()); - AppMenuPropertiesDelegateImpl.setPageBookmarkedForTesting(true); - showAppMenuAndAssertMenuShown(); - InstrumentationRegistry.getInstrumentation().waitForIdleSync(); - - bookmarkRow = AppMenuTestSupport.getMenu(mActivityTestRule.getAppMenuCoordinator()) - .findItem(R.id.all_bookmarks_row_menu_id); - bookmarkMenuItem = bookmarkRow.getSubMenu().getItem(1); - Assert.assertTrue("Bookmark item should be checked.", bookmarkMenuItem.isChecked()); - mRenderTestRule.render(getListView().getChildAt(bookmarkRowIndex), - "bookmark_row_rounded_action_chip_view_bookmarked"); - - AppMenuPropertiesDelegateImpl.setPageBookmarkedForTesting(null); - } - - @Test - @SmallTest - @Feature({"Browser", "Main", "RenderTest"}) - @Restriction(UiRestriction.RESTRICTION_TYPE_PHONE) - @EnableFeatures({ChromeFeatureList.TABBED_APP_OVERFLOW_MENU_THREE_BUTTON_ACTIONBAR + "<Study"}) - @CommandLineFlags.Add({"force-fieldtrials=Study/Group", - "force-fieldtrial-params=Study.Group:three_button_action_bar/destination_chip_view"}) - public void - testDestinationChipViewMenuItem() throws IOException { - LinearLayout actionBar = (LinearLayout) getListView().getChildAt(0); - Assert.assertEquals(3, actionBar.getChildCount()); - mRenderTestRule.render( - getListView().getChildAt(0), "tinted_rounded_corner_icon_row_three_buttons"); - - int downloadRowIndex = findIndexOfMenuItemById(R.id.downloads_row_menu_id); - Assert.assertNotEquals("No download row found.", -1, downloadRowIndex); - mRenderTestRule.render(getListView().getChildAt(downloadRowIndex), - "download_row_rounded_destination_chip_view"); - - MenuItem bookmarkRow = AppMenuTestSupport.getMenu(mActivityTestRule.getAppMenuCoordinator()) - .findItem(R.id.all_bookmarks_row_menu_id); - MenuItem bookmarkMenuItem = bookmarkRow.getSubMenu().getItem(1); - Assert.assertFalse("Bookmark item should not be checked.", bookmarkMenuItem.isChecked()); - int bookmarkRowIndex = findIndexOfMenuItemById(R.id.all_bookmarks_row_menu_id); - Assert.assertTrue("No bookmark row found.", bookmarkRowIndex != -1); - mRenderTestRule.render(getListView().getChildAt(bookmarkRowIndex), - "bookmark_row_rounded_destination_chip_view"); - - TestThreadUtils.runOnUiThreadBlocking(() -> mAppMenuHandler.hideAppMenu()); - AppMenuPropertiesDelegateImpl.setPageBookmarkedForTesting(true); - showAppMenuAndAssertMenuShown(); - InstrumentationRegistry.getInstrumentation().waitForIdleSync(); - - bookmarkRow = AppMenuTestSupport.getMenu(mActivityTestRule.getAppMenuCoordinator()) - .findItem(R.id.all_bookmarks_row_menu_id); - bookmarkMenuItem = bookmarkRow.getSubMenu().getItem(1); - Assert.assertTrue("Bookmark item should be checked.", bookmarkMenuItem.isChecked()); - mRenderTestRule.render(getListView().getChildAt(bookmarkRowIndex), - "bookmark_row_rounded_destination_chip_view_bookmarked"); - - AppMenuPropertiesDelegateImpl.setPageBookmarkedForTesting(null); - } - - @Test - @SmallTest - @Feature({"Browser", "Main", "RenderTest"}) public void testDividerLineMenuItem() throws IOException { int firstDividerLineIndex = findIndexOfMenuItemById(R.id.divider_line_id); Assert.assertTrue("No divider line found.", firstDividerLineIndex != -1);
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/bookmarks/BookmarkTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/bookmarks/BookmarkTest.java index 45c7657..e22a7dc 100644 --- a/chrome/android/javatests/src/org/chromium/chrome/browser/bookmarks/BookmarkTest.java +++ b/chrome/android/javatests/src/org/chromium/chrome/browser/bookmarks/BookmarkTest.java
@@ -1825,9 +1825,18 @@ LaunchCauseMetrics.LAUNCH_CAUSE_HISTOGRAM)); } + /** + * Test that we record Bookmarks.OpenBookmarkManager.PerProfileType when + * R.id.all_bookmarks_menu_id is clicked in regular mode. + * + * Please note that this test doesn't run for tablet because of the way bookmark manager is + * opened for tablets via openBookmarkManager test method which circumvents the click of + * R.id.all_bookmarks_menu_id, this doesn't happen in actual case and the metric indeed gets + * recorded in tablets. + */ @Test @MediumTest - @DisabledTest(message = "crbug.com/1187193") + @Restriction({UiRestriction.RESTRICTION_TYPE_PHONE}) public void testRecordsHistogramWhenBookmarkManagerOpened_InRegular() throws Throwable { Assert.assertEquals(0, RecordHistogram.getHistogramTotalCountForTesting( @@ -1847,9 +1856,18 @@ BrowserProfileType.REGULAR)); } + /** + * Test that we record Bookmarks.OpenBookmarkManager.PerProfileType when + * R.id.all_bookmarks_menu_id is clicked in Incognito mode. + * + * Please note that this test doesn't run for tablet because of the way bookmark manager is + * opened for tablets via openBookmarkManager test method which circumvents the click of + * R.id.all_bookmarks_menu_id. This doesn't happen in actual case and the metric indeed gets + * recorded in tablets. + */ @Test @MediumTest - @DisabledTest(message = "crbug.com/1187193") + @Restriction({UiRestriction.RESTRICTION_TYPE_PHONE}) public void testRecordsHistogramWhenBookmarkManagerOpened_InIncognito() throws Throwable { Assert.assertEquals(0, RecordHistogram.getHistogramTotalCountForTesting(
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/browserservices/TrustedWebActivityClientTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/browserservices/TrustedWebActivityClientTest.java index 0bf558d..2b1f9dd 100644 --- a/chrome/android/javatests/src/org/chromium/chrome/browser/browserservices/TrustedWebActivityClientTest.java +++ b/chrome/android/javatests/src/org/chromium/chrome/browser/browserservices/TrustedWebActivityClientTest.java
@@ -38,7 +38,7 @@ import org.chromium.chrome.browser.notifications.NotificationUmaTracker; import org.chromium.chrome.browser.notifications.StandardNotificationBuilder; import org.chromium.chrome.test.util.browser.Features; -import org.chromium.chrome.test.util.browser.Features.DisableFeatures; +import org.chromium.chrome.test.util.browser.Features.EnableFeatures; import org.chromium.components.embedder_support.util.Origin; import org.chromium.content_public.browser.UiThreadTaskTraits; @@ -64,7 +64,7 @@ * 4. This sends a Message to ResponseHandler in this class. */ @RunWith(BaseJUnit4ClassRunner.class) -@DisableFeatures(ChromeFeatureList.USE_NOTIFICATION_COMPAT_BUILDER) +@EnableFeatures(ChromeFeatureList.USE_NOTIFICATION_COMPAT_BUILDER) public class TrustedWebActivityClientTest { private static final Uri SCOPE = Uri.parse("https://www.example.com/notifications"); private static final Origin ORIGIN = Origin.create(SCOPE);
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/contextmenu/RevampedContextMenuTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/contextmenu/RevampedContextMenuTest.java index 437bf88..cd307c3c 100644 --- a/chrome/android/javatests/src/org/chromium/chrome/browser/contextmenu/RevampedContextMenuTest.java +++ b/chrome/android/javatests/src/org/chromium/chrome/browser/contextmenu/RevampedContextMenuTest.java
@@ -329,27 +329,6 @@ @Test @MediumTest @Feature({"Browser"}) - @CommandLineFlags.Add({"enable-features=" - + ChromeFeatureList.CONTEXT_MENU_SEARCH_WITH_GOOGLE_LENS + "<FakeStudyName", - "force-fieldtrials=FakeStudyName/Enabled", - "force-fieldtrial-params=FakeStudyName.Enabled:useSearchByImageText/true"}) - public void - testSearchWithGoogleLensWithSearchByImageTextFiresIntent() throws Throwable { - Tab tab = mDownloadTestRule.getActivity().getActivityTab(); - - LensUtils.setFakePassableLensEnvironmentForTesting(true); - ShareHelper.setIgnoreActivityNotFoundExceptionForTesting(true); - hardcodeTestImageForSharing(TEST_JPG_IMAGE_FILE_EXTENSION); - - RevampedContextMenuUtils.selectContextMenuItemWithExpectedIntent( - InstrumentationRegistry.getInstrumentation(), mDownloadTestRule.getActivity(), tab, - "testImage", R.id.contextmenu_search_by_image, - "com.google.android.googlequicksearchbox"); - } - - @Test - @MediumTest - @Feature({"Browser"}) public void testLongPressOnImage() throws TimeoutException { checkOpenImageInNewTab("testImage", "/chrome/test/data/android/contextmenu/test_image.png"); } @@ -940,28 +919,6 @@ @Test @SmallTest @Feature({"Browser", "ContextMenu"}) - @CommandLineFlags.Add({"enable-features=" - + ChromeFeatureList.CONTEXT_MENU_SEARCH_WITH_GOOGLE_LENS + "<FakeStudyName", - "force-fieldtrials=FakeStudyName/Enabled", - "force-fieldtrial-params=FakeStudyName.Enabled:useSearchByImageText/true"}) - public void - testContextMenuRetrievesImageOptionsLensEnabledSearchByImageText() throws TimeoutException { - Tab tab = mDownloadTestRule.getActivity().getActivityTab(); - RevampedContextMenuCoordinator menu = - RevampedContextMenuUtils.openContextMenu(tab, "testImage"); - - Integer[] expectedItems = {R.id.contextmenu_save_image, - R.id.contextmenu_open_image_in_new_tab, R.id.contextmenu_search_by_image, - R.id.contextmenu_share_image, R.id.contextmenu_copy_image}; - Integer[] featureItems = {R.id.contextmenu_open_image_in_ephemeral_tab}; - expectedItems = - addItemsIf(EphemeralTabCoordinator.isSupported(), expectedItems, featureItems); - assertMenuItemsAreEqual(menu, expectedItems); - } - - @Test - @SmallTest - @Feature({"Browser", "ContextMenu"}) @Policies.Add({ @Policies.Item(key = "DefaultSearchProviderEnabled", string = "false") }) public void testContextMenuRetrievesImageOptions_NoDefaultSearchEngine() throws TimeoutException { @@ -1049,35 +1006,6 @@ @Test @SmallTest @Feature({"Browser", "ContextMenu"}) - @CommandLineFlags.Add({"enable-features=" - + ChromeFeatureList.CONTEXT_MENU_SEARCH_WITH_GOOGLE_LENS + "<FakeStudyName", - "force-fieldtrials=FakeStudyName/Enabled", - "force-fieldtrial-params=FakeStudyName.Enabled:useSearchByImageText/true"}) - public void - testContextMenuRetrievesImageLinkOptionsSearchLensEnabledSearchByImageText() - throws TimeoutException { - LensUtils.setFakePassableLensEnvironmentForTesting(true); - - Tab tab = mDownloadTestRule.getActivity().getActivityTab(); - RevampedContextMenuCoordinator menu = - RevampedContextMenuUtils.openContextMenu(tab, "testImageLink"); - - Integer[] expectedItems = {R.id.contextmenu_open_in_new_tab, - R.id.contextmenu_open_in_incognito_tab, R.id.contextmenu_copy_link_address, - R.id.contextmenu_save_link_as, R.id.contextmenu_save_image, - R.id.contextmenu_open_image_in_new_tab, R.id.contextmenu_search_by_image, - R.id.contextmenu_share_image, R.id.contextmenu_share_link, - R.id.contextmenu_copy_image}; - Integer[] featureItems = {R.id.contextmenu_open_in_ephemeral_tab, - R.id.contextmenu_open_image_in_ephemeral_tab}; - expectedItems = - addItemsIf(EphemeralTabCoordinator.isSupported(), expectedItems, featureItems); - assertMenuItemsAreEqual(menu, expectedItems); - } - - @Test - @SmallTest - @Feature({"Browser", "ContextMenu"}) @CommandLineFlags. Add({"enable-features=" + ChromeFeatureList.CONTEXT_MENU_GOOGLE_LENS_CHIP + "<FakeStudyName", "force-fieldtrials=FakeStudyName/Enabled",
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/notifications/CustomNotificationBuilderTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/notifications/CustomNotificationBuilderTest.java index fb20a734..903bf77 100644 --- a/chrome/android/javatests/src/org/chromium/chrome/browser/notifications/CustomNotificationBuilderTest.java +++ b/chrome/android/javatests/src/org/chromium/chrome/browser/notifications/CustomNotificationBuilderTest.java
@@ -39,7 +39,7 @@ import org.chromium.chrome.browser.flags.ChromeFeatureList; import org.chromium.chrome.browser.notifications.channels.ChromeChannelDefinitions; import org.chromium.chrome.test.util.browser.Features; -import org.chromium.chrome.test.util.browser.Features.DisableFeatures; +import org.chromium.chrome.test.util.browser.Features.EnableFeatures; import org.chromium.components.browser_ui.notifications.NotificationMetadata; import org.chromium.components.browser_ui.notifications.PendingIntentProvider; import org.chromium.content_public.browser.test.NativeLibraryTestUtils; @@ -51,7 +51,7 @@ * Instrumentation unit tests for CustomNotificationBuilder. */ @RunWith(BaseJUnit4ClassRunner.class) -@DisableFeatures(ChromeFeatureList.USE_NOTIFICATION_COMPAT_BUILDER) +@EnableFeatures(ChromeFeatureList.USE_NOTIFICATION_COMPAT_BUILDER) public class CustomNotificationBuilderTest { private static final String NOTIFICATION_TAG = "TestNotificationTag"; private static final int NOTIFICATION_ID = 99;
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/notifications/StandardNotificationBuilderTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/notifications/StandardNotificationBuilderTest.java index 19ef0a5..1ba7b2a 100644 --- a/chrome/android/javatests/src/org/chromium/chrome/browser/notifications/StandardNotificationBuilderTest.java +++ b/chrome/android/javatests/src/org/chromium/chrome/browser/notifications/StandardNotificationBuilderTest.java
@@ -35,7 +35,7 @@ import org.chromium.chrome.browser.flags.ChromeFeatureList; import org.chromium.chrome.browser.notifications.channels.ChromeChannelDefinitions; import org.chromium.chrome.test.util.browser.Features; -import org.chromium.chrome.test.util.browser.Features.DisableFeatures; +import org.chromium.chrome.test.util.browser.Features.EnableFeatures; import org.chromium.components.browser_ui.notifications.NotificationMetadata; import org.chromium.components.browser_ui.notifications.PendingIntentProvider; import org.chromium.components.browser_ui.widget.RoundedIconGenerator; @@ -51,7 +51,7 @@ */ @RunWith(BaseJUnit4ClassRunner.class) @Batch(Batch.UNIT_TESTS) -@DisableFeatures(ChromeFeatureList.USE_NOTIFICATION_COMPAT_BUILDER) +@EnableFeatures(ChromeFeatureList.USE_NOTIFICATION_COMPAT_BUILDER) public class StandardNotificationBuilderTest { private static final String NOTIFICATION_TAG = "TestNotificationTag"; private static final int NOTIFICATION_ID = 99; @@ -141,9 +141,17 @@ Assert.assertNotNull( NotificationTestUtil.getLargeIconFromNotification(context, notification)); - Assert.assertEquals(Notification.DEFAULT_ALL, notification.defaults); - Assert.assertEquals(1, notification.vibrate.length); - Assert.assertEquals(100L, notification.vibrate[0]); + // On Android O+ the defaults are ignored as vibrate and silent moved to the notification + // channel. + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O + && NotificationBuilderBase.shouldUseCompat()) { + Assert.assertEquals(0, notification.defaults); + } else { + Assert.assertEquals(Notification.DEFAULT_ALL, notification.defaults); + Assert.assertEquals(1, notification.vibrate.length); + Assert.assertEquals(100L, notification.vibrate[0]); + } + Notification.Action[] actions = NotificationTestUtil.getActions(notification); Assert.assertEquals(3, actions.length); Assert.assertEquals("button 1", NotificationTestUtil.getActionTitle(actions[0]));
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/omnibox/voice/VoiceRecognitionHandlerTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/omnibox/voice/VoiceRecognitionHandlerTest.java index f07b48a..3785cbc 100644 --- a/chrome/android/javatests/src/org/chromium/chrome/browser/omnibox/voice/VoiceRecognitionHandlerTest.java +++ b/chrome/android/javatests/src/org/chromium/chrome/browser/omnibox/voice/VoiceRecognitionHandlerTest.java
@@ -50,6 +50,7 @@ import org.chromium.chrome.browser.omnibox.suggestions.AutocompleteDelegate; import org.chromium.chrome.browser.omnibox.suggestions.OmniboxSuggestionsDropdownEmbedder; import org.chromium.chrome.browser.omnibox.voice.VoiceRecognitionHandler.AssistantActionPerformed; +import org.chromium.chrome.browser.omnibox.voice.VoiceRecognitionHandler.AudioPermissionState; import org.chromium.chrome.browser.omnibox.voice.VoiceRecognitionHandler.TranslateBridgeWrapper; import org.chromium.chrome.browser.omnibox.voice.VoiceRecognitionHandler.VoiceIntentTarget; import org.chromium.chrome.browser.omnibox.voice.VoiceRecognitionHandler.VoiceInteractionSource; @@ -1481,6 +1482,43 @@ @Test @SmallTest + public void testRecordAudioState_deniedCannotAsk() { + mPermissionDelegate.setHasPermission(false); + mPermissionDelegate.setCanRequestPermission(false); + TestThreadUtils.runOnUiThreadBlocking( + () -> { mHandler.startVoiceRecognition(VoiceInteractionSource.OMNIBOX); }); + Assert.assertEquals(1, + RecordHistogram.getHistogramValueCountForTesting( + "VoiceInteraction.AudioPermissionEvent", + AudioPermissionState.DENIED_CANNOT_ASK_AGAIN)); + } + + @Test + @SmallTest + public void testRecordAudioState_deniedCanAsk() { + mPermissionDelegate.setCanRequestPermission(true); + mPermissionDelegate.setPermissionResults(PackageManager.PERMISSION_DENIED); + TestThreadUtils.runOnUiThreadBlocking( + () -> { mHandler.startVoiceRecognition(VoiceInteractionSource.OMNIBOX); }); + Assert.assertEquals(1, + RecordHistogram.getHistogramValueCountForTesting( + "VoiceInteraction.AudioPermissionEvent", + AudioPermissionState.DENIED_CAN_ASK_AGAIN)); + } + + @Test + @SmallTest + public void testRecordAudioState_granted() { + mPermissionDelegate.setHasPermission(true); + TestThreadUtils.runOnUiThreadBlocking( + () -> { mHandler.startVoiceRecognition(VoiceInteractionSource.OMNIBOX); }); + Assert.assertEquals(1, + RecordHistogram.getHistogramValueCountForTesting( + "VoiceInteraction.AudioPermissionEvent", AudioPermissionState.GRANTED)); + } + + @Test + @SmallTest public void testCallback_CalledTwice() { startVoiceRecognition(VoiceInteractionSource.NTP); Assert.assertEquals(-1, mHandler.getVoiceSearchUnexpectedResultSource());
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/read_later/ReadLaterIphTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/read_later/ReadLaterIphTest.java index 72f0e79b..4f11fc0 100644 --- a/chrome/android/javatests/src/org/chromium/chrome/browser/read_later/ReadLaterIphTest.java +++ b/chrome/android/javatests/src/org/chromium/chrome/browser/read_later/ReadLaterIphTest.java
@@ -107,13 +107,8 @@ RevampedContextMenuUtils.selectContextMenuItem(InstrumentationRegistry.getInstrumentation(), activity, tab, CONTEXT_MENU_LINK_DOM_ID, R.id.contextmenu_copy_link_address); - boolean threeButtonActionBarEnabled = ChromeFeatureList.isEnabled( - ChromeFeatureList.TABBED_APP_OVERFLOW_MENU_THREE_BUTTON_ACTIONBAR); - onView(withId(R.id.menu_button_wrapper)) - .check(matches(withHighlight(!threeButtonActionBarEnabled))); - if (!threeButtonActionBarEnabled) { - waitForHelpBubble(withText(R.string.reading_list_save_pages_for_later)); - } + onView(withId(R.id.menu_button_wrapper)).check(matches(withHighlight(true))); + waitForHelpBubble(withText(R.string.reading_list_save_pages_for_later)); } private ViewInteraction waitForHelpBubble(Matcher<View> matcher) {
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/toolbar/adaptive/OptionalNewTabButtonControllerTabletTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/toolbar/adaptive/OptionalNewTabButtonControllerTabletTest.java new file mode 100644 index 0000000..b3e81d4 --- /dev/null +++ b/chrome/android/javatests/src/org/chromium/chrome/browser/toolbar/adaptive/OptionalNewTabButtonControllerTabletTest.java
@@ -0,0 +1,74 @@ +// Copyright 2021 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package org.chromium.chrome.browser.toolbar.adaptive; + +import static androidx.test.espresso.Espresso.onView; +import static androidx.test.espresso.matcher.ViewMatchers.isRoot; +import static androidx.test.espresso.matcher.ViewMatchers.withId; + +import static org.chromium.chrome.test.util.ViewUtils.waitForView; + +import androidx.test.filters.MediumTest; + +import org.junit.Before; +import org.junit.ClassRule; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; + +import org.chromium.base.test.util.Batch; +import org.chromium.base.test.util.CommandLineFlags; +import org.chromium.base.test.util.DisableIf; +import org.chromium.chrome.R; +import org.chromium.chrome.browser.flags.ChromeFeatureList; +import org.chromium.chrome.browser.flags.ChromeSwitches; +import org.chromium.chrome.test.ChromeJUnit4ClassRunner; +import org.chromium.chrome.test.ChromeTabbedActivityTestRule; +import org.chromium.chrome.test.batch.BlankCTATabInitialStateRule; +import org.chromium.chrome.test.util.ViewUtils; +import org.chromium.chrome.test.util.browser.Features.EnableFeatures; +import org.chromium.ui.test.util.UiDisableIf; + +/** + * Tests {@link OptionalNewTabButtonController} on tablet. Phone functionality is tested by {@link + * OptionalNewTabButtonControllerTest}. + */ +@RunWith(ChromeJUnit4ClassRunner.class) +@Batch(Batch.PER_CLASS) +@EnableFeatures({ChromeFeatureList.ADAPTIVE_BUTTON_IN_TOP_TOOLBAR}) +@CommandLineFlags.Add({ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE, + "enable-features=" + ChromeFeatureList.ADAPTIVE_BUTTON_IN_TOP_TOOLBAR + "<Study", + "force-fieldtrials=Study/Group", "force-fieldtrial-params=Study.Group:mode/always-new-tab"}) +@DisableIf.Device(type = {UiDisableIf.PHONE}) +public class OptionalNewTabButtonControllerTabletTest { + private static final String TEST_PAGE = "/chrome/test/data/android/navigate/simple.html"; + + @ClassRule + public static final ChromeTabbedActivityTestRule sActivityTestRule = + new ChromeTabbedActivityTestRule(); + + @Rule + public final BlankCTATabInitialStateRule mInitialStateRule = + new BlankCTATabInitialStateRule(sActivityTestRule, /*clearAllTabState=*/false); + + private String mTestPageUrl; + private String mButtonDescription; + + @Before + public void setUp() { + mTestPageUrl = sActivityTestRule.getTestServer().getURL(TEST_PAGE); + mButtonDescription = + sActivityTestRule.getActivity().getResources().getString(R.string.button_new_tab); + } + + @Test + @MediumTest + public void testButton_hiddenOnTablet() { + sActivityTestRule.loadUrl(mTestPageUrl, /*secondsToWait=*/10); + + onView(isRoot()).check(waitForView( + withId(R.id.optional_toolbar_button), ViewUtils.VIEW_GONE | ViewUtils.VIEW_NULL)); + } +}
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/toolbar/adaptive/OptionalNewTabButtonControllerTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/toolbar/adaptive/OptionalNewTabButtonControllerTest.java index fc529fae..807f83eb 100644 --- a/chrome/android/javatests/src/org/chromium/chrome/browser/toolbar/adaptive/OptionalNewTabButtonControllerTest.java +++ b/chrome/android/javatests/src/org/chromium/chrome/browser/toolbar/adaptive/OptionalNewTabButtonControllerTest.java
@@ -45,14 +45,16 @@ import org.chromium.content_public.browser.test.util.TestThreadUtils; import org.chromium.ui.test.util.UiDisableIf; -/** Tests {@link OptionalNewTabButtonController}. */ +/** + * Tests {@link OptionalNewTabButtonController}. See also {@link + * OptionalNewTabButtonControllerTabletTest}. + */ @RunWith(ChromeJUnit4ClassRunner.class) @Batch(Batch.PER_CLASS) @EnableFeatures({ChromeFeatureList.ADAPTIVE_BUTTON_IN_TOP_TOOLBAR}) @CommandLineFlags.Add({ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE, "enable-features=" + ChromeFeatureList.ADAPTIVE_BUTTON_IN_TOP_TOOLBAR + "<Study", "force-fieldtrials=Study/Group", "force-fieldtrial-params=Study.Group:mode/always-new-tab"}) -// See: https://crbug.com/1187559 @DisableIf.Device(type = {UiDisableIf.TABLET}) public class OptionalNewTabButtonControllerTest { private static final String TEST_PAGE = "/chrome/test/data/android/navigate/simple.html";
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/vr/README.md b/chrome/android/javatests/src/org/chromium/chrome/browser/vr/README.md index 19a5348..5f219d0 100644 --- a/chrome/android/javatests/src/org/chromium/chrome/browser/vr/README.md +++ b/chrome/android/javatests/src/org/chromium/chrome/browser/vr/README.md
@@ -15,7 +15,9 @@ If you are reproducing an issue with the AR tests, run `export DOWNLOAD_VR_TEST_APKS=1 && gclient runhooks` in order to get the -playback datasets that are necessary. +playback datasets that are necessary. This requires authentication, run +`gsutil.py config` [documentation](https://chromium.googlesource.com/chromiumos/docs/+/master/gsutil.md) +to set this up if necessary. **NOTE** The message "Main Unable to find package info for org.chromium.chrome" is usually displayed when the test package is being installed and does
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/vr/WebXrArAnchorsTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/vr/WebXrArAnchorsTest.java index 8cb33e3..131984a 100644 --- a/chrome/android/javatests/src/org/chromium/chrome/browser/vr/WebXrArAnchorsTest.java +++ b/chrome/android/javatests/src/org/chromium/chrome/browser/vr/WebXrArAnchorsTest.java
@@ -23,6 +23,7 @@ import org.chromium.base.test.params.ParameterSet; import org.chromium.base.test.params.ParameterizedRunner; import org.chromium.base.test.util.CommandLineFlags; +import org.chromium.base.test.util.DisabledTest; import org.chromium.base.test.util.MinAndroidSdkLevel; import org.chromium.chrome.browser.flags.ChromeSwitches; import org.chromium.chrome.browser.vr.rules.ArPlaybackFile; @@ -69,6 +70,7 @@ @MediumTest @XrActivityRestriction({XrActivityRestriction.SupportedActivity.ALL}) @ArPlaybackFile("chrome/test/data/xr/ar_playback_datasets/floor_session_12s_30fps.mp4") + @DisabledTest(message = "crbug.com/1188722") public void testHitTestAnchorSucceedsWithPlane() { mWebXrArTestFramework.loadFileAndAwaitInitialization( "webxr_test_basic_anchors_hittest", PAGE_LOAD_TIMEOUT_S); @@ -84,6 +86,7 @@ @MediumTest @XrActivityRestriction({XrActivityRestriction.SupportedActivity.ALL}) @ArPlaybackFile("chrome/test/data/xr/ar_playback_datasets/floor_session_12s_30fps.mp4") + @DisabledTest(message = "crbug.com/1188722") public void testFreeFloatingAnchorSucceeds() { mWebXrArTestFramework.loadFileAndAwaitInitialization( "webxr_test_basic_anchors_freefloating", PAGE_LOAD_TIMEOUT_S); @@ -101,6 +104,7 @@ @XrActivityRestriction({XrActivityRestriction.SupportedActivity.ALL}) @ArPlaybackFile( "chrome/test/data/xr/ar_playback_datasets/floor_session_with_tracking_loss_37s_30fps.mp4") + @DisabledTest(message = "crbug.com/1188722") public void testAnchorStates() { mWebXrArTestFramework.loadFileAndAwaitInitialization(
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/app/appmenu/AppMenuPropertiesDelegateUnitTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/app/appmenu/AppMenuPropertiesDelegateUnitTest.java index 99cfdbd..c9d3979 100644 --- a/chrome/android/junit/src/org/chromium/chrome/browser/app/appmenu/AppMenuPropertiesDelegateUnitTest.java +++ b/chrome/android/junit/src/org/chromium/chrome/browser/app/appmenu/AppMenuPropertiesDelegateUnitTest.java
@@ -37,14 +37,11 @@ import org.chromium.base.test.util.JniMocker; import org.chromium.chrome.R; import org.chromium.chrome.browser.ActivityTabProvider; -import org.chromium.chrome.browser.app.appmenu.AppMenuPropertiesDelegateImpl.AppMenuSimilarSelectionType; import org.chromium.chrome.browser.app.appmenu.AppMenuPropertiesDelegateImpl.MenuGroup; import org.chromium.chrome.browser.bookmarks.BookmarkBridge; import org.chromium.chrome.browser.compositor.layouts.OverviewModeBehavior; import org.chromium.chrome.browser.device.DeviceConditions; import org.chromium.chrome.browser.device.ShadowDeviceConditions; -import org.chromium.chrome.browser.flags.CachedFeatureFlags; -import org.chromium.chrome.browser.flags.ChromeFeatureList; import org.chromium.chrome.browser.multiwindow.MultiWindowModeStateDispatcher; import org.chromium.chrome.browser.omaha.UpdateMenuItemHelper; import org.chromium.chrome.browser.preferences.Pref; @@ -234,10 +231,9 @@ Integer[] expectedItems = {R.id.icon_row_menu_id, R.id.new_tab_menu_id, R.id.new_incognito_tab_menu_id, R.id.divider_line_id, R.id.open_history_menu_id, - R.id.downloads_row_menu_id, R.id.all_bookmarks_row_menu_id, - R.id.recent_tabs_menu_id, R.id.divider_line_id, - R.id.request_desktop_site_row_menu_id, R.id.divider_line_id, R.id.preferences_id, - R.id.help_id}; + R.id.downloads_menu_id, R.id.all_bookmarks_menu_id, R.id.recent_tabs_menu_id, + R.id.divider_line_id, R.id.request_desktop_site_row_menu_id, R.id.divider_line_id, + R.id.preferences_id, R.id.help_id}; assertMenuItemsAreEqual(menu, expectedItems); } @@ -255,15 +251,15 @@ Integer[] expectedItems = {R.id.icon_row_menu_id, R.id.new_tab_menu_id, R.id.new_incognito_tab_menu_id, R.id.divider_line_id, R.id.open_history_menu_id, - R.id.downloads_row_menu_id, R.id.all_bookmarks_row_menu_id, - R.id.recent_tabs_menu_id, R.id.divider_line_id, R.id.share_row_menu_id, - R.id.find_in_page_id, R.id.translate_id, R.id.add_to_homescreen_id, - R.id.request_desktop_site_row_menu_id, R.id.divider_line_id, R.id.preferences_id, - R.id.help_id}; + R.id.downloads_menu_id, R.id.all_bookmarks_menu_id, R.id.recent_tabs_menu_id, + R.id.divider_line_id, R.id.share_row_menu_id, R.id.find_in_page_id, + R.id.translate_id, R.id.add_to_homescreen_id, R.id.request_desktop_site_row_menu_id, + R.id.divider_line_id, R.id.preferences_id, R.id.help_id}; Integer[] expectedTitles = {0, R.string.menu_new_tab, R.string.menu_new_incognito_tab, 0, - R.string.menu_history, 0, 0, R.string.menu_recent_tabs, 0, 0, - R.string.menu_find_in_page, R.string.menu_translate, - R.string.menu_add_to_homescreen, 0, 0, R.string.menu_settings, R.string.menu_help}; + R.string.menu_history, R.string.menu_downloads, R.string.menu_bookmarks, + R.string.menu_recent_tabs, 0, 0, R.string.menu_find_in_page, + R.string.menu_translate, R.string.menu_add_to_homescreen, 0, 0, + R.string.menu_settings, R.string.menu_help}; Integer[] expectedActionBarItems = {R.id.forward_menu_id, R.id.bookmark_this_page_id, R.id.offline_page_id, R.id.info_menu_id, R.id.reload_menu_id}; assertMenuItemsAreEqual(menu, expectedItems); @@ -288,17 +284,17 @@ mAppMenuPropertiesDelegate.prepareMenu(menu, null); Integer[] expectedItems = {R.id.icon_row_menu_id, R.id.new_tab_menu_id, - R.id.new_incognito_tab_menu_id, R.id.divider_line_id, - R.id.all_bookmarks_row_menu_id, R.id.open_history_menu_id, - R.id.downloads_row_menu_id, R.id.recent_tabs_menu_id, R.id.divider_line_id, - R.id.translate_id, R.id.share_row_menu_id, R.id.find_in_page_id, - R.id.add_to_homescreen_id, R.id.request_desktop_site_row_menu_id, - R.id.divider_line_id, R.id.preferences_id, R.id.help_id}; + R.id.new_incognito_tab_menu_id, R.id.divider_line_id, R.id.open_history_menu_id, + R.id.downloads_menu_id, R.id.all_bookmarks_menu_id, R.id.recent_tabs_menu_id, + R.id.divider_line_id, R.id.translate_id, R.id.share_row_menu_id, + R.id.find_in_page_id, R.id.add_to_homescreen_id, + R.id.request_desktop_site_row_menu_id, R.id.divider_line_id, R.id.preferences_id, + R.id.help_id}; Integer[] expectedTitles = {0, R.string.menu_new_tab, R.string.menu_new_incognito_tab, 0, - R.string.menu_history, 0, 0, R.string.menu_recent_tabs, 0, 0, - R.string.menu_find_in_page, R.string.menu_translate, - R.string.menu_add_to_homescreen_install, 0, 0, R.string.menu_settings, - R.string.menu_help}; + R.string.menu_history, R.string.menu_downloads, R.string.menu_bookmarks, + R.string.menu_recent_tabs, 0, 0, R.string.menu_find_in_page, + R.string.menu_translate, R.string.menu_add_to_homescreen_install, 0, 0, + R.string.menu_settings, R.string.menu_help}; Integer[] expectedActionBarItems = {R.id.forward_menu_id, R.id.bookmark_this_page_id, R.id.offline_page_id, R.id.info_menu_id, R.id.reload_menu_id}; assertMenuItemsAreEqual(menu, expectedItems); @@ -321,11 +317,10 @@ Integer[] expectedItems = {R.id.icon_row_menu_id, R.id.new_tab_menu_id, R.id.new_incognito_tab_menu_id, R.id.divider_line_id, R.id.open_history_menu_id, - R.id.downloads_row_menu_id, R.id.all_bookmarks_row_menu_id, - R.id.recent_tabs_menu_id, R.id.divider_line_id, R.id.share_row_menu_id, - R.id.find_in_page_id, R.id.translate_id, R.id.add_to_homescreen_id, - R.id.request_desktop_site_row_menu_id, R.id.divider_line_id, R.id.preferences_id, - R.id.help_id, R.id.managed_by_menu_id}; + R.id.downloads_menu_id, R.id.all_bookmarks_menu_id, R.id.recent_tabs_menu_id, + R.id.divider_line_id, R.id.share_row_menu_id, R.id.find_in_page_id, + R.id.translate_id, R.id.add_to_homescreen_id, R.id.request_desktop_site_row_menu_id, + R.id.divider_line_id, R.id.preferences_id, R.id.help_id, R.id.managed_by_menu_id}; assertMenuItemsAreEqual(menu, expectedItems); } @@ -359,69 +354,15 @@ mAppMenuPropertiesDelegate.prepareMenu(menu, null); Integer[] expectedItems = {R.id.update_menu_id, R.id.new_tab_menu_id, - R.id.new_incognito_tab_menu_id, R.id.recent_tabs_menu_id, R.id.open_history_menu_id, - R.id.translate_id, R.id.find_in_page_id, R.id.add_to_homescreen_id, - R.id.reader_mode_prefs_id, R.id.preferences_id, R.id.help_id}; + R.id.new_incognito_tab_menu_id, R.id.open_history_menu_id, R.id.downloads_menu_id, + R.id.all_bookmarks_menu_id, R.id.recent_tabs_menu_id, R.id.translate_id, + R.id.find_in_page_id, R.id.add_to_homescreen_id, R.id.reader_mode_prefs_id, + R.id.preferences_id, R.id.help_id}; assertMenuItemsHaveIcons(menu, expectedItems); } @Test @Config(qualifiers = "sw320dp") - public void testPageMenuItems_Phone_RegularPage_regroup() { - setUpMocksForPageMenu(); - setMenuOptions(false /*isNativePage*/, true /*showTranslate*/, true /*showUpdate*/, - true /*showMoveToOtherWindow*/, false /*showReaderModePrefs*/, - true /*showAddToHomeScreen*/, true /*showPaintPreview*/); - - Assert.assertEquals(MenuGroup.PAGE_MENU, mAppMenuPropertiesDelegate.getMenuGroup()); - Menu menu = createTestMenu(); - mAppMenuPropertiesDelegate.prepareMenu(menu, null); - - Integer[] expectedItems = {R.id.icon_row_menu_id, R.id.update_menu_id, - R.id.move_to_other_window_menu_id, R.id.new_tab_menu_id, - R.id.new_incognito_tab_menu_id, R.id.divider_line_id, R.id.open_history_menu_id, - R.id.downloads_row_menu_id, R.id.all_bookmarks_row_menu_id, - R.id.recent_tabs_menu_id, R.id.divider_line_id, R.id.share_row_menu_id, - R.id.paint_preview_show_id, R.id.find_in_page_id, R.id.translate_id, - R.id.add_to_homescreen_id, R.id.request_desktop_site_row_menu_id, - R.id.divider_line_id, R.id.preferences_id, R.id.help_id}; - Integer[] expectedActionBarItems = {R.id.forward_menu_id, R.id.bookmark_this_page_id, - R.id.offline_page_id, R.id.info_menu_id, R.id.reload_menu_id}; - assertMenuItemsAreEqual(menu, expectedItems); - assertActionBarItemsAreEqual(menu, expectedActionBarItems); - } - - @Test - @Config(qualifiers = "sw320dp") - public void testPageMenuItems_Phone_RegularPage_threebutton_actionbar() { - CachedFeatureFlags.setForTesting( - ChromeFeatureList.TABBED_APP_OVERFLOW_MENU_THREE_BUTTON_ACTIONBAR, true); - AppMenuPropertiesDelegateImpl.THREE_BUTTON_ACTION_BAR_VARIATION.setForTesting( - "action_chip_view"); - setUpMocksForPageMenu(); - setMenuOptions(false /*isNativePage*/, true /*showTranslate*/, true /*showUpdate*/, - true /*showMoveToOtherWindow*/, false /*showReaderModePrefs*/, - true /*showAddToHomeScreen*/, true /*showPaintPreview*/); - - Assert.assertEquals(MenuGroup.PAGE_MENU, mAppMenuPropertiesDelegate.getMenuGroup()); - Menu menu = createTestMenu(); - mAppMenuPropertiesDelegate.prepareMenu(menu, null); - - Integer[] expectedItems = {R.id.icon_row_menu_id, R.id.update_menu_id, R.id.new_tab_menu_id, - R.id.new_incognito_tab_menu_id, R.id.move_to_other_window_menu_id, - R.id.divider_line_id, R.id.open_history_menu_id, R.id.downloads_row_menu_id, - R.id.all_bookmarks_row_menu_id, R.id.recent_tabs_menu_id, R.id.divider_line_id, - R.id.share_row_menu_id, R.id.paint_preview_show_id, R.id.find_in_page_id, - R.id.translate_id, R.id.add_to_homescreen_id, R.id.request_desktop_site_row_menu_id, - R.id.divider_line_id, R.id.preferences_id, R.id.help_id}; - Integer[] expectedActionBarItems = { - R.id.forward_menu_id, R.id.info_menu_id, R.id.reload_menu_id}; - assertMenuItemsAreEqual(menu, expectedItems); - assertActionBarItemsAreEqual(menu, expectedActionBarItems); - } - - @Test - @Config(qualifiers = "sw320dp") public void testOverviewMenuItems_Phone() { setUpMocksForOverviewMenu(); when(mIncognitoTabModel.getCount()).thenReturn(0); @@ -490,9 +431,9 @@ Integer[] expectedItems = {R.id.icon_row_menu_id, R.id.new_tab_menu_id, R.id.new_incognito_tab_menu_id, R.id.divider_line_id, R.id.open_history_menu_id, - R.id.all_bookmarks_row_menu_id, R.id.downloads_row_menu_id, - R.id.recent_tabs_menu_id, R.id.divider_line_id, R.id.share_row_menu_id, - R.id.get_image_descriptions_id, R.id.find_in_page_id, R.id.add_to_homescreen_id, + R.id.downloads_menu_id, R.id.all_bookmarks_menu_id, R.id.recent_tabs_menu_id, + R.id.divider_line_id, R.id.share_row_menu_id, R.id.get_image_descriptions_id, + R.id.find_in_page_id, R.id.add_to_homescreen_id, R.id.request_desktop_site_row_menu_id, R.id.divider_line_id, R.id.preferences_id, R.id.help_id}; @@ -522,78 +463,6 @@ "Get image descriptions", menu.findItem(R.id.get_image_descriptions_id).getTitle()); } - @Test - public void testMenuItems_AppMenuSimilarSelectionChecker() { - Assert.assertEquals("No match for bookmark page then all bookmarks", - AppMenuSimilarSelectionType.BOOKMARK_PAGE_THEN_ALL_BOOKMARKS, - mAppMenuPropertiesDelegate.findSimilarSelectionPattern( - R.id.bookmark_this_page_id, R.id.all_bookmarks_menu_id)); - Assert.assertEquals("No match for bookmark page then all bookmarks", - AppMenuSimilarSelectionType.BOOKMARK_PAGE_THEN_ALL_BOOKMARKS, - mAppMenuPropertiesDelegate.findSimilarSelectionPattern( - R.id.bookmark_this_page_chip_id, R.id.all_bookmarks_menu_id)); - Assert.assertTrue("Should return true for bookmark page then all bookmarks", - mAppMenuPropertiesDelegate.recordAppMenuSimilarSelectionIfNeeded( - R.id.bookmark_this_page_id, R.id.all_bookmarks_menu_id)); - Assert.assertTrue("Should return true for bookmark page then all bookmarks", - mAppMenuPropertiesDelegate.recordAppMenuSimilarSelectionIfNeeded( - R.id.bookmark_this_page_chip_id, R.id.all_bookmarks_menu_id)); - - Assert.assertEquals("No match for all bookmarks then bookmark page", - AppMenuSimilarSelectionType.ALL_BOOKMARKS_THEN_BOOKMARK_PAGE, - mAppMenuPropertiesDelegate.findSimilarSelectionPattern( - R.id.all_bookmarks_menu_id, R.id.bookmark_this_page_id)); - Assert.assertEquals("No match for all bookmarks then bookmark page", - AppMenuSimilarSelectionType.ALL_BOOKMARKS_THEN_BOOKMARK_PAGE, - mAppMenuPropertiesDelegate.findSimilarSelectionPattern( - R.id.all_bookmarks_menu_id, R.id.bookmark_this_page_chip_id)); - Assert.assertTrue("Should return true for all bookmarks then bookmark page", - mAppMenuPropertiesDelegate.recordAppMenuSimilarSelectionIfNeeded( - R.id.all_bookmarks_menu_id, R.id.bookmark_this_page_id)); - Assert.assertTrue("Should return true for all bookmarks then bookmark page", - mAppMenuPropertiesDelegate.recordAppMenuSimilarSelectionIfNeeded( - R.id.all_bookmarks_menu_id, R.id.bookmark_this_page_chip_id)); - - Assert.assertEquals("No match for download page then all downloads", - AppMenuSimilarSelectionType.DOWNLOAD_PAGE_THEN_ALL_DOWNLOADS, - mAppMenuPropertiesDelegate.findSimilarSelectionPattern( - R.id.offline_page_id, R.id.downloads_menu_id)); - Assert.assertEquals("No match for download page then all downloads", - AppMenuSimilarSelectionType.DOWNLOAD_PAGE_THEN_ALL_DOWNLOADS, - mAppMenuPropertiesDelegate.findSimilarSelectionPattern( - R.id.offline_page_chip_id, R.id.downloads_menu_id)); - Assert.assertTrue("Should return true for download page then all downloads", - mAppMenuPropertiesDelegate.recordAppMenuSimilarSelectionIfNeeded( - R.id.offline_page_id, R.id.downloads_menu_id)); - Assert.assertTrue("Should return true for download page then all downloads", - mAppMenuPropertiesDelegate.recordAppMenuSimilarSelectionIfNeeded( - R.id.offline_page_chip_id, R.id.downloads_menu_id)); - - Assert.assertEquals("No match for all downloads then download page", - AppMenuSimilarSelectionType.ALL_DOWNLOADS_THEN_DOWNLOAD_PAGE, - mAppMenuPropertiesDelegate.findSimilarSelectionPattern( - R.id.downloads_menu_id, R.id.offline_page_id)); - Assert.assertEquals("No match for all downloads then download page", - AppMenuSimilarSelectionType.ALL_DOWNLOADS_THEN_DOWNLOAD_PAGE, - mAppMenuPropertiesDelegate.findSimilarSelectionPattern( - R.id.downloads_menu_id, R.id.offline_page_chip_id)); - Assert.assertTrue("Should return true for all downloads then download page", - mAppMenuPropertiesDelegate.recordAppMenuSimilarSelectionIfNeeded( - R.id.downloads_menu_id, R.id.offline_page_id)); - Assert.assertTrue("Should return true for all downloads then download page", - mAppMenuPropertiesDelegate.recordAppMenuSimilarSelectionIfNeeded( - R.id.downloads_menu_id, R.id.offline_page_chip_id)); - - Assert.assertEquals("Should no match for all downloads then all bookmarks", - AppMenuSimilarSelectionType.NO_MATCH, - mAppMenuPropertiesDelegate.findSimilarSelectionPattern( - R.id.downloads_menu_id, R.id.all_bookmarks_menu_id)); - Assert.assertEquals("Should no match for new tab then find in page", - AppMenuSimilarSelectionType.NO_MATCH, - mAppMenuPropertiesDelegate.findSimilarSelectionPattern( - R.id.new_tab_menu_id, R.id.find_in_page_id)); - } - private void setUpMocksForPageMenu() { when(mActivityTabProvider.get()).thenReturn(mTab); when(mOverviewModeBehavior.overviewVisible()).thenReturn(false);
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/feed/v2/FeedStreamSurfaceTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/feed/v2/FeedStreamSurfaceTest.java index d470d17..2d74f0df 100644 --- a/chrome/android/junit/src/org/chromium/chrome/browser/feed/v2/FeedStreamSurfaceTest.java +++ b/chrome/android/junit/src/org/chromium/chrome/browser/feed/v2/FeedStreamSurfaceTest.java
@@ -88,9 +88,6 @@ } @Override - public void onAddStarting() {} - - @Override public void onAddFinished() {} }
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/tabbed_mode/TabbedAppMenuPropertiesDelegateUnitTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/tabbed_mode/TabbedAppMenuPropertiesDelegateUnitTest.java index dcc345b..db6215c30 100644 --- a/chrome/android/junit/src/org/chromium/chrome/browser/tabbed_mode/TabbedAppMenuPropertiesDelegateUnitTest.java +++ b/chrome/android/junit/src/org/chromium/chrome/browser/tabbed_mode/TabbedAppMenuPropertiesDelegateUnitTest.java
@@ -184,9 +184,9 @@ Integer[] expectedItems = {R.id.icon_row_menu_id, R.id.new_tab_menu_id, R.id.new_incognito_tab_menu_id, R.id.divider_line_id, R.id.open_history_menu_id, - R.id.downloads_row_menu_id, R.id.all_bookmarks_row_menu_id, - R.id.recent_tabs_menu_id, R.id.divider_line_id, R.id.translate_id, - R.id.share_row_menu_id, R.id.find_in_page_id, R.id.add_to_homescreen_id, + R.id.downloads_menu_id, R.id.all_bookmarks_menu_id, R.id.recent_tabs_menu_id, + R.id.divider_line_id, R.id.translate_id, R.id.share_row_menu_id, + R.id.find_in_page_id, R.id.add_to_homescreen_id, R.id.request_desktop_site_row_menu_id, R.id.divider_line_id, R.id.preferences_id, R.id.help_id, R.id.managed_by_menu_id}; assertMenuItemsAreEqual(menu, expectedItems);
diff --git a/chrome/android/profiles/newest.txt b/chrome/android/profiles/newest.txt index 9b161f15..28d8a3a 100644 --- a/chrome/android/profiles/newest.txt +++ b/chrome/android/profiles/newest.txt
@@ -1 +1 @@ -chromeos-chrome-amd64-91.0.4435.0_rc-r1-merged.afdo.bz2 +chromeos-chrome-amd64-91.0.4448.0_rc-r1-merged.afdo.bz2
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn index a7e1d41..2688160 100644 --- a/chrome/browser/BUILD.gn +++ b/chrome/browser/BUILD.gn
@@ -1336,8 +1336,6 @@ "prefs/chrome_pref_service_factory.h", "prefs/incognito_mode_prefs.cc", "prefs/incognito_mode_prefs.h", - "prefs/origin_trial_prefs.cc", - "prefs/origin_trial_prefs.h", "prefs/pref_metrics_service.cc", "prefs/pref_metrics_service.h", "prefs/pref_service_incognito_allowlist.cc", @@ -2035,6 +2033,7 @@ "//components/download/public/background_service:public", "//components/embedder_support", "//components/embedder_support:browser_util", + "//components/embedder_support/origin_trials", "//components/encrypted_messages", "//components/enterprise", "//components/enterprise/common/proto:connectors_proto", @@ -4000,8 +3999,6 @@ # to extensions # section ? "speech/extension_api/tts_extension_api_constants.h", - "speech/on_device_speech_recognizer.cc", - "speech/on_device_speech_recognizer.h", "speech/speech_recognition_client_browser_interface.cc", "speech/speech_recognition_client_browser_interface.h", "speech/speech_recognition_client_browser_interface_factory.cc", @@ -4209,6 +4206,8 @@ "speech/cros_speech_recognition_service.h", "speech/cros_speech_recognition_service_factory.cc", "speech/cros_speech_recognition_service_factory.h", + "speech/on_device_speech_recognizer.cc", + "speech/on_device_speech_recognizer.h", ] deps += [ "//chrome/services/speech:lib" ] @@ -4631,11 +4630,15 @@ "lacros/account_manager_facade_factory_lacros.cc", "lacros/account_manager_util.cc", "lacros/account_manager_util.h", + "lacros/automation_manager_lacros.cc", + "lacros/automation_manager_lacros.h", "lacros/cert_db_initializer.h", "lacros/cert_db_initializer_factory.cc", "lacros/cert_db_initializer_factory.h", "lacros/cert_db_initializer_impl.cc", "lacros/cert_db_initializer_impl.h", + "lacros/chrome_browser_main_extra_parts_lacros.cc", + "lacros/chrome_browser_main_extra_parts_lacros.h", "lacros/client_cert_store_lacros.cc", "lacros/client_cert_store_lacros.h", "lacros/crosapi_pref_observer.cc",
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc index 6f216ba..3299fbbce 100644 --- a/chrome/browser/about_flags.cc +++ b/chrome/browser/about_flags.cc
@@ -2070,22 +2070,6 @@ base::size(kPhotoPickerVideoSupportEnabledWithAnimatedThumbnails), nullptr}}; -const FeatureEntry::FeatureParam - kTabbedAppOverflowMenuThreeButtonActionbarAction[] = { - {"three_button_action_bar", "action_chip_view"}}; -const FeatureEntry::FeatureParam - kTabbedAppOverflowMenuThreeButtonActionbarDestination[] = { - {"three_button_action_bar", "destination_chip_view"}}; -const FeatureEntry::FeatureVariation - kTabbedAppOverflowMenuThreeButtonActionbarVariations[] = { - {"(three button with action chip view)", - kTabbedAppOverflowMenuThreeButtonActionbarAction, - base::size(kTabbedAppOverflowMenuThreeButtonActionbarAction), nullptr}, - {"(three button with destination chip view)", - kTabbedAppOverflowMenuThreeButtonActionbarDestination, - base::size(kTabbedAppOverflowMenuThreeButtonActionbarDestination), - nullptr}}; - // Request Desktop Site on Tablet by default variations. const FeatureEntry::FeatureParam kRequestDesktopSiteForTablets768[] = { {"screen_width_dp", "768"}, @@ -4471,14 +4455,6 @@ #endif // OS_ANDROID #if defined(OS_ANDROID) - {"tabbed-app-overflow-menu-three-button-actionbar", - flag_descriptions::kTabbedAppOverflowMenuThreeButtonActionbarName, - flag_descriptions::kTabbedAppOverflowMenuThreeButtonActionbarDescription, - kOsAndroid, - FEATURE_WITH_PARAMS_VALUE_TYPE( - chrome::android::kTabbedAppOverflowMenuThreeButtonActionbar, - kTabbedAppOverflowMenuThreeButtonActionbarVariations, - "AndroidAppMenuUiReworkPhase4And5")}, {"request-desktop-site-for-tablets", flag_descriptions::kRequestDesktopSiteForTabletsName, flag_descriptions::kRequestDesktopSiteForTabletsDescription, kOsAndroid,
diff --git a/chrome/browser/accessibility/caption_controller_browsertest.cc b/chrome/browser/accessibility/caption_controller_browsertest.cc index 239686a0..c36aaef 100644 --- a/chrome/browser/accessibility/caption_controller_browsertest.cc +++ b/chrome/browser/accessibility/caption_controller_browsertest.cc
@@ -6,6 +6,7 @@ #include "base/files/file_path.h" #include "base/ranges/ranges.h" +#include "base/test/scoped_feature_list.h" #include "build/build_config.h" #include "build/chromeos_buildflags.h" #include "chrome/browser/accessibility/caption_controller_factory.h" @@ -13,8 +14,10 @@ #include "chrome/browser/browser_process.h" #include "chrome/browser/lifetime/application_lifetime.h" #include "chrome/browser/profiles/profile.h" +#include "chrome/browser/profiles/profile_keep_alive_types.h" #include "chrome/browser/profiles/profile_manager.h" #include "chrome/browser/profiles/profile_window.h" +#include "chrome/browser/profiles/scoped_profile_keep_alive.h" #include "chrome/browser/ui/browser.h" #include "chrome/browser/ui/browser_window.h" #include "chrome/browser/ui/caption_bubble_controller.h" @@ -320,6 +323,7 @@ // // TODO(crbug.com/88586): Remove the other branch once // DestroyProfileOnBrowserClose becomes the default. + base::RunLoop().RunUntilIdle(); std::vector<Profile*> loaded_profiles = g_browser_process->profile_manager()->GetLoadedProfiles(); auto it = base::ranges::find(loaded_profiles, profile); @@ -591,6 +595,13 @@ CaptionController* controller1 = GetControllerForProfile(profile1); CaptionController* controller2 = GetControllerForProfile(profile2); + // TODO(crbug.com/88586): Remove this test when the + // DestroyProfileOnBrowserClose flag is removed. + ScopedProfileKeepAlive profile1_keep_alive( + profile1, ProfileKeepAliveOrigin::kBrowserWindow); + ScopedProfileKeepAlive profile2_keep_alive( + profile2, ProfileKeepAliveOrigin::kBrowserWindow); + // Enable live caption on both profiles. SetLiveCaptionEnabled(true); SetLiveCaptionEnabledForProfile(true, profile2);
diff --git a/chrome/browser/android/explore_sites/explore_sites_fetcher.cc b/chrome/browser/android/explore_sites/explore_sites_fetcher.cc index 55c56a9..45b6e6b 100644 --- a/chrome/browser/android/explore_sites/explore_sites_fetcher.cc +++ b/chrome/browser/android/explore_sites/explore_sites_fetcher.cc
@@ -120,7 +120,8 @@ callback_(std::move(callback)), url_loader_factory_(loader_factory) { base::Version version = version_info::GetVersion(); - std::string channel_name = chrome::GetChannelName(); + std::string channel_name = + chrome::GetChannelName(chrome::WithExtendedStable(true)); client_version_ = base::StringPrintf("%d.%d.%d.%s.chrome", version.components()[0], // Major version.components()[2], // Build
diff --git a/chrome/browser/ash/crosapi/automation_ash.cc b/chrome/browser/ash/crosapi/automation_ash.cc new file mode 100644 index 0000000..d393f05 --- /dev/null +++ b/chrome/browser/ash/crosapi/automation_ash.cc
@@ -0,0 +1,132 @@ +// Copyright 2021 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/ash/crosapi/automation_ash.h" + +#include "base/bind.h" +#include "base/pickle.h" +#include "chrome/browser/ash/crosapi/window_util.h" +#include "chrome/common/channel_info.h" +#include "components/exo/shell_surface_base.h" +#include "components/version_info/channel.h" +#include "extensions/browser/api/automation_internal/automation_event_router.h" +#include "extensions/common/extension_messages.h" +#include "ui/accessibility/ax_tree_id_registry.h" +#include "ui/views/widget/widget.h" +#include "ui/views/widget/widget_delegate.h" + +namespace crosapi { + +AutomationAsh::AutomationAsh() { + // TODO(https://crbug.com/1185764): Add a hook to listen for all actions. +} + +AutomationAsh::~AutomationAsh() = default; + +void AutomationAsh::BindReceiver( + mojo::PendingReceiver<mojom::Automation> pending_receiver) { + receivers_.Add(this, std::move(pending_receiver)); +} + +void AutomationAsh::EnableDesktop() { + desktop_enabled_ = true; + for (auto& pair : automation_clients_) { + pair.second->Enable(); + } +} + +void AutomationAsh::EnableTree(const ui::AXTreeID& tree_id) { + if (!tree_id.token().has_value()) + return; + + for (auto& pair : automation_clients_) { + pair.second->EnableTree(tree_id.token().value()); + } +} + +void AutomationAsh::RegisterAutomationClient( + mojo::PendingRemote<mojom::AutomationClient> client, + const base::UnguessableToken& token) { + mojo::Remote<mojom::AutomationClient> remote(std::move(client)); + remote.set_disconnect_handler(base::BindOnce( + &AutomationAsh::ClientDisconnected, weak_factory_.GetWeakPtr(), token)); + automation_clients_[token] = std::move(remote); + + if (desktop_enabled_) { + automation_clients_[token]->Enable(); + } +} + +void AutomationAsh::ReceiveEventPrototype( + const std::string& event_bundle_string, + bool root, + const base::UnguessableToken& token, + const std::string& window_id) { + // This prototype method is only implemented on developer builds of Chrome. We + // check for this by checking that the build of Chrome is unbranded. + if (chrome::GetChannel() != version_info::Channel::UNKNOWN) + return; + + auto it = automation_clients_.find(token); + if (it == automation_clients_.end()) { + LOG(ERROR) << "Received automation event for an unregistered client. " + "Ignoring the event."; + return; + } + + base::Pickle pickle(event_bundle_string.data(), event_bundle_string.size()); + base::PickleIterator iterator(pickle); + ExtensionMsg_AccessibilityEventBundleParams event_bundle; + bool success = + IPC::ParamTraits<ExtensionMsg_AccessibilityEventBundleParams>::Read( + &pickle, &iterator, &event_bundle); + if (!success) { + LOG(ERROR) << "ExtensionMsg_AccessibilityEventBundleParams deserialization " + "failure"; + return; + } + + if (root) { + // TODO(https://crbug.com/1185764): This is fine for prototyping but we'll + // likely want a specific binding for a Lacros AutomationManagerAura to push + // its ax tree id along with the window id, and ax root node id + aura::Window* window = crosapi::GetShellSurfaceWindow(window_id); + if (window) { + views::Widget* widget = views::Widget::GetWidgetForNativeWindow(window); + if (widget) { + static_cast<exo::ShellSurfaceBase*>(widget->widget_delegate()) + ->SetChildAxTreeId(event_bundle.tree_id); + } + } + } + + // TODO(https://crbug.com/1185764): Forward the event to + // AutomationEventRouter. +} + +void AutomationAsh::ForwardActionPrototype( + const ui::AXTreeID& tree_id, + int32_t automation_node_id, + const std::string& action_type, + int32_t request_id, + const base::DictionaryValue& optional_args) { + // This prototype method is only implemented on developer builds of Chrome. We + // check for this by checking that the build of Chrome is unbranded. + if (chrome::GetChannel() != version_info::Channel::UNKNOWN) + return; + + if (!tree_id.token().has_value()) + return; + for (auto& pair : automation_clients_) { + pair.second->PerformActionPrototype(tree_id.token().value(), + automation_node_id, action_type, + request_id, optional_args.Clone()); + } +} + +void AutomationAsh::ClientDisconnected(const base::UnguessableToken& token) { + automation_clients_.erase(token); +} + +} // namespace crosapi
diff --git a/chrome/browser/ash/crosapi/automation_ash.h b/chrome/browser/ash/crosapi/automation_ash.h new file mode 100644 index 0000000..476d8bae --- /dev/null +++ b/chrome/browser/ash/crosapi/automation_ash.h
@@ -0,0 +1,68 @@ +// Copyright 2021 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_ASH_CROSAPI_AUTOMATION_ASH_H_ +#define CHROME_BROWSER_ASH_CROSAPI_AUTOMATION_ASH_H_ + +#include "base/memory/weak_ptr.h" +#include "base/unguessable_token.h" +#include "chromeos/crosapi/mojom/automation.mojom.h" +#include "mojo/public/cpp/bindings/pending_receiver.h" +#include "mojo/public/cpp/bindings/pending_remote.h" +#include "mojo/public/cpp/bindings/receiver_set.h" +#include "ui/accessibility/ax_tree_id.h" + +namespace crosapi { + +// Implements the crosapi interface for automation. Lives in Ash-Chrome on +// the UI thread. +class AutomationAsh : public mojom::Automation { + public: + AutomationAsh(); + AutomationAsh(const AutomationAsh&) = delete; + AutomationAsh& operator=(const AutomationAsh&) = delete; + ~AutomationAsh() override; + + void BindReceiver(mojo::PendingReceiver<mojom::Automation> receiver); + + // Called by ash's internal a11y implementation. Data is forwarded to Lacros. + void EnableDesktop(); + void EnableTree(const ui::AXTreeID& tree_id); + + // crosapi::mojom::Automation: + void RegisterAutomationClient( + mojo::PendingRemote<mojom::AutomationClient> client, + const base::UnguessableToken& token) override; + void ReceiveEventPrototype(const std::string& event_bundle, + bool root, + const base::UnguessableToken& token, + const std::string& window_id) override; + + // Forwards an action to all crosapi clients. This has no effect on production + // builds of chrome. It exists for prototyping for developers. + void ForwardActionPrototype(const ui::AXTreeID& tree_id, + int32_t automation_node_id, + const std::string& action_type, + int32_t request_id, + const base::DictionaryValue& optional_args); + + private: + // Called when an AutomationClient is disconnected. + void ClientDisconnected(const base::UnguessableToken& token); + + bool desktop_enabled_ = false; + + // Any number of crosapi clients can connect to this class. + mojo::ReceiverSet<mojom::Automation> receivers_; + + // This map maintains a list of all known automation clients. + std::map<base::UnguessableToken, mojo::Remote<mojom::AutomationClient>> + automation_clients_; + + base::WeakPtrFactory<AutomationAsh> weak_factory_{this}; +}; + +} // namespace crosapi + +#endif // CHROME_BROWSER_ASH_CROSAPI_AUTOMATION_ASH_H_
diff --git a/chrome/browser/ash/crosapi/browser_util.cc b/chrome/browser/ash/crosapi/browser_util.cc index 6251c44..2678e694 100644 --- a/chrome/browser/ash/crosapi/browser_util.cc +++ b/chrome/browser/ash/crosapi/browser_util.cc
@@ -193,10 +193,11 @@ base::flat_map<base::Token, uint32_t> GetInterfaceVersions() { static_assert( - crosapi::mojom::Crosapi::Version_ == 17, + crosapi::mojom::Crosapi::Version_ == 18, "if you add a new crosapi, please add it to the version map here"); InterfaceVersions versions; AddVersion<chromeos::sensors::mojom::SensorHalClient>(&versions); + AddVersion<crosapi::mojom::Automation>(&versions); AddVersion<crosapi::mojom::AccountManager>(&versions); AddVersion<crosapi::mojom::BrowserServiceHost>(&versions); AddVersion<crosapi::mojom::CertDatabase>(&versions);
diff --git a/chrome/browser/ash/crosapi/crosapi_ash.cc b/chrome/browser/ash/crosapi/crosapi_ash.cc index 0d897ad..c0cdc0c 100644 --- a/chrome/browser/ash/crosapi/crosapi_ash.cc +++ b/chrome/browser/ash/crosapi/crosapi_ash.cc
@@ -11,6 +11,7 @@ #include "ash/components/account_manager/account_manager.h" #include "ash/components/account_manager/account_manager_ash.h" #include "ash/components/account_manager/account_manager_factory.h" +#include "chrome/browser/ash/crosapi/automation_ash.h" #include "chrome/browser/ash/crosapi/browser_manager.h" #include "chrome/browser/ash/crosapi/browser_service_host_ash.h" #include "chrome/browser/ash/crosapi/cert_database_ash.h" @@ -48,7 +49,8 @@ namespace crosapi { CrosapiAsh::CrosapiAsh() - : browser_service_host_ash_(std::make_unique<BrowserServiceHostAsh>()), + : automation_ash_(std::make_unique<AutomationAsh>()), + browser_service_host_ash_(std::make_unique<BrowserServiceHostAsh>()), cert_database_ash_(std::make_unique<CertDatabaseAsh>()), clipboard_ash_(std::make_unique<ClipboardAsh>()), device_attributes_ash_(std::make_unique<DeviceAttributesAsh>()), @@ -87,6 +89,11 @@ disconnect_handler_map_.emplace(id, std::move(disconnect_handler)); } +void CrosapiAsh::BindAutomation( + mojo::PendingReceiver<mojom::Automation> receiver) { + automation_ash_->BindReceiver(std::move(receiver)); +} + void CrosapiAsh::BindAccountManager( mojo::PendingReceiver<mojom::AccountManager> receiver) { // Assumptions:
diff --git a/chrome/browser/ash/crosapi/crosapi_ash.h b/chrome/browser/ash/crosapi/crosapi_ash.h index ac15958..1cccbfc1a 100644 --- a/chrome/browser/ash/crosapi/crosapi_ash.h +++ b/chrome/browser/ash/crosapi/crosapi_ash.h
@@ -17,6 +17,7 @@ namespace crosapi { +class AutomationAsh; class BrowserServiceHostAsh; class CertDatabaseAsh; class ClipboardAsh; @@ -47,6 +48,8 @@ base::OnceClosure disconnect_handler); // crosapi::mojom::Crosapi: + void BindAutomation( + mojo::PendingReceiver<mojom::Automation> receiver) override; void BindAccountManager( mojo::PendingReceiver<mojom::AccountManager> receiver) override; void BindBrowserServiceHost( @@ -100,10 +103,13 @@ return browser_service_host_ash_.get(); } + AutomationAsh* automation_ash() { return automation_ash_.get(); } + private: // Called when a connection is lost. void OnDisconnected(); + std::unique_ptr<AutomationAsh> automation_ash_; std::unique_ptr<BrowserServiceHostAsh> browser_service_host_ash_; std::unique_ptr<CertDatabaseAsh> cert_database_ash_; std::unique_ptr<ClipboardAsh> clipboard_ash_;
diff --git a/chrome/browser/ash/crosapi/message_center_ash.cc b/chrome/browser/ash/crosapi/message_center_ash.cc index beccb8e5..c744b26 100644 --- a/chrome/browser/ash/crosapi/message_center_ash.cc +++ b/chrome/browser/ash/crosapi/message_center_ash.cc
@@ -57,8 +57,13 @@ rich_data.timestamp = notification->timestamp; if (!notification->image.isNull()) rich_data.image = gfx::Image(notification->image); - if (!notification->badge.isNull()) + if (!notification->badge.isNull()) { rich_data.small_image = gfx::Image(notification->badge); + if (notification->badge_needs_additional_masking_has_value) { + rich_data.small_image_needs_additional_masking = + notification->badge_needs_additional_masking; + } + } for (const auto& mojo_item : notification->items) { mc::NotificationItem item; item.title = mojo_item->title;
diff --git a/chrome/browser/ash/login/session/user_session_initializer.cc b/chrome/browser/ash/login/session/user_session_initializer.cc index 87ea1b05..bee81773 100644 --- a/chrome/browser/ash/login/session/user_session_initializer.cc +++ b/chrome/browser/ash/login/session/user_session_initializer.cc
@@ -15,6 +15,7 @@ #include "chrome/browser/ash/plugin_vm/plugin_vm_manager.h" #include "chrome/browser/ash/plugin_vm/plugin_vm_manager_factory.h" #include "chrome/browser/ash/profiles/profile_helper.h" +#include "chrome/browser/ash/settings/cros_settings.h" #include "chrome/browser/browser_process.h" #include "chrome/browser/browser_process_platform_part_chromeos.h" #include "chrome/browser/chromeos/arc/session/arc_service_launcher.h" @@ -38,6 +39,7 @@ #include "chrome/browser/ui/ash/holding_space/holding_space_keyed_service_factory.h" #include "chrome/browser/ui/ash/media_client_impl.h" #include "chrome/common/pref_names.h" +#include "chromeos/dbus/pciguard/pciguard_client.h" #include "chromeos/network/network_cert_loader.h" #include "chromeos/tpm/install_attributes.h" #include "components/prefs/pref_service.h" @@ -229,6 +231,14 @@ plugin_vm_manager->OnPrimaryUserSessionStarted(); VmCameraMicManager::Get()->OnPrimaryUserSessionStarted(primary_profile_); + + bool pcie_tunneling_allowed = false; + CrosSettings::Get()->GetBoolean( + chromeos::kDevicePeripheralDataAccessEnabled, &pcie_tunneling_allowed); + // Pciguard can only be set by non-guest, primary users. By default, + // Pciguard is turned on. + PciguardClient::Get()->SendExternalPciDevicesPermissionState( + pcie_tunneling_allowed); } }
diff --git a/chrome/browser/ash/login/ui/captive_portal_dialog_delegate.cc b/chrome/browser/ash/login/ui/captive_portal_dialog_delegate.cc index 31364de..6299bcce 100644 --- a/chrome/browser/ash/login/ui/captive_portal_dialog_delegate.cc +++ b/chrome/browser/ash/login/ui/captive_portal_dialog_delegate.cc
@@ -10,6 +10,7 @@ #include "chrome/browser/ui/ash/ash_util.h" #include "chrome/browser/ui/webui/chrome_web_contents_handler.h" #include "components/web_modal/web_contents_modal_dialog_manager.h" +#include "content/public/browser/web_contents_observer.h" #include "ui/aura/window.h" #include "ui/display/display.h" #include "ui/display/screen.h" @@ -18,10 +19,35 @@ namespace chromeos { +// Cleans up the delegate for a WebContentsModalDialogManager on destruction, or +// on WebContents destruction, whichever comes first. +class CaptivePortalDialogDelegate::ModalDialogManagerCleanup + : public content::WebContentsObserver { + public: + // This constructor automatically observes |web_contents| for its lifetime. + explicit ModalDialogManagerCleanup(content::WebContents* web_contents) + : content::WebContentsObserver(web_contents) {} + ModalDialogManagerCleanup(const ModalDialogManagerCleanup&) = delete; + ModalDialogManagerCleanup& operator=(const ModalDialogManagerCleanup&) = + delete; + ~ModalDialogManagerCleanup() override { ResetDelegate(); } + + // content::WebContentsObserver: + void WebContentsDestroyed() override { ResetDelegate(); } + + void ResetDelegate() { + if (!web_contents()) + return; + web_modal::WebContentsModalDialogManager::FromWebContents(web_contents()) + ->SetDelegate(nullptr); + } +}; + CaptivePortalDialogDelegate::CaptivePortalDialogDelegate( views::WebDialogView* host_dialog_view) : host_view_(host_dialog_view), web_contents_(host_dialog_view->web_contents()) { + DCHECK(web_contents_); view_ = new views::WebDialogView(ProfileHelper::GetSigninProfile(), this, std::make_unique<ChromeWebContentsHandler>()); @@ -46,10 +72,13 @@ web_modal::WebContentsModalDialogManager::CreateForWebContents(web_contents_); web_modal::WebContentsModalDialogManager::FromWebContents(web_contents_) ->SetDelegate(this); + modal_dialog_manager_cleanup_ = + std::make_unique<ModalDialogManagerCleanup>(web_contents_); } CaptivePortalDialogDelegate::~CaptivePortalDialogDelegate() { - // TODO(jamescook): Clean up modal dialog delegate and observers. + for (auto& observer : modal_dialog_host_observer_list_) + observer.OnHostDestroying(); } void CaptivePortalDialogDelegate::Show() { @@ -133,12 +162,12 @@ void CaptivePortalDialogDelegate::AddObserver( web_modal::ModalDialogHostObserver* observer) { - // TODO(jamescook): Store observers in a list. + modal_dialog_host_observer_list_.AddObserver(observer); } void CaptivePortalDialogDelegate::RemoveObserver( web_modal::ModalDialogHostObserver* observer) { - // TODO(jamescook): Store observers in a list. + modal_dialog_host_observer_list_.RemoveObserver(observer); } base::WeakPtr<CaptivePortalDialogDelegate>
diff --git a/chrome/browser/ash/login/ui/captive_portal_dialog_delegate.h b/chrome/browser/ash/login/ui/captive_portal_dialog_delegate.h index 51c04223..4804c90 100644 --- a/chrome/browser/ash/login/ui/captive_portal_dialog_delegate.h +++ b/chrome/browser/ash/login/ui/captive_portal_dialog_delegate.h
@@ -5,7 +5,10 @@ #ifndef CHROME_BROWSER_ASH_LOGIN_UI_CAPTIVE_PORTAL_DIALOG_DELEGATE_H_ #define CHROME_BROWSER_ASH_LOGIN_UI_CAPTIVE_PORTAL_DIALOG_DELEGATE_H_ +#include <memory> + #include "base/memory/weak_ptr.h" +#include "base/observer_list.h" #include "chrome/browser/ui/chrome_web_modal_dialog_manager_delegate.h" #include "components/web_modal/web_contents_modal_dialog_host.h" #include "ui/web_dialogs/web_dialog_delegate.h" @@ -66,12 +69,20 @@ base::WeakPtr<CaptivePortalDialogDelegate> GetWeakPtr(); + views::Widget* widget_for_test() { return widget_; } + content::WebContents* web_contents_for_test() { return web_contents_; } + private: views::Widget* widget_ = nullptr; views::WebDialogView* view_ = nullptr; views::WebDialogView* host_view_ = nullptr; content::WebContents* web_contents_ = nullptr; + class ModalDialogManagerCleanup; + std::unique_ptr<ModalDialogManagerCleanup> modal_dialog_manager_cleanup_; + base::ObserverList<web_modal::ModalDialogHostObserver>::Unchecked + modal_dialog_host_observer_list_; + base::WeakPtrFactory<CaptivePortalDialogDelegate> weak_ptr_factory_{this}; };
diff --git a/chrome/browser/ash/login/ui/captive_portal_dialog_delegate_browsertest.cc b/chrome/browser/ash/login/ui/captive_portal_dialog_delegate_browsertest.cc new file mode 100644 index 0000000..eb250e1 --- /dev/null +++ b/chrome/browser/ash/login/ui/captive_portal_dialog_delegate_browsertest.cc
@@ -0,0 +1,82 @@ +// Copyright 2021 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/ash/login/ui/captive_portal_dialog_delegate.h" + +#include "chrome/browser/ash/login/login_manager_test.h" +#include "chrome/browser/ash/login/test/login_manager_mixin.h" +#include "chrome/browser/ash/login/ui/login_display_host.h" +#include "chrome/browser/ash/login/ui/login_display_host_mojo.h" +#include "components/constrained_window/constrained_window_views.h" +#include "content/public/test/browser_test.h" +#include "ui/views/accessibility/view_accessibility.h" +#include "ui/views/test/widget_test.h" +#include "ui/views/view.h" +#include "ui/views/widget/widget.h" +#include "ui/views/window/dialog_delegate.h" + +namespace chromeos { +namespace { + +// A simulated modal dialog. Taking focus seems important to repro the crash, +// but I'm not sure why. +class ChildModalDialogDelegate : public views::DialogDelegateView { + public: + ChildModalDialogDelegate() { + // Our views::Widget will delete us. + DCHECK(owned_by_widget()); + SetModalType(ui::MODAL_TYPE_CHILD); + SetFocusBehavior(FocusBehavior::ALWAYS); + // Dialogs that take focus must have a name to pass accessibility checks. + GetViewAccessibility().OverrideName("Test dialog"); + } + ChildModalDialogDelegate(const ChildModalDialogDelegate&) = delete; + ChildModalDialogDelegate& operator=(const ChildModalDialogDelegate&) = delete; + ~ChildModalDialogDelegate() override = default; +}; + +class CaptivePortalDialogDelegateTest : public LoginManagerTest { + public: + // Simulate a login screen with an existing user. + CaptivePortalDialogDelegateTest() { login_mixin_.AppendRegularUsers(1); } + CaptivePortalDialogDelegateTest(const CaptivePortalDialogDelegateTest&) = + delete; + CaptivePortalDialogDelegateTest& operator=( + const CaptivePortalDialogDelegateTest&) = delete; + ~CaptivePortalDialogDelegateTest() override = default; + + LoginManagerMixin login_mixin_{&mixin_host_}; +}; + +// Regression test for use-after-free and crash. https://1170577 +IN_PROC_BROWSER_TEST_F(CaptivePortalDialogDelegateTest, + ShowModalDialogDoesNotCrash) { + // Show the captive portal dialog. + LoginDisplayHostMojo* login_display_host = + static_cast<LoginDisplayHostMojo*>(LoginDisplayHost::default_host()); + OobeUIDialogDelegate* oobe_ui_dialog = login_display_host->dialog_for_test(); + CaptivePortalDialogDelegate* portal_dialog = + oobe_ui_dialog->captive_portal_delegate_for_test(); + views::test::WidgetVisibleWaiter show_waiter( + portal_dialog->widget_for_test()); + portal_dialog->Show(); + show_waiter.Wait(); + + // Show a child modal dialog, similar to an http auth modal dialog. + content::WebContents* web_contents = portal_dialog->web_contents_for_test(); + // The ChildModalDialogDelegate is owned by the views system. + constrained_window::ShowWebModalDialogViews(new ChildModalDialogDelegate, + web_contents); + + // Close the parent dialog. + views::test::WidgetDestroyedWaiter destroyed_waiter( + portal_dialog->widget_for_test()); + portal_dialog->Close(); + destroyed_waiter.Wait(); + + // No crash. +} + +} // namespace +} // namespace chromeos
diff --git a/chrome/browser/ash/login/ui/login_display_host_mojo.h b/chrome/browser/ash/login/ui/login_display_host_mojo.h index 26f927b..a47518b 100644 --- a/chrome/browser/ash/login/ui/login_display_host_mojo.h +++ b/chrome/browser/ash/login/ui/login_display_host_mojo.h
@@ -137,6 +137,8 @@ // messages on the Views and WebUI side. Consider removing. bool IsOobeUIDialogVisible() const; + OobeUIDialogDelegate* dialog_for_test() { return dialog_; } + private: void LoadOobeDialog();
diff --git a/chrome/browser/ash/login/ui/oobe_ui_dialog_delegate.h b/chrome/browser/ash/login/ui/oobe_ui_dialog_delegate.h index 1580ffd..39ed4b9 100644 --- a/chrome/browser/ash/login/ui/oobe_ui_dialog_delegate.h +++ b/chrome/browser/ash/login/ui/oobe_ui_dialog_delegate.h
@@ -84,6 +84,10 @@ views::View* GetWebDialogView(); + CaptivePortalDialogDelegate* captive_portal_delegate_for_test() { + return captive_portal_delegate_.get(); + } + private: // ui::WebDialogDelegate: ui::ModalType GetDialogModalType() const override;
diff --git a/chrome/browser/background/background_contents_service.cc b/chrome/browser/background/background_contents_service.cc index 19eb67e..3b5224e 100644 --- a/chrome/browser/background/background_contents_service.cc +++ b/chrome/browser/background/background_contents_service.cc
@@ -652,7 +652,7 @@ DCHECK(IsTracked(background_contents)); const std::string& appid = GetParentApplicationId(background_contents); DictionaryPrefUpdate update(prefs_, prefs::kRegisteredBackgroundContents); - update.Get()->RemoveWithoutPathExpansion(appid, nullptr); + update.Get()->RemoveKey(appid); } void BackgroundContentsService::ShutdownAssociatedBackgroundContents(
diff --git a/chrome/browser/chrome_content_browser_client.cc b/chrome/browser/chrome_content_browser_client.cc index e266c95..c526332 100644 --- a/chrome/browser/chrome_content_browser_client.cc +++ b/chrome/browser/chrome_content_browser_client.cc
@@ -659,6 +659,7 @@ #if BUILDFLAG(IS_CHROMEOS_LACROS) #include "chrome/browser/chrome_browser_main_parts_lacros.h" +#include "chrome/browser/lacros/chrome_browser_main_extra_parts_lacros.h" #include "chrome/browser/ui/views/chrome_browser_main_extra_parts_views_lacros.h" #include "chromeos/lacros/lacros_chrome_service_impl.h" #include "ui/base/ui_base_switches.h" @@ -1400,6 +1401,10 @@ main_parts->AddParts(std::make_unique<ChromeBrowserMainExtraPartsAsh>()); #endif +#if BUILDFLAG(IS_CHROMEOS_LACROS) + main_parts->AddParts(std::make_unique<ChromeBrowserMainExtraPartsLacros>()); +#endif + #if defined(USE_X11) || defined(USE_OZONE) main_parts->AddParts(std::make_unique<ChromeBrowserMainExtraPartsOzone>()); #endif @@ -5433,9 +5438,8 @@ recv_bytes, sent_bytes); } #if !defined(OS_ANDROID) - task_manager::TaskManagerInterface::GetTaskManager() - ->UpdateAccumulatedStatsNetworkForRoute(process_id, routing_id, - recv_bytes, sent_bytes); + task_manager::TaskManagerInterface::UpdateAccumulatedStatsNetworkForRoute( + process_id, routing_id, recv_bytes, sent_bytes); #endif }
diff --git a/chrome/browser/chromeos/BUILD.gn b/chrome/browser/chromeos/BUILD.gn index b25b1bbe..9765280 100644 --- a/chrome/browser/chromeos/BUILD.gn +++ b/chrome/browser/chromeos/BUILD.gn
@@ -660,6 +660,8 @@ "../ash/certificate_provider/sign_requests.h", "../ash/certificate_provider/thread_safe_certificate_map.cc", "../ash/certificate_provider/thread_safe_certificate_map.h", + "../ash/crosapi/automation_ash.cc", + "../ash/crosapi/automation_ash.h", "../ash/crosapi/browser_loader.cc", "../ash/crosapi/browser_loader.h", "../ash/crosapi/browser_manager.cc",
diff --git a/chrome/browser/chromeos/arc/intent_helper/arc_settings_service.cc b/chrome/browser/chromeos/arc/intent_helper/arc_settings_service.cc index 7684c93..d4614a5 100644 --- a/chrome/browser/chromeos/arc/intent_helper/arc_settings_service.cc +++ b/chrome/browser/chromeos/arc/intent_helper/arc_settings_service.cc
@@ -49,6 +49,7 @@ #include "components/proxy_config/pref_proxy_config_tracker_impl.h" #include "components/proxy_config/proxy_config_dictionary.h" #include "components/proxy_config/proxy_config_pref_names.h" +#include "components/proxy_config/proxy_prefs.h" #include "net/base/url_util.h" #include "net/proxy_resolution/proxy_bypass_rules.h" #include "net/proxy_resolution/proxy_config.h" @@ -94,6 +95,13 @@ return !host->empty() && *port; } +bool IsProxyAutoDetectionConfigured(const base::Value* proxy_config_dict) { + ProxyConfigDictionary dict(proxy_config_dict->Clone()); + ProxyPrefs::ProxyMode mode; + dict.GetMode(&mode); + return mode == ProxyPrefs::MODE_AUTO_DETECT; +} + } // namespace namespace arc { @@ -233,6 +241,9 @@ std::string default_network_name_; // Proxy configuration of the default network. base::Value default_proxy_config_; + // The PAC URL associated with `default_network_name_`, received via the DHCP + // discovery method. + GURL dhcp_wpad_url_; DISALLOW_COPY_AND_ASSIGN(ArcSettingsServiceImpl); }; @@ -303,15 +314,35 @@ } // This function is called when the default network changes or when any of its -// properties change. +// properties change. If the proxy configuration of the default network has +// changed, this method will call `SyncProxySettings` which syncs the proxy +// settings with ARC. Proxy changes on the default network are triggered by: +// - a user changing the proxy in the Network Settings UI; +// - ONC policy changes; +// - DHCP settings the WPAD URL via option 252. void ArcSettingsServiceImpl::DefaultNetworkChanged( const chromeos::NetworkState* network) { - bool sync_proxy = false; - // kProxy pref has more priority than the default network update. - // If a default network is changed to the network with ONC policy with proxy - // settings, it should be translated here. - if (!network || IsPrefProxyConfigApplied()) + if (!network) return; + + bool dhcp_wpad_url_changed = + dhcp_wpad_url_ != network->GetWebProxyAutoDiscoveryUrl(); + dhcp_wpad_url_ = network->GetWebProxyAutoDiscoveryUrl(); + + if (IsPrefProxyConfigApplied()) { + // Normally, we would ignore proxy changes coming from the default + // network because the kProxy pref has priority. If the proxy is + // configured to use the Web Proxy Auto-Discovery (WPAD) Protocol via the + // DHCP discovery method, the PAC URL will be propagated to Chrome via the + // default network properties. + if (dhcp_wpad_url_changed && IsProxyAutoDetectionConfigured(GetPrefs()->Get( + proxy_config::prefs::kProxy))) { + SyncProxySettings(); + } + return; + } + + bool sync_proxy = false; // Trigger a proxy settings sync to ARC if the default network changes. if (default_network_name_ != network->name()) { default_network_name_ = network->name(); @@ -325,6 +356,14 @@ default_proxy_config_ = network->proxy_config().Clone(); sync_proxy = true; } + + // Check if proxy auto detection is enabled. If yes, and the PAC URL set via + // DHCP has changed, propagate the change to ARC. + if (!default_proxy_config_.is_none() && dhcp_wpad_url_changed && + IsProxyAutoDetectionConfigured(&default_proxy_config_)) { + sync_proxy = true; + } + if (!sync_proxy) return; @@ -522,9 +561,16 @@ case ProxyPrefs::MODE_SYSTEM: VLOG(1) << "The system mode is not translated."; return; - case ProxyPrefs::MODE_AUTO_DETECT: - extras.SetString("pacUrl", "http://wpad/wpad.dat"); + case ProxyPrefs::MODE_AUTO_DETECT: { + // WPAD with DHCP has a higher priority than DNS. + if (dhcp_wpad_url_.is_valid()) { + extras.SetString("pacUrl", dhcp_wpad_url_.spec()); + } else { + // Fallback to WPAD via DNS. + extras.SetString("pacUrl", "http://wpad/wpad.dat"); + } break; + } case ProxyPrefs::MODE_PAC_SCRIPT: { std::string pac_url; if (!proxy_config_dict->GetPacUrl(&pac_url)) {
diff --git a/chrome/browser/chromeos/arc/intent_helper/arc_settings_service_browsertest.cc b/chrome/browser/chromeos/arc/intent_helper/arc_settings_service_browsertest.cc index d8b6129b..14d0743 100644 --- a/chrome/browser/chromeos/arc/intent_helper/arc_settings_service_browsertest.cc +++ b/chrome/browser/chromeos/arc/intent_helper/arc_settings_service_browsertest.cc
@@ -21,6 +21,7 @@ #include "chrome/common/pref_names.h" #include "chrome/test/base/in_process_browser_test.h" #include "chromeos/dbus/dbus_thread_manager.h" +#include "chromeos/dbus/shill/shill_ipconfig_client.h" #include "chromeos/dbus/shill/shill_profile_client.h" #include "chromeos/dbus/shill/shill_service_client.h" #include "chromeos/network/network_handler.h" @@ -856,4 +857,72 @@ proxy_sync_count); } +IN_PROC_BROWSER_TEST_F(ArcSettingsServiceTest, WebProxyAutoDiscovery) { + fake_intent_helper_instance_->clear_broadcasts(); + + // Set the proxy config to use auto-discovery. There's no PAC URL set via DHCP + // so the URL "http://wpad/wpad.dat" set via DNS will be propagated to ARC. + base::Value proxy_config_wpad(base::Value::Type::DICTIONARY); + proxy_config_wpad.SetKey("mode", + base::Value(ProxyPrefs::kAutoDetectProxyModeName)); + browser()->profile()->GetPrefs()->Set(proxy_config::prefs::kProxy, + proxy_config_wpad); + + RunUntilIdle(); + const char kWebProxyAutodetectionUrl[] = "www.proxyurl.com:443"; + + chromeos::ShillIPConfigClient::TestInterface* ip_config_client = + chromeos::DBusThreadManager::Get() + ->GetShillIPConfigClient() + ->GetTestInterface(); + + // Set the WPAD DHCP URL. This should now have precedence over the PAC URL set + // via DNS. + base::Value wpad_config(base::Value::Type::DICTIONARY); + wpad_config.SetKey(shill::kWebProxyAutoDiscoveryUrlProperty, + base::Value(kWebProxyAutodetectionUrl)); + const std::string kIPConfigPath = "test_ip_config"; + ip_config_client->AddIPConfig(kIPConfigPath, wpad_config); + + chromeos::ShillServiceClient::TestInterface* service_test = + chromeos::DBusThreadManager::Get() + ->GetShillServiceClient() + ->GetTestInterface(); + + service_test->SetServiceProperty(kDefaultServicePath, + shill::kIPConfigProperty, + base::Value(kIPConfigPath)); + RunUntilIdle(); + + // Remove the proxy. + base::Value proxy_config_direct(base::Value::Type::DICTIONARY); + proxy_config_direct.SetKey("mode", + base::Value(ProxyPrefs::kDirectProxyModeName)); + browser()->profile()->GetPrefs()->Set(proxy_config::prefs::kProxy, + proxy_config_direct); + + RunUntilIdle(); + base::Value expected_proxy_config_wpad_dns(base::Value::Type::DICTIONARY); + expected_proxy_config_wpad_dns.SetKey( + "mode", base::Value(ProxyPrefs::kAutoDetectProxyModeName)); + expected_proxy_config_wpad_dns.SetKey("pacUrl", + base::Value("http://wpad/wpad.dat")); + + base::Value expected_proxy_config_wpad_dhcp(base::Value::Type::DICTIONARY); + expected_proxy_config_wpad_dhcp.SetKey( + "mode", base::Value(ProxyPrefs::kAutoDetectProxyModeName)); + expected_proxy_config_wpad_dhcp.SetKey( + "pacUrl", base::Value(kWebProxyAutodetectionUrl)); + + base::Value expected_proxy_config_direct(base::Value::Type::DICTIONARY); + expected_proxy_config_direct.SetKey( + "mode", base::Value(ProxyPrefs::kDirectProxyModeName)); + + EXPECT_EQ(CountProxyBroadcasts(fake_intent_helper_instance_->broadcasts(), + {&expected_proxy_config_wpad_dns, + &expected_proxy_config_wpad_dhcp, + &expected_proxy_config_direct}), + 3); +} + } // namespace arc
diff --git a/chrome/browser/chromeos/chrome_browser_main_chromeos.cc b/chrome/browser/chromeos/chrome_browser_main_chromeos.cc index fd0ee2f..d0595b9 100644 --- a/chrome/browser/chromeos/chrome_browser_main_chromeos.cc +++ b/chrome/browser/chromeos/chrome_browser_main_chromeos.cc
@@ -161,7 +161,6 @@ #include "chromeos/cryptohome/system_salt_getter.h" #include "chromeos/dbus/constants/cryptohome_key_delegate_constants.h" #include "chromeos/dbus/dbus_thread_manager.h" -#include "chromeos/dbus/pciguard/pciguard_client.h" #include "chromeos/dbus/power/power_manager_client.h" #include "chromeos/dbus/power/power_policy_controller.h" #include "chromeos/dbus/services/cros_dbus_service.h" @@ -1115,14 +1114,6 @@ bool pcie_tunneling_allowed = false; CrosSettings::Get()->GetBoolean(chromeos::kDevicePeripheralDataAccessEnabled, &pcie_tunneling_allowed); - - // External PCI devices are only allowed in non-guest, primary users. - if (!user_manager::UserManager::Get()->IsLoggedInAsGuest() && - ProfileHelper::IsPrimaryProfile(profile())) { - PciguardClient::Get()->SendExternalPciDevicesPermissionState( - pcie_tunneling_allowed); - } - if (chromeos::features::IsPciguardUiEnabled()) { ash::PciePeripheralManager::Initialize( user_manager::UserManager::Get()->IsLoggedInAsGuest(),
diff --git a/chrome/browser/chromeos/platform_keys/extension_platform_keys_service.cc b/chrome/browser/chromeos/platform_keys/extension_platform_keys_service.cc index adac86b7..12fd2e3 100644 --- a/chrome/browser/chromeos/platform_keys/extension_platform_keys_service.cc +++ b/chrome/browser/chromeos/platform_keys/extension_platform_keys_service.cc
@@ -28,7 +28,6 @@ #include "chrome/browser/profiles/profile.h" #include "content/public/browser/browser_thread.h" #include "extensions/browser/extension_registry.h" -#include "extensions/browser/state_store.h" #include "extensions/common/extension.h" #include "extensions/common/features/behavior_feature.h" #include "extensions/common/features/feature.h" @@ -763,11 +762,7 @@ ExtensionPlatformKeysService::SelectDelegate::~SelectDelegate() {} ExtensionPlatformKeysService::ExtensionPlatformKeysService( - bool profile_is_managed, - PrefService* profile_prefs, - policy::PolicyService* profile_policies, - content::BrowserContext* browser_context, - extensions::StateStore* state_store) + content::BrowserContext* browser_context) : browser_context_(browser_context), platform_keys_service_( platform_keys::PlatformKeysServiceFactory::GetForBrowserContext( @@ -777,7 +772,6 @@ GetForBrowserContext(browser_context)) { DCHECK(platform_keys_service_); DCHECK(browser_context); - DCHECK(state_store); } ExtensionPlatformKeysService::~ExtensionPlatformKeysService() {}
diff --git a/chrome/browser/chromeos/platform_keys/extension_platform_keys_service.h b/chrome/browser/chromeos/platform_keys/extension_platform_keys_service.h index 3718c2ce..e9ae589 100644 --- a/chrome/browser/chromeos/platform_keys/extension_platform_keys_service.h +++ b/chrome/browser/chromeos/platform_keys/extension_platform_keys_service.h
@@ -18,26 +18,16 @@ #include "chrome/browser/chromeos/platform_keys/platform_keys_service.h" #include "components/keyed_service/core/keyed_service.h" -class PrefService; - namespace content { class BrowserContext; class WebContents; } // namespace content -namespace extensions { -class StateStore; -} - namespace net { class X509Certificate; typedef std::vector<scoped_refptr<X509Certificate>> CertificateList; } // namespace net -namespace policy { -class PolicyService; -} - namespace chromeos { class ExtensionPlatformKeysService : public KeyedService { @@ -72,17 +62,9 @@ DISALLOW_ASSIGN(SelectDelegate); }; - // Stores registration information in |state_store|, i.e. for each extension - // the list of public keys that are valid to be used for signing. See - // |ExtensionKeyPermissionsService| for more details. - // |browser_context| and |state_store| must not be null and outlive this - // object. + // |browser_context| must not be null and must outlive this object. explicit ExtensionPlatformKeysService( - bool profile_is_managed, - PrefService* profile_prefs, - policy::PolicyService* profile_policies, - content::BrowserContext* browser_context, - extensions::StateStore* state_store); + content::BrowserContext* browser_context); ~ExtensionPlatformKeysService() override;
diff --git a/chrome/browser/chromeos/platform_keys/extension_platform_keys_service_factory.cc b/chrome/browser/chromeos/platform_keys/extension_platform_keys_service_factory.cc index a63e44ee..125e5b4 100644 --- a/chrome/browser/chromeos/platform_keys/extension_platform_keys_service_factory.cc +++ b/chrome/browser/chromeos/platform_keys/extension_platform_keys_service_factory.cc
@@ -17,10 +17,7 @@ #include "chrome/browser/chromeos/platform_keys/extension_platform_keys_service.h" #include "chrome/browser/chromeos/platform_keys/key_permissions/key_permissions_service_factory.h" #include "chrome/browser/chromeos/platform_keys/platform_keys_service_factory.h" -#include "chrome/browser/extensions/extension_system_factory.h" -#include "chrome/browser/policy/profile_policy_connector.h" #include "chrome/browser/profiles/incognito_helpers.h" -#include "chrome/browser/profiles/profile.h" #include "chrome/browser/ui/platform_keys_certificate_selector_chromeos.h" #include "components/keyed_service/content/browser_context_dependency_manager.h" #include "extensions/browser/extension_registry.h" @@ -91,7 +88,6 @@ : BrowserContextKeyedServiceFactory( "ExtensionPlatformKeysService", BrowserContextDependencyManager::GetInstance()) { - DependsOn(extensions::ExtensionSystemFactory::GetInstance()); DependsOn(chromeos::platform_keys::PlatformKeysServiceFactory::GetInstance()); DependsOn( chromeos::platform_keys::KeyPermissionsServiceFactory::GetInstance()); @@ -107,18 +103,8 @@ KeyedService* ExtensionPlatformKeysServiceFactory::BuildServiceInstanceFor( content::BrowserContext* context) const { - extensions::StateStore* const store = - extensions::ExtensionSystem::Get(context)->state_store(); - - policy::ProfilePolicyConnector* const policy_connector = - Profile::FromBrowserContext(context)->GetProfilePolicyConnector(); - - Profile* const profile = Profile::FromBrowserContext(context); - ExtensionPlatformKeysService* const service = - new ExtensionPlatformKeysService( - policy_connector->IsManaged(), profile->GetPrefs(), - policy_connector->policy_service(), context, store); + new ExtensionPlatformKeysService(context); service->SetSelectDelegate(std::make_unique<DefaultSelectDelegate>()); return service;
diff --git a/chrome/browser/component_updater/chrome_origin_trials_component_installer.cc b/chrome/browser/component_updater/chrome_origin_trials_component_installer.cc index 15dfd84..bfcb1c2 100644 --- a/chrome/browser/component_updater/chrome_origin_trials_component_installer.cc +++ b/chrome/browser/component_updater/chrome_origin_trials_component_installer.cc
@@ -4,24 +4,16 @@ #include "chrome/browser/component_updater/chrome_origin_trials_component_installer.h" +#include <cstdint> +#include <utility> +#include <vector> + +#include "base/callback.h" #include "chrome/browser/browser_process.h" -#include "chrome/common/chrome_switches.h" -#include "components/embedder_support/origin_trials/pref_names.h" -#include "components/prefs/pref_service.h" -#include "components/prefs/scoped_user_pref_update.h" +#include "components/embedder_support/origin_trials/component_updater_utils.h" namespace component_updater { -namespace { - -static const char kManifestPublicKeyPath[] = "origin-trials.public-key"; -static const char kManifestDisabledFeaturesPath[] = - "origin-trials.disabled-features"; -static const char kManifestDisabledTokenSignaturesPath[] = - "origin-trials.disabled-tokens.signatures"; - -} // namespace - void ChromeOriginTrialsComponentInstallerPolicy::ComponentReady( const base::Version& version, const base::FilePath& install_dir, @@ -30,36 +22,8 @@ // local_state. These will be used on the next browser restart. // If an individual configuration value is missing, treat as a reset to the // browser defaults. - PrefService* local_state = g_browser_process->local_state(); - std::string override_public_key; - if (manifest->GetString(kManifestPublicKeyPath, &override_public_key)) { - local_state->Set(embedder_support::prefs::kOriginTrialPublicKey, - base::Value(override_public_key)); - } else { - local_state->ClearPref(embedder_support::prefs::kOriginTrialPublicKey); - } - base::ListValue* override_disabled_feature_list = nullptr; - const bool manifest_has_disabled_features = manifest->GetList( - kManifestDisabledFeaturesPath, &override_disabled_feature_list); - if (manifest_has_disabled_features && - !override_disabled_feature_list->empty()) { - ListPrefUpdate update( - local_state, embedder_support::prefs::kOriginTrialDisabledFeatures); - update->Swap(override_disabled_feature_list); - } else { - local_state->ClearPref( - embedder_support::prefs::kOriginTrialDisabledFeatures); - } - base::ListValue* disabled_tokens_list = nullptr; - const bool manifest_has_disabled_tokens = manifest->GetList( - kManifestDisabledTokenSignaturesPath, &disabled_tokens_list); - if (manifest_has_disabled_tokens && !disabled_tokens_list->empty()) { - ListPrefUpdate update(local_state, - embedder_support::prefs::kOriginTrialDisabledTokens); - update->Swap(disabled_tokens_list); - } else { - local_state->ClearPref(embedder_support::prefs::kOriginTrialDisabledTokens); - } + embedder_support::ReadOriginTrialsConfigAndPopulateLocalState( + g_browser_process->local_state(), std::move(manifest)); } void RegisterOriginTrialsComponent(ComponentUpdateService* updater_service) {
diff --git a/chrome/browser/component_updater/origin_trials_component_installer_unittest.cc b/chrome/browser/component_updater/origin_trials_component_installer_unittest.cc index 6ac2bf3..259423b 100644 --- a/chrome/browser/component_updater/origin_trials_component_installer_unittest.cc +++ b/chrome/browser/component_updater/origin_trials_component_installer_unittest.cc
@@ -4,10 +4,8 @@ #include "chrome/browser/component_updater/chrome_origin_trials_component_installer.h" -#include <string> #include <utility> -#include "base/files/file_path.h" #include "base/files/scoped_temp_dir.h" #include "base/values.h" #include "base/version.h" @@ -26,31 +24,8 @@ // will be generated server-side, so any changes need to be intentional and // coordinated. static const char kManifestOriginTrialsKey[] = "origin-trials"; -static const char kManifestPublicKeyPath[] = "origin-trials.public-key"; -static const char kManifestDisabledFeaturesPath[] = - "origin-trials.disabled-features"; -static const char kManifestDisabledTokensPath[] = - "origin-trials.disabled-tokens"; -static const char kManifestDisabledTokenSignaturesPath[] = - "origin-trials.disabled-tokens.signatures"; - static const char kTestUpdateVersion[] = "1.0"; static const char kExistingPublicKey[] = "existing public key"; -static const char kNewPublicKey[] = "new public key"; -static const char kExistingDisabledFeature[] = "already disabled"; -static const std::vector<std::string> kExistingDisabledFeatures = { - kExistingDisabledFeature}; -static const char kNewDisabledFeature1[] = "newly disabled 1"; -static const char kNewDisabledFeature2[] = "newly disabled 2"; -static const std::vector<std::string> kNewDisabledFeatures = { - kNewDisabledFeature1, kNewDisabledFeature2}; -static const char kExistingDisabledToken[] = "already disabled token"; -static const std::vector<std::string> kExistingDisabledTokens = { - kExistingDisabledToken}; -static const char kNewDisabledToken1[] = "newly disabled token 1"; -static const char kNewDisabledToken2[] = "newly disabled token 2"; -static const std::vector<std::string> kNewDisabledTokens = {kNewDisabledToken1, - kNewDisabledToken2}; } // namespace @@ -80,70 +55,6 @@ std::move(manifest)); } - void AddDisabledFeaturesToPrefs(const std::vector<std::string>& features) { - base::ListValue disabled_feature_list; - disabled_feature_list.AppendStrings(features); - ListPrefUpdate update( - local_state(), embedder_support::prefs::kOriginTrialDisabledFeatures); - update->Swap(&disabled_feature_list); - } - - void CheckDisabledFeaturesPrefs(const std::vector<std::string>& features) { - ASSERT_FALSE(features.empty()); - - ASSERT_TRUE(local_state()->HasPrefPath( - embedder_support::prefs::kOriginTrialDisabledFeatures)); - - const base::ListValue* disabled_feature_list = local_state()->GetList( - embedder_support::prefs::kOriginTrialDisabledFeatures); - ASSERT_TRUE(disabled_feature_list); - - ASSERT_EQ(features.size(), disabled_feature_list->GetSize()); - - std::string disabled_feature; - for (size_t i = 0; i < features.size(); ++i) { - const bool found = disabled_feature_list->GetString(i, &disabled_feature); - EXPECT_TRUE(found) << "Entry not found or not a string at index " << i; - if (!found) { - continue; - } - EXPECT_EQ(features[i], disabled_feature) - << "Feature lists differ at index " << i; - } - } - - void AddDisabledTokensToPrefs(const std::vector<std::string>& tokens) { - base::ListValue disabled_token_list; - disabled_token_list.AppendStrings(tokens); - ListPrefUpdate update(local_state(), - embedder_support::prefs::kOriginTrialDisabledTokens); - update->Swap(&disabled_token_list); - } - - void CheckDisabledTokensPrefs(const std::vector<std::string>& tokens) { - ASSERT_FALSE(tokens.empty()); - - ASSERT_TRUE(local_state()->HasPrefPath( - embedder_support::prefs::kOriginTrialDisabledTokens)); - - const base::ListValue* disabled_token_list = local_state()->GetList( - embedder_support::prefs::kOriginTrialDisabledTokens); - ASSERT_TRUE(disabled_token_list); - - ASSERT_EQ(tokens.size(), disabled_token_list->GetSize()); - - std::string disabled_token; - for (size_t i = 0; i < tokens.size(); ++i) { - const bool found = disabled_token_list->GetString(i, &disabled_token); - EXPECT_TRUE(found) << "Entry not found or not a string at index " << i; - if (!found) { - continue; - } - EXPECT_EQ(tokens[i], disabled_token) - << "Token lists differ at index " << i; - } - } - PrefService* local_state() { return g_browser_process->local_state(); } protected: @@ -170,158 +81,4 @@ embedder_support::prefs::kOriginTrialPublicKey)); } -TEST_F(OriginTrialsComponentInstallerTest, PublicKeySetWhenOverrideExists) { - ASSERT_FALSE(local_state()->HasPrefPath( - embedder_support::prefs::kOriginTrialPublicKey)); - - auto manifest = std::make_unique<base::DictionaryValue>(); - manifest->SetString(kManifestPublicKeyPath, kNewPublicKey); - LoadUpdates(std::move(manifest)); - - EXPECT_EQ(kNewPublicKey, local_state()->GetString( - embedder_support::prefs::kOriginTrialPublicKey)); -} - -TEST_F(OriginTrialsComponentInstallerTest, - DisabledFeaturesResetToDefaultWhenListMissing) { - AddDisabledFeaturesToPrefs(kExistingDisabledFeatures); - ASSERT_TRUE(local_state()->HasPrefPath( - embedder_support::prefs::kOriginTrialDisabledFeatures)); - - // Load with empty section in manifest - LoadUpdates(nullptr); - - EXPECT_FALSE(local_state()->HasPrefPath( - embedder_support::prefs::kOriginTrialDisabledFeatures)); -} - -TEST_F(OriginTrialsComponentInstallerTest, - DisabledFeaturesResetToDefaultWhenListEmpty) { - AddDisabledFeaturesToPrefs(kExistingDisabledFeatures); - ASSERT_TRUE(local_state()->HasPrefPath( - embedder_support::prefs::kOriginTrialDisabledFeatures)); - - auto manifest = std::make_unique<base::DictionaryValue>(); - auto disabled_feature_list = std::make_unique<base::ListValue>(); - manifest->Set(kManifestDisabledFeaturesPath, - std::move(disabled_feature_list)); - - LoadUpdates(std::move(manifest)); - - EXPECT_FALSE(local_state()->HasPrefPath( - embedder_support::prefs::kOriginTrialDisabledFeatures)); -} - -TEST_F(OriginTrialsComponentInstallerTest, DisabledFeaturesSetWhenListExists) { - ASSERT_FALSE(local_state()->HasPrefPath( - embedder_support::prefs::kOriginTrialDisabledFeatures)); - - auto manifest = std::make_unique<base::DictionaryValue>(); - auto disabled_feature_list = std::make_unique<base::ListValue>(); - disabled_feature_list->AppendString(kNewDisabledFeature1); - manifest->Set(kManifestDisabledFeaturesPath, - std::move(disabled_feature_list)); - - LoadUpdates(std::move(manifest)); - - std::vector<std::string> features = {kNewDisabledFeature1}; - CheckDisabledFeaturesPrefs(features); -} - -TEST_F(OriginTrialsComponentInstallerTest, - DisabledFeaturesReplacedWhenListExists) { - AddDisabledFeaturesToPrefs(kExistingDisabledFeatures); - ASSERT_TRUE(local_state()->HasPrefPath( - embedder_support::prefs::kOriginTrialDisabledFeatures)); - - auto manifest = std::make_unique<base::DictionaryValue>(); - auto disabled_feature_list = std::make_unique<base::ListValue>(); - disabled_feature_list->AppendStrings(kNewDisabledFeatures); - manifest->Set(kManifestDisabledFeaturesPath, - std::move(disabled_feature_list)); - - LoadUpdates(std::move(manifest)); - - CheckDisabledFeaturesPrefs(kNewDisabledFeatures); -} - -TEST_F(OriginTrialsComponentInstallerTest, - DisabledTokensResetToDefaultWhenListMissing) { - AddDisabledTokensToPrefs(kExistingDisabledTokens); - ASSERT_TRUE(local_state()->HasPrefPath( - embedder_support::prefs::kOriginTrialDisabledTokens)); - - // Load with empty section in manifest - LoadUpdates(nullptr); - - EXPECT_FALSE(local_state()->HasPrefPath( - embedder_support::prefs::kOriginTrialDisabledTokens)); -} - -TEST_F(OriginTrialsComponentInstallerTest, - DisabledTokensResetToDefaultWhenKeyExistsAndListMissing) { - AddDisabledTokensToPrefs(kExistingDisabledTokens); - ASSERT_TRUE(local_state()->HasPrefPath( - embedder_support::prefs::kOriginTrialDisabledTokens)); - - // Load with disabled tokens key in manifest, but no list values - auto manifest = std::make_unique<base::DictionaryValue>(); - manifest->Set(kManifestDisabledTokensPath, std::make_unique<base::Value>()); - - LoadUpdates(std::move(manifest)); - - EXPECT_FALSE(local_state()->HasPrefPath( - embedder_support::prefs::kOriginTrialDisabledTokens)); -} - -TEST_F(OriginTrialsComponentInstallerTest, - DisabledTokensResetToDefaultWhenListEmpty) { - AddDisabledTokensToPrefs(kExistingDisabledTokens); - ASSERT_TRUE(local_state()->HasPrefPath( - embedder_support::prefs::kOriginTrialDisabledTokens)); - - auto manifest = std::make_unique<base::DictionaryValue>(); - auto disabled_token_list = std::make_unique<base::ListValue>(); - manifest->Set(kManifestDisabledTokenSignaturesPath, - std::move(disabled_token_list)); - - LoadUpdates(std::move(manifest)); - - EXPECT_FALSE(local_state()->HasPrefPath( - embedder_support::prefs::kOriginTrialDisabledTokens)); -} - -TEST_F(OriginTrialsComponentInstallerTest, DisabledTokensSetWhenListExists) { - ASSERT_FALSE(local_state()->HasPrefPath( - embedder_support::prefs::kOriginTrialDisabledTokens)); - - auto manifest = std::make_unique<base::DictionaryValue>(); - auto disabled_token_list = std::make_unique<base::ListValue>(); - disabled_token_list->AppendString(kNewDisabledToken1); - manifest->Set(kManifestDisabledTokenSignaturesPath, - std::move(disabled_token_list)); - - LoadUpdates(std::move(manifest)); - - std::vector<std::string> tokens = {kNewDisabledToken1}; - CheckDisabledTokensPrefs(tokens); -} - -TEST_F(OriginTrialsComponentInstallerTest, - DisabledTokensReplacedWhenListExists) { - AddDisabledTokensToPrefs(kExistingDisabledTokens); - ASSERT_TRUE(local_state()->HasPrefPath( - embedder_support::prefs::kOriginTrialDisabledTokens)); - - auto manifest = std::make_unique<base::DictionaryValue>(); - auto disabled_token_list = std::make_unique<base::ListValue>(); - disabled_token_list->AppendStrings(kNewDisabledTokens); - manifest->Set(kManifestDisabledTokenSignaturesPath, - std::move(disabled_token_list)); - - LoadUpdates(std::move(manifest)); - - CheckDisabledTokensPrefs(kNewDisabledTokens); -} - } // namespace component_updater
diff --git a/chrome/browser/extensions/BUILD.gn b/chrome/browser/extensions/BUILD.gn index 12fd069..ca86ca1 100644 --- a/chrome/browser/extensions/BUILD.gn +++ b/chrome/browser/extensions/BUILD.gn
@@ -957,17 +957,11 @@ sources += [ "api/enterprise_device_attributes/enterprise_device_attributes_api.h", "api/enterprise_platform_keys/enterprise_platform_keys_api.h", - "api/messaging/native_message_built_in_host.cc", - "api/messaging/native_message_built_in_host.h", - "api/messaging/native_message_echo_host.cc", - "api/messaging/native_message_echo_host.h", "api/platform_keys/platform_keys_api.h", ] deps += [ "//chromeos/crosapi/cpp", "//chromeos/crosapi/mojom", - "//remoting/host", - "//remoting/host/it2me:chrome_os_host", ] if (is_chromeos_lacros) { sources += [ @@ -975,23 +969,11 @@ "api/enterprise_device_attributes/enterprise_device_attributes_api_lacros.h", "api/enterprise_platform_keys/enterprise_platform_keys_api_lacros.cc", "api/enterprise_platform_keys/enterprise_platform_keys_api_lacros.h", - "api/messaging/native_message_host_lacros.cc", "api/platform_keys/platform_keys_api_lacros.cc", "api/platform_keys/platform_keys_api_lacros.h", ] deps += [ "//chromeos/lacros" ] - } else { - sources += [ "api/messaging/native_message_host_chromeos.cc" ] } - } else { - sources += [ - "api/messaging/native_message_process_host.cc", - "api/messaging/native_message_process_host.h", - "api/messaging/native_messaging_launch_from_native.cc", - "api/messaging/native_messaging_launch_from_native.h", - "api/messaging/native_process_launcher.cc", - "api/messaging/native_process_launcher.h", - ] } if (is_chromeos_ash) { @@ -1024,6 +1006,7 @@ "api/input_ime/input_ime_event_router_base.h", "api/media_perception_private/media_perception_api_delegate_chromeos.cc", "api/media_perception_private/media_perception_api_delegate_chromeos.h", + "api/messaging/native_message_host_chromeos.cc", "api/networking_cast_private/chrome_networking_cast_private_delegate.cc", "api/networking_cast_private/chrome_networking_cast_private_delegate.h", "api/networking_cast_private/networking_cast_private_api.cc", @@ -1111,6 +1094,9 @@ "//components/user_manager", "//media/capture:capture_lib", "//media/capture/video/chromeos/mojom:cros_camera", + "//remoting/base", + "//remoting/host", + "//remoting/host/it2me:chrome_os_host", "//third_party/protobuf:protobuf_lite", "//ui/base/ime/chromeos", "//ui/chromeos", @@ -1139,6 +1125,12 @@ "api/enterprise_reporting_private/keychain_data_helper_mac.h", "api/enterprise_reporting_private/keychain_data_helper_mac.mm", "api/image_writer_private/operation_nonchromeos.cc", + "api/messaging/native_message_process_host.cc", + "api/messaging/native_message_process_host.h", + "api/messaging/native_messaging_launch_from_native.cc", + "api/messaging/native_messaging_launch_from_native.h", + "api/messaging/native_process_launcher.cc", + "api/messaging/native_process_launcher.h", "api/tabs/tabs_util.cc", "chrome_kiosk_delegate.cc", "default_apps.cc",
diff --git a/chrome/browser/extensions/api/DEPS b/chrome/browser/extensions/api/DEPS index e66c2600..88f2d8a 100644 --- a/chrome/browser/extensions/api/DEPS +++ b/chrome/browser/extensions/api/DEPS
@@ -3,6 +3,7 @@ "+services/device/public", # Enable remote assistance on Chrome OS + "+remoting/base", "+remoting/host", ]
diff --git a/chrome/browser/extensions/api/automation_internal/chrome_automation_internal_api_delegate.cc b/chrome/browser/extensions/api/automation_internal/chrome_automation_internal_api_delegate.cc index d75371b..a992b6bb 100644 --- a/chrome/browser/extensions/api/automation_internal/chrome_automation_internal_api_delegate.cc +++ b/chrome/browser/extensions/api/automation_internal/chrome_automation_internal_api_delegate.cc
@@ -26,6 +26,9 @@ #endif #if BUILDFLAG(IS_CHROMEOS_ASH) +#include "chrome/browser/ash/crosapi/automation_ash.h" +#include "chrome/browser/ash/crosapi/crosapi_ash.h" +#include "chrome/browser/ash/crosapi/crosapi_manager.h" #include "chrome/browser/chromeos/arc/accessibility/arc_accessibility_helper_bridge.h" #endif @@ -82,6 +85,13 @@ bool ChromeAutomationInternalApiDelegate::EnableTree( const ui::AXTreeID& tree_id) { #if BUILDFLAG(IS_CHROMEOS_ASH) + // CrosapiManager may not be initialized on unit testing. + // Propagate the EnableTree signal to crosapi clients. + if (crosapi::CrosapiManager::IsInitialized()) { + crosapi::CrosapiManager::Get()->crosapi_ash()->automation_ash()->EnableTree( + tree_id); + } + arc::ArcAccessibilityHelperBridge* bridge = arc::ArcAccessibilityHelperBridge::GetForBrowserContext( GetActiveUserContext()); @@ -92,6 +102,17 @@ } void ChromeAutomationInternalApiDelegate::EnableDesktop() { +#if BUILDFLAG(IS_CHROMEOS_ASH) + // CrosapiManager may not be initialized on unit testing. + // Propagate the EnableDesktop signal to crosapi clients. + if (crosapi::CrosapiManager::IsInitialized()) { + crosapi::CrosapiManager::Get() + ->crosapi_ash() + ->automation_ash() + ->EnableDesktop(); + } +#endif + #if defined(USE_AURA) AutomationManagerAura::GetInstance()->Enable(); #else
diff --git a/chrome/browser/extensions/api/messaging/native_message_built_in_host.cc b/chrome/browser/extensions/api/messaging/native_message_built_in_host.cc deleted file mode 100644 index 97d7e38..0000000 --- a/chrome/browser/extensions/api/messaging/native_message_built_in_host.cc +++ /dev/null
@@ -1,55 +0,0 @@ -// Copyright 2021 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/extensions/api/messaging/native_message_built_in_host.h" - -#include <string> - -#include "content/public/browser/browser_context.h" -#include "extensions/browser/api/messaging/native_message_host.h" -#include "extensions/common/constants.h" -#include "extensions/common/url_pattern.h" -#include "ui/gfx/native_widget_types.h" -#include "url/gurl.h" - -namespace extensions { - -namespace { - -bool MatchesSecurityOrigin(const NativeMessageBuiltInHost& host, - const std::string& extension_id) { - GURL origin(std::string(kExtensionScheme) + "://" + extension_id); - for (size_t i = 0; i < host.allowed_origins_count; i++) { - URLPattern allowed_origin(URLPattern::SCHEME_ALL, host.allowed_origins[i]); - if (allowed_origin.MatchesSecurityOrigin(origin)) { - return true; - } - } - return false; -} - -} // namespace - -std::unique_ptr<NativeMessageHost> NativeMessageHost::Create( - content::BrowserContext* browser_context, - gfx::NativeView native_view, - const std::string& source_extension_id, - const std::string& native_host_name, - bool allow_user_level, - std::string* error) { - for (size_t i = 0; i < kBuiltInHostsCount; i++) { - const auto& host = kBuiltInHosts[i]; - if (host.name == native_host_name) { - if (MatchesSecurityOrigin(host, source_extension_id)) { - return (*host.create_function)(browser_context); - } - *error = kForbiddenError; - return nullptr; - } - } - *error = kNotFoundError; - return nullptr; -} - -} // namespace extensions
diff --git a/chrome/browser/extensions/api/messaging/native_message_built_in_host.h b/chrome/browser/extensions/api/messaging/native_message_built_in_host.h deleted file mode 100644 index 71da8c6..0000000 --- a/chrome/browser/extensions/api/messaging/native_message_built_in_host.h +++ /dev/null
@@ -1,44 +0,0 @@ -// Copyright 2021 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_EXTENSIONS_API_MESSAGING_NATIVE_MESSAGE_BUILT_IN_HOST_H_ -#define CHROME_BROWSER_EXTENSIONS_API_MESSAGING_NATIVE_MESSAGE_BUILT_IN_HOST_H_ - -#include <memory> - -#include <stddef.h> - -namespace content { -class BrowserContext; -} - -namespace extensions { - -class NativeMessageHost; - -struct NativeMessageBuiltInHost { - // Unique name to identify the built-in host. - const char* const name; - - // The extension origins allowed to create the built-in host. - const char* const* const allowed_origins; - - // The count of |allowed_origins|. - size_t allowed_origins_count; - - // The factory function used to create new instances of this host. - std::unique_ptr<NativeMessageHost> (*create_function)( - content::BrowserContext*); -}; - -// The set of built-in hosts that can be instantiated. These are defined in the -// platform-specific impl files. -extern const NativeMessageBuiltInHost kBuiltInHosts[]; - -// The count of built-in hosts defined in |kBuiltInHosts|. -extern const size_t kBuiltInHostsCount; - -} // namespace extensions - -#endif // CHROME_BROWSER_EXTENSIONS_API_MESSAGING_NATIVE_MESSAGE_BUILT_IN_HOST_H_
diff --git a/chrome/browser/extensions/api/messaging/native_message_echo_host.cc b/chrome/browser/extensions/api/messaging/native_message_echo_host.cc deleted file mode 100644 index 93151b21..0000000 --- a/chrome/browser/extensions/api/messaging/native_message_echo_host.cc +++ /dev/null
@@ -1,75 +0,0 @@ -// Copyright 2021 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/extensions/api/messaging/native_message_echo_host.h" - -#include <utility> - -#include "base/json/json_reader.h" -#include "base/json/json_writer.h" -#include "base/optional.h" -#include "base/single_thread_task_runner.h" -#include "base/stl_util.h" -#include "base/threading/thread_task_runner_handle.h" -#include "base/values.h" -#include "content/public/browser/browser_context.h" - -namespace extensions { - -// static -// Must match ScopedTestNativeMessagingHost::kHostName. -const char* const NativeMessageEchoHost::kHostName = - "com.google.chrome.test.echo"; - -// static -// Must match ScopedTestNativeMessagingHost::kExtensionId. -const char* const NativeMessageEchoHost::kOrigins[] = { - "chrome-extension://knldjmfmopnpolahpmmgbagdohdnhkik/"}; - -// static -const int NativeMessageEchoHost::kOriginCount = base::size(kOrigins); - -// static -std::unique_ptr<NativeMessageHost> NativeMessageEchoHost::Create( - content::BrowserContext* browser_context) { - return std::make_unique<NativeMessageEchoHost>(); -} - -NativeMessageEchoHost::NativeMessageEchoHost() = default; -NativeMessageEchoHost::~NativeMessageEchoHost() = default; - -void NativeMessageEchoHost::Start(Client* client) { - client_ = client; -} - -void NativeMessageEchoHost::OnMessage(const std::string& request_string) { - base::Optional<base::Value> request_value = - base::JSONReader::Read(request_string); - if (!request_value.has_value()) { - client_->CloseChannel(kHostInputOutputError); - } else if (request_string.find("stopHostTest") != std::string::npos) { - client_->CloseChannel(kNativeHostExited); - } else if (request_string.find("bigMessageTest") != std::string::npos) { - client_->CloseChannel(kHostInputOutputError); - } else { - ProcessEcho(base::Value::AsDictionaryValue(request_value.value())); - } -} - -scoped_refptr<base::SingleThreadTaskRunner> NativeMessageEchoHost::task_runner() - const { - return base::ThreadTaskRunnerHandle::Get(); -} - -void NativeMessageEchoHost::ProcessEcho(const base::DictionaryValue& request) { - base::DictionaryValue response; - response.SetInteger("id", ++message_number_); - response.Set("echo", request.CreateDeepCopy()); - response.SetString("caller_url", kOrigins[0]); - std::string response_string; - base::JSONWriter::Write(response, &response_string); - client_->PostMessageFromNativeHost(response_string); -} - -} // namespace extensions
diff --git a/chrome/browser/extensions/api/messaging/native_message_echo_host.h b/chrome/browser/extensions/api/messaging/native_message_echo_host.h deleted file mode 100644 index 7c3e3ea2..0000000 --- a/chrome/browser/extensions/api/messaging/native_message_echo_host.h +++ /dev/null
@@ -1,60 +0,0 @@ -// Copyright 2021 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_EXTENSIONS_API_MESSAGING_NATIVE_MESSAGE_ECHO_HOST_H_ -#define CHROME_BROWSER_EXTENSIONS_API_MESSAGING_NATIVE_MESSAGE_ECHO_HOST_H_ - -#include <memory> -#include <string> - -#include "extensions/browser/api/messaging/native_message_host.h" - -namespace base { -class DictionaryValue; -class SingleThreadTaskRunner; -} // namespace base - -namespace { -class BrowserContext; -} - -namespace extensions { - -// A test NativeMessageHost used in ExtensionApiTest::NativeMessagingBasic. -// See //chrome/browser/extensions/api/messaging/native_messaging_apitest.cc -// The behavior in this implementation must match the expectations defined in -// //chrome/test/data/native_messaging/native_hosts/echo.py as that script is -// used to drive the tests. -class NativeMessageEchoHost : public NativeMessageHost { - public: - static const char* const kHostName; - static const char* const kOrigins[]; - static const int kOriginCount; - - static std::unique_ptr<NativeMessageHost> Create( - content::BrowserContext* browser_context); - - NativeMessageEchoHost(); - NativeMessageEchoHost(const NativeMessageEchoHost&) = delete; - NativeMessageEchoHost& operator=(const NativeMessageEchoHost&) = delete; - ~NativeMessageEchoHost() override; - - // NativeMessageHost implementation. - void Start(Client* client) override; - void OnMessage(const std::string& request_string) override; - scoped_refptr<base::SingleThreadTaskRunner> task_runner() const override; - - private: - void ProcessEcho(const base::DictionaryValue& request); - - // Counter used to ensure message uniqueness for testing. - int message_number_ = 0; - - // |client_| must outlive this test instance. - Client* client_ = nullptr; -}; - -} // namespace extensions - -#endif // CHROME_BROWSER_EXTENSIONS_API_MESSAGING_NATIVE_MESSAGE_ECHO_HOST_H_
diff --git a/chrome/browser/extensions/api/messaging/native_message_host_chromeos.cc b/chrome/browser/extensions/api/messaging/native_message_host_chromeos.cc index 712e167..0459cad 100644 --- a/chrome/browser/extensions/api/messaging/native_message_host_chromeos.cc +++ b/chrome/browser/extensions/api/messaging/native_message_host_chromeos.cc
@@ -8,23 +8,93 @@ #include <string> #include <utility> +#include "base/bind.h" +#include "base/callback_helpers.h" +#include "base/json/json_reader.h" +#include "base/json/json_writer.h" +#include "base/location.h" +#include "base/single_thread_task_runner.h" #include "base/stl_util.h" +#include "base/threading/thread_task_runner_handle.h" +#include "base/values.h" #include "chrome/browser/browser_process.h" #include "chrome/browser/chromeos/arc/extensions/arc_support_message_host.h" #include "chrome/browser/chromeos/drive/drivefs_native_message_host.h" #include "chrome/browser/chromeos/wilco_dtc_supportd/wilco_dtc_supportd_messaging.h" -#include "chrome/browser/extensions/api/messaging/native_message_built_in_host.h" -#include "chrome/browser/extensions/api/messaging/native_message_echo_host.h" -#include "content/public/browser/browser_context.h" #include "content/public/browser/browser_task_traits.h" #include "content/public/browser/browser_thread.h" -#include "remoting/host/it2me/it2me_native_messaging_host_allowed_origins.h" +#include "extensions/common/constants.h" +#include "extensions/common/url_pattern.h" #include "remoting/host/it2me/it2me_native_messaging_host_chromeos.h" +#include "ui/gfx/native_widget_types.h" +#include "url/gurl.h" namespace extensions { namespace { +// A simple NativeMessageHost that mimics the implementation of +// chrome/test/data/native_messaging/native_hosts/echo.py. It is currently +// used for testing by ExtensionApiTest::NativeMessagingBasic. + +const char* const kEchoHostOrigins[] = { + // ScopedTestNativeMessagingHost::kExtensionId + "chrome-extension://knldjmfmopnpolahpmmgbagdohdnhkik/"}; + +class EchoHost : public NativeMessageHost { + public: + static std::unique_ptr<NativeMessageHost> Create( + content::BrowserContext* browser_context) { + return std::make_unique<EchoHost>(); + } + + EchoHost() = default; + + void Start(Client* client) override { client_ = client; } + + void OnMessage(const std::string& request_string) override { + std::unique_ptr<base::Value> request_value = + base::JSONReader::ReadDeprecated(request_string); + std::unique_ptr<base::DictionaryValue> request( + static_cast<base::DictionaryValue*>(request_value.release())); + if (request_string.find("stopHostTest") != std::string::npos) { + client_->CloseChannel(kNativeHostExited); + } else if (request_string.find("bigMessageTest") != std::string::npos) { + client_->CloseChannel(kHostInputOutputError); + } else { + ProcessEcho(*request); + } + } + + scoped_refptr<base::SingleThreadTaskRunner> task_runner() const override { + return base::ThreadTaskRunnerHandle::Get(); + } + + private: + void ProcessEcho(const base::DictionaryValue& request) { + base::DictionaryValue response; + response.SetInteger("id", ++message_number_); + response.Set("echo", request.CreateDeepCopy()); + response.SetString("caller_url", kEchoHostOrigins[0]); + std::string response_string; + base::JSONWriter::Write(response, &response_string); + client_->PostMessageFromNativeHost(response_string); + } + + int message_number_ = 0; + Client* client_ = nullptr; + + DISALLOW_COPY_AND_ASSIGN(EchoHost); +}; + +struct BuiltInHost { + const char* const name; + const char* const* const allowed_origins; + int allowed_origins_count; + std::unique_ptr<NativeMessageHost> (*create_function)( + content::BrowserContext*); +}; + std::unique_ptr<NativeMessageHost> CreateIt2MeHost( content::BrowserContext* browser_context) { return remoting::CreateIt2MeNativeMessagingHostForChromeOS( @@ -32,26 +102,67 @@ g_browser_process->policy_service()); } +// If you modify the list of allowed_origins, don't forget to update +// remoting/host/it2me/com.google.chrome.remote_assistance.json.jinja2 +// to keep the two lists in sync. +// TODO(kelvinp): Load the native messaging manifest as a resource file into +// chrome and fetch the list of allowed_origins from the manifest (see +// crbug/424743). +const char* const kRemotingIt2MeOrigins[] = { + "chrome-extension://inomeogfingihgjfjlpeplalcfajhgai/", + "chrome-extension://hpodccmdligbeohchckkeajbfohibipg/"}; + +bool MatchesSecurityOrigin(const BuiltInHost& host, + const std::string& extension_id) { + GURL origin(std::string(kExtensionScheme) + "://" + extension_id); + for (int i = 0; i < host.allowed_origins_count; i++) { + URLPattern allowed_origin(URLPattern::SCHEME_ALL, host.allowed_origins[i]); + if (allowed_origin.MatchesSecurityOrigin(origin)) { + return true; + } + } + return false; +} + } // namespace -const NativeMessageBuiltInHost kBuiltInHosts[] = { - {NativeMessageEchoHost::kHostName, NativeMessageEchoHost::kOrigins, - NativeMessageEchoHost::kOriginCount, &NativeMessageEchoHost::Create}, - {remoting::kIt2MeNativeMessageHostName, remoting::kIt2MeOrigins, - remoting::kIt2MeOriginsSize, &CreateIt2MeHost}, - {arc::ArcSupportMessageHost::kHostName, - arc::ArcSupportMessageHost::kHostOrigin, 1, - &arc::ArcSupportMessageHost::Create}, - {chromeos::kWilcoDtcSupportdUiMessageHost, - chromeos::kWilcoDtcSupportdHostOrigins, - chromeos::kWilcoDtcSupportdHostOriginsSize, - &chromeos::CreateExtensionOwnedWilcoDtcSupportdMessageHost}, - {drive::kDriveFsNativeMessageHostName, - drive::kDriveFsNativeMessageHostOrigins, - drive::kDriveFsNativeMessageHostOriginsSize, - &drive::CreateDriveFsNativeMessageHost}, -}; +std::unique_ptr<NativeMessageHost> NativeMessageHost::Create( + content::BrowserContext* browser_context, + gfx::NativeView native_view, + const std::string& source_extension_id, + const std::string& native_host_name, + bool allow_user_level, + std::string* error) { + static const BuiltInHost kBuiltInHosts[] = { + // ScopedTestNativeMessagingHost::kHostName + {"com.google.chrome.test.echo", kEchoHostOrigins, + base::size(kEchoHostOrigins), &EchoHost::Create}, + {"com.google.chrome.remote_assistance", kRemotingIt2MeOrigins, + base::size(kRemotingIt2MeOrigins), &CreateIt2MeHost}, + {arc::ArcSupportMessageHost::kHostName, + arc::ArcSupportMessageHost::kHostOrigin, 1, + &arc::ArcSupportMessageHost::Create}, + {chromeos::kWilcoDtcSupportdUiMessageHost, + chromeos::kWilcoDtcSupportdHostOrigins, + chromeos::kWilcoDtcSupportdHostOriginsSize, + &chromeos::CreateExtensionOwnedWilcoDtcSupportdMessageHost}, + {drive::kDriveFsNativeMessageHostName, + drive::kDriveFsNativeMessageHostOrigins, + drive::kDriveFsNativeMessageHostOriginsSize, + &drive::CreateDriveFsNativeMessageHost}, + }; -const size_t kBuiltInHostsCount = base::size(kBuiltInHosts); + for (const BuiltInHost& host : kBuiltInHosts) { + if (host.name == native_host_name) { + if (MatchesSecurityOrigin(host, source_extension_id)) { + return (*host.create_function)(browser_context); + } + *error = kForbiddenError; + return nullptr; + } + } + *error = kNotFoundError; + return nullptr; +} } // namespace extensions
diff --git a/chrome/browser/extensions/api/messaging/native_message_host_lacros.cc b/chrome/browser/extensions/api/messaging/native_message_host_lacros.cc deleted file mode 100644 index a2b5097..0000000 --- a/chrome/browser/extensions/api/messaging/native_message_host_lacros.cc +++ /dev/null
@@ -1,40 +0,0 @@ -// Copyright 2021 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 "extensions/browser/api/messaging/native_message_host.h" - -#include <memory> -#include <string> - -#include "base/stl_util.h" -#include "chrome/browser/extensions/api/messaging/native_message_built_in_host.h" -#include "chrome/browser/extensions/api/messaging/native_message_echo_host.h" -#include "content/public/browser/browser_context.h" -#include "content/public/browser/browser_task_traits.h" -#include "content/public/browser/browser_thread.h" -#include "remoting/host/it2me/it2me_native_messaging_host_allowed_origins.h" -#include "remoting/host/it2me/it2me_native_messaging_host_lacros.h" - -namespace extensions { - -namespace { - -std::unique_ptr<NativeMessageHost> CreateIt2MeHost( - content::BrowserContext* browser_context) { - return remoting::CreateIt2MeNativeMessagingHostForLacros( - content::GetIOThreadTaskRunner({}), content::GetUIThreadTaskRunner({})); -} - -} // namespace - -const NativeMessageBuiltInHost kBuiltInHosts[] = { - {NativeMessageEchoHost::kHostName, NativeMessageEchoHost::kOrigins, - NativeMessageEchoHost::kOriginCount, &NativeMessageEchoHost::Create}, - {remoting::kIt2MeNativeMessageHostName, remoting::kIt2MeOrigins, - remoting::kIt2MeOriginsSize, &CreateIt2MeHost}, -}; - -const size_t kBuiltInHostsCount = base::size(kBuiltInHosts); - -} // namespace extensions
diff --git a/chrome/browser/extensions/api/messaging/native_messaging_apitest.cc b/chrome/browser/extensions/api/messaging/native_messaging_apitest.cc index 8134e92..d40535f 100644 --- a/chrome/browser/extensions/api/messaging/native_messaging_apitest.cc +++ b/chrome/browser/extensions/api/messaging/native_messaging_apitest.cc
@@ -83,7 +83,7 @@ ASSERT_TRUE(RunTest("native_messaging_connect")) << message_; } -#if !BUILDFLAG(IS_CHROMEOS_ASH) && !BUILDFLAG(IS_CHROMEOS_LACROS) +#if !BUILDFLAG(IS_CHROMEOS_ASH) class TestProcessManagerObserver : public ProcessManagerObserver { public: @@ -437,7 +437,7 @@ ASSERT_NO_FATAL_FAILURE(TestKeepAliveStateObserver().WaitForNoKeepAlive()); } -#endif // !BUILDFLAG(IS_CHROMEOS_ASH) && !BUILDFLAG(IS_CHROMEOS_LACROS) +#endif // !BUILDFLAG(IS_CHROMEOS_ASH) } // namespace } // namespace extensions
diff --git a/chrome/browser/extensions/api/notifications/extension_notification_handler.cc b/chrome/browser/extensions/api/notifications/extension_notification_handler.cc index 90e763b4..f4f4142 100644 --- a/chrome/browser/extensions/api/notifications/extension_notification_handler.cc +++ b/chrome/browser/extensions/api/notifications/extension_notification_handler.cc
@@ -7,7 +7,6 @@ #include "base/callback.h" #include "base/check_op.h" #include "base/metrics/histogram_macros.h" -#include "base/strings/nullable_string16.h" #include "base/strings/string_piece.h" #include "chrome/browser/extensions/api/notifications/extension_notification_display_helper.h" #include "chrome/browser/extensions/api/notifications/extension_notification_display_helper_factory.h"
diff --git a/chrome/browser/extensions/api/notifications/notifications_api.cc b/chrome/browser/extensions/api/notifications/notifications_api.cc index a8b745f..e21637a 100644 --- a/chrome/browser/extensions/api/notifications/notifications_api.cc +++ b/chrome/browser/extensions/api/notifications/notifications_api.cc
@@ -104,33 +104,6 @@ return scoped_id.substr(index_of_separator); } -const gfx::ImageSkia CreateSolidColorImage(int width, - int height, - SkColor color) { - SkBitmap bitmap; - bitmap.allocN32Pixels(width, height); - bitmap.eraseColor(color); - return gfx::ImageSkia::CreateFrom1xBitmap(bitmap); -} - -// Take the alpha channel of small_image, mask it with the foreground, -// then add the masked foreground on top of the background -const gfx::Image GetMaskedSmallImage(const gfx::ImageSkia& small_image) { - int width = small_image.width(); - int height = small_image.height(); - - // Background color grey - const gfx::ImageSkia background = CreateSolidColorImage( - width, height, message_center::kSmallImageMaskBackgroundColor); - // Foreground color white - const gfx::ImageSkia foreground = CreateSolidColorImage( - width, height, message_center::kSmallImageMaskForegroundColor); - const gfx::ImageSkia masked_small_image = - gfx::ImageSkiaOperations::CreateMaskedImage(foreground, small_image); - return gfx::Image(gfx::ImageSkiaOperations::CreateSuperimposedImage( - background, masked_small_image)); -} - // Converts the |notification_bitmap| (in RGBA format) to the |*return_image| // (which is in ARGB format). bool NotificationBitmapToGfxImage( @@ -275,8 +248,8 @@ *error = kUnableToDecodeIconError; return false; } - optional_fields.small_image = - GetMaskedSmallImage(small_icon_mask.AsImageSkia()); + optional_fields.small_image = small_icon_mask; + optional_fields.small_image_needs_additional_masking = true; } if (options->priority.get()) @@ -430,8 +403,8 @@ *error = kUnableToDecodeIconError; return false; } - notification->set_small_image( - GetMaskedSmallImage(app_icon_mask.AsImageSkia())); + notification->set_small_image(app_icon_mask); + notification->set_small_image_needs_additional_masking(true); } if (options->priority)
diff --git a/chrome/browser/extensions/api/notifications/notifications_apitest.cc b/chrome/browser/extensions/api/notifications/notifications_apitest.cc index 523d702..32a5223 100644 --- a/chrome/browser/extensions/api/notifications/notifications_apitest.cc +++ b/chrome/browser/extensions/api/notifications/notifications_apitest.cc
@@ -556,3 +556,18 @@ notification->fullscreen_visibility()); } #endif // !defined(OS_MAC) + +IN_PROC_BROWSER_TEST_F(NotificationsApiTest, TestSmallImage) { + ExtensionTestMessageListener notification_created_listener("created", false); + const Extension* extension = LoadAppWithWindowState( + "notifications/api/basic_app", WindowState::NORMAL); + ASSERT_TRUE(extension) << message_; + ASSERT_TRUE(notification_created_listener.WaitUntilSatisfied()); + + message_center::Notification* notification = + GetNotificationForExtension(extension); + ASSERT_TRUE(notification); + + EXPECT_FALSE(notification->small_image().IsEmpty()); + EXPECT_TRUE(notification->small_image_needs_additional_masking()); +}
diff --git a/chrome/browser/extensions/extension_allowlist.cc b/chrome/browser/extensions/extension_allowlist.cc index fdb9b5b6..e8a3ace8 100644 --- a/chrome/browser/extensions/extension_allowlist.cc +++ b/chrome/browser/extensions/extension_allowlist.cc
@@ -20,7 +20,11 @@ : profile_(profile), extension_prefs_(extension_prefs), extension_service_(extension_service), - registry_(ExtensionRegistry::Get(profile)) {} + registry_(ExtensionRegistry::Get(profile)) { + extension_prefs_observation_.Observe(extension_prefs); +} + +ExtensionAllowlist::~ExtensionAllowlist() = default; void ExtensionAllowlist::Init() { SetAllowlistEnforcedField(); @@ -55,33 +59,46 @@ return; } - bool allowlisted = allowlist_value->GetBool(); + AllowlistState allowlist_state = allowlist_value->GetBool() + ? ALLOWLIST_ALLOWLISTED + : ALLOWLIST_NOT_ALLOWLISTED; + + if (allowlist_state == + extension_prefs_->GetExtensionAllowlistState(extension_id)) { + // Do nothing if the state didn't change. + return; + } // TODO(jeffcyr): Add an observer when allowlist state change, otherwise the // 'chrome://extensions' warning may not be refreshed. // Set the allowlist state even if there is no enforcement. This will allow // immediate enforcement when it is activated. - extension_prefs_->SetExtensionAllowlistState( - extension_id, - allowlisted ? ALLOWLIST_ALLOWLISTED : ALLOWLIST_NOT_ALLOWLISTED); + extension_prefs_->SetExtensionAllowlistState(extension_id, allowlist_state); - if (!is_allowlist_enforced_) { - DCHECK(!extension_prefs_->HasDisableReason( - extension_id, disable_reason::DISABLE_NOT_ALLOWLISTED)); - return; - } + if (allowlist_state == ALLOWLIST_ALLOWLISTED) { + // The extension is now allowlisted, remove the disable reason if present + // and ask for a user acknowledge if the extension was re-enabled in the + // process. - if (allowlisted) { + if (!extension_prefs_->HasDisableReason( + extension_id, disable_reason::DISABLE_NOT_ALLOWLISTED)) { + // Nothing to do if the extension was not already disabled by allowlist + // enforcement. + return; + } + extension_service_->RemoveDisableReasonAndMaybeEnable( extension_id, disable_reason::DISABLE_NOT_ALLOWLISTED); - } else { - // TODO(jeffcyr): User re-enable is broken, if the user manually re-enabled - // the extension, it is currently disabled again on the next update check or - // restart. This will be fixed before launch. - extension_service_->DisableExtension( - extension_id, disable_reason::DISABLE_NOT_ALLOWLISTED); + if (registry_->enabled_extensions().Contains(extension_id)) { + // Inform the user if the extension is now enabled. + extension_prefs_->SetExtensionAllowlistAcknowledgeState( + extension_id, ALLOWLIST_ACKNOWLEDGE_NEEDED); + } + } else if (is_allowlist_enforced_) { + // The extension is no longer allowlisted, try to apply enforcement. + ApplyEnforcement(extension_id); } } @@ -99,6 +116,44 @@ safe_browsing::IsEnhancedProtectionEnabled(*profile_->GetPrefs()); } +// `ApplyEnforcement` can be called when an extension becomes not allowlisted or +// when the allowlist enforcement is activated (for already not allowlisted +// extensions). +void ExtensionAllowlist::ApplyEnforcement(const std::string& extension_id) { + DCHECK(is_allowlist_enforced_); + DCHECK_EQ(extension_prefs_->GetExtensionAllowlistState(extension_id), + ALLOWLIST_NOT_ALLOWLISTED); + + // Early exit if the enforcement is already done. + if (extension_prefs_->HasDisableReason( + extension_id, disable_reason::DISABLE_NOT_ALLOWLISTED)) { + return; + } + + // Do not re-enforce if the extension was explicitly enabled by the user. + if (extension_prefs_->GetExtensionAllowlistAcknowledgeState(extension_id) == + ALLOWLIST_ACKNOWLEDGE_ENABLED_BY_USER) { + return; + } + + bool was_enabled = registry_->enabled_extensions().Contains(extension_id); + extension_service_->DisableExtension(extension_id, + disable_reason::DISABLE_NOT_ALLOWLISTED); + + // The user should acknowledge the disable action if the extension was + // previously enabled and the disable reason could be added (it can be denied + // by policy). + if (was_enabled && + extension_prefs_->HasDisableReason( + extension_id, disable_reason::DISABLE_NOT_ALLOWLISTED)) { + extension_prefs_->SetExtensionAllowlistAcknowledgeState( + extension_id, ALLOWLIST_ACKNOWLEDGE_NEEDED); + } else { + extension_prefs_->SetExtensionAllowlistAcknowledgeState( + extension_id, ALLOWLIST_ACKNOWLEDGE_NONE); + } +} + void ExtensionAllowlist::ActivateAllowlistEnforcement() { DCHECK(is_allowlist_enforced_); @@ -107,8 +162,7 @@ for (const auto& extension : *all_extensions) { if (extension_prefs_->GetExtensionAllowlistState(extension->id()) == ALLOWLIST_NOT_ALLOWLISTED) { - extension_service_->DisableExtension( - extension->id(), disable_reason::DISABLE_NOT_ALLOWLISTED); + ApplyEnforcement(extension->id()); } } } @@ -118,16 +172,24 @@ std::unique_ptr<ExtensionSet> all_extensions = registry_->GenerateInstalledExtensionsSet(); + + // Find all extensions disabled by allowlist enforcement, remove the disable + // reason and reset the acknowledge state. for (const auto& extension : *all_extensions) { - extension_service_->RemoveDisableReasonAndMaybeEnable( - extension->id(), disable_reason::DISABLE_NOT_ALLOWLISTED); + if (extension_prefs_->HasDisableReason( + extension->id(), disable_reason::DISABLE_NOT_ALLOWLISTED)) { + extension_service_->RemoveDisableReasonAndMaybeEnable( + extension->id(), disable_reason::DISABLE_NOT_ALLOWLISTED); + extension_prefs_->SetExtensionAllowlistAcknowledgeState( + extension->id(), ALLOWLIST_ACKNOWLEDGE_NONE); + } } } void ExtensionAllowlist::OnSafeBrowsingEnhancedChanged() { bool old_value = is_allowlist_enforced_; - // Note that |is_allowlist_enforced_| could remain |false| even if the ESB + // Note that `is_allowlist_enforced_` could remain `false` even if the ESB // setting was turned on if the feature flag is disabled. SetAllowlistEnforcedField(); @@ -141,4 +203,24 @@ } } +// ExtensionPrefsObserver::OnExtensionStateChanged override +void ExtensionAllowlist::OnExtensionStateChanged( + const std::string& extension_id, + bool is_now_enabled) { + if (!is_now_enabled) + return; // We only care if the extension is now enabled. + + if (!is_allowlist_enforced_) + return; // We only care if allowlist if being enforced. + + if (extension_prefs_->GetExtensionAllowlistState(extension_id) == + ALLOWLIST_NOT_ALLOWLISTED) { + // The extension was enabled even though it's not on the allowlist. Consider + // this an acknowledgement from the user, and ensure we don't disable the + // extension again. + extension_prefs_->SetExtensionAllowlistAcknowledgeState( + extension_id, ALLOWLIST_ACKNOWLEDGE_ENABLED_BY_USER); + } +} + } // namespace extensions
diff --git a/chrome/browser/extensions/extension_allowlist.h b/chrome/browser/extensions/extension_allowlist.h index 05685321..afb4cb5 100644 --- a/chrome/browser/extensions/extension_allowlist.h +++ b/chrome/browser/extensions/extension_allowlist.h
@@ -5,7 +5,10 @@ #ifndef CHROME_BROWSER_EXTENSIONS_EXTENSION_ALLOWLIST_H_ #define CHROME_BROWSER_EXTENSIONS_EXTENSION_ALLOWLIST_H_ +#include "base/scoped_observation.h" #include "components/prefs/pref_change_registrar.h" +#include "extensions/browser/extension_prefs.h" +#include "extensions/browser/extension_prefs_observer.h" class Profile; @@ -14,19 +17,18 @@ } // namespace base namespace extensions { -class ExtensionPrefs; class ExtensionRegistry; class ExtensionService; // Manages the Safe Browsing CRX Allowlist. -class ExtensionAllowlist { +class ExtensionAllowlist : private ExtensionPrefsObserver { public: ExtensionAllowlist(Profile* profile, ExtensionPrefs* extension_prefs, ExtensionService* extension_service); ExtensionAllowlist(const ExtensionAllowlist&) = delete; ExtensionAllowlist& operator=(const ExtensionAllowlist&) = delete; - ~ExtensionAllowlist() = default; + ~ExtensionAllowlist(); void Init(); @@ -45,16 +47,27 @@ // Set if the allowlist should be enforced or not. void SetAllowlistEnforcedField(); + // Apply the allowlist enforcement by disabling a not allowlisted extension if + // allowed by policy. + void ApplyEnforcement(const std::string& extension_id); + // Blocklist all extensions with allowlist state `ALLOWLIST_NOT_ALLOWLISTED`. void ActivateAllowlistEnforcement(); // Unblocklist all extensions with allowlist state - // |ALLOWLIST_NOT_ALLOWLISTED|. + // `ALLOWLIST_NOT_ALLOWLISTED`. void DeactivateAllowlistEnforcement(); // Called when the 'Enhanced Safe Browsing' setting changes. void OnSafeBrowsingEnhancedChanged(); + // ExtensionPrefsObserver: + // Observes extension state changes to set + // `ALLOWLIST_ACKNOWLEDGE_ENABLED_BY_USER` when a not allowlisted extension is + // re-enabled by the user. + void OnExtensionStateChanged(const std::string& extension_id, + bool is_now_enabled) override; + Profile* profile_ = nullptr; ExtensionPrefs* extension_prefs_ = nullptr; ExtensionService* extension_service_ = nullptr; @@ -65,6 +78,9 @@ // Used to subscribe to profile preferences updates. PrefChangeRegistrar pref_change_registrar_; + + base::ScopedObservation<ExtensionPrefs, ExtensionPrefsObserver> + extension_prefs_observation_{this}; }; } // namespace extensions
diff --git a/chrome/browser/extensions/extension_allowlist_unittest.cc b/chrome/browser/extensions/extension_allowlist_unittest.cc index 0bb59d0f..987a2836 100644 --- a/chrome/browser/extensions/extension_allowlist_unittest.cc +++ b/chrome/browser/extensions/extension_allowlist_unittest.cc
@@ -19,6 +19,7 @@ // Extension ids used during testing. constexpr char kExtensionId1[] = "behllobkkfkfnphdnhnkndlbkcpglgmj"; constexpr char kExtensionId2[] = "hpiknbiabeeppbpihjehijgoemciehgk"; +constexpr char kExtensionId3[] = "bjafgdebaacbbbecmhlhpofkepfkgcpa"; } // namespace @@ -26,15 +27,7 @@ // // Features EnforceSafeBrowsingExtensionAllowlist and // DisableMalwareExtensionsRemotely are enabled. -class ExtensionAllowlistUnitTest : public ExtensionServiceTestBase { - public: - ExtensionAllowlistUnitTest() { - feature_list_.InitWithFeatures( - {extensions_features::kEnforceSafeBrowsingExtensionAllowlist, - extensions_features::kDisableMalwareExtensionsRemotely}, - {}); - } - +class ExtensionAllowlistUnitTestBase : public ExtensionServiceTestBase { protected: // Creates a test extension service with 3 installed extensions. void CreateExtensionService(bool enhanced_protection_enabled) { @@ -77,6 +70,16 @@ ExtensionPrefs* extension_prefs_; }; +class ExtensionAllowlistUnitTest : public ExtensionAllowlistUnitTestBase { + public: + ExtensionAllowlistUnitTest() { + feature_list_.InitWithFeatures( + {extensions_features::kEnforceSafeBrowsingExtensionAllowlist, + extensions_features::kDisableMalwareExtensionsRemotely}, + {}); + } +}; + TEST_F(ExtensionAllowlistUnitTest, AllowlistEnforcement) { // Created with 3 installed extensions. CreateExtensionService(/*enhanced_protection_enabled=*/true); @@ -409,11 +412,192 @@ extension_prefs()->GetDisableReasons(kExtensionId2)); } -TEST_F(ExtensionAllowlistUnitTest, NoEnforcementWhenFeatureDisabled) { - base::test::ScopedFeatureList feature_list; - feature_list.InitAndDisableFeature( - extensions_features::kEnforceSafeBrowsingExtensionAllowlist); +TEST_F(ExtensionAllowlistUnitTest, AcknowledgeNeededOnEnforcement) { + CreateExtensionService(/*enhanced_protection_enabled=*/true); + service()->Init(); + EXPECT_TRUE(IsEnabled(kExtensionId1)); + EXPECT_EQ( + ALLOWLIST_ACKNOWLEDGE_NONE, + extension_prefs()->GetExtensionAllowlistAcknowledgeState(kExtensionId1)); + + // Make the extension not allowlisted. + PerformActionBasedOnOmahaAttributes(kExtensionId1, + /*is_malware=*/false, + /*is_allowlisted=*/false); + + // Expect the acknowledge state to change appropriately. + EXPECT_TRUE(IsDisabled(kExtensionId1)); + EXPECT_EQ( + ALLOWLIST_ACKNOWLEDGE_NEEDED, + extension_prefs()->GetExtensionAllowlistAcknowledgeState(kExtensionId1)); +} + +TEST_F(ExtensionAllowlistUnitTest, AcknowledgeNotNeededIfAlreadyDisabled) { + CreateExtensionService(/*enhanced_protection_enabled=*/true); + + service()->Init(); + service()->DisableExtension(kExtensionId1, + disable_reason::DISABLE_USER_ACTION); + EXPECT_TRUE(IsDisabled(kExtensionId1)); + EXPECT_EQ( + ALLOWLIST_ACKNOWLEDGE_NONE, + extension_prefs()->GetExtensionAllowlistAcknowledgeState(kExtensionId1)); + + // Make the extension not allowlisted. + PerformActionBasedOnOmahaAttributes(kExtensionId1, + /*is_malware=*/false, + /*is_allowlisted=*/false); + + // There is no need for acknowledge if the extension was already disabled. + EXPECT_TRUE(IsDisabled(kExtensionId1)); + EXPECT_EQ(disable_reason::DISABLE_NOT_ALLOWLISTED | + disable_reason::DISABLE_USER_ACTION, + extension_prefs()->GetDisableReasons(kExtensionId1)); + EXPECT_EQ( + ALLOWLIST_ACKNOWLEDGE_NONE, + extension_prefs()->GetExtensionAllowlistAcknowledgeState(kExtensionId1)); +} + +TEST_F(ExtensionAllowlistUnitTest, + AcknowledgeStateIsSetWhenExtensionIsReenabled) { + CreateExtensionService(/*enhanced_protection_enabled=*/true); + + EXPECT_EQ( + ALLOWLIST_ACKNOWLEDGE_NONE, + extension_prefs()->GetExtensionAllowlistAcknowledgeState(kExtensionId1)); + + // Start with a not allowlisted extension. + extension_prefs()->SetExtensionAllowlistState(kExtensionId1, + ALLOWLIST_NOT_ALLOWLISTED); + + // The enforcement on init should disable the extension. + service()->Init(); + EXPECT_TRUE(IsDisabled(kExtensionId1)); + EXPECT_EQ( + ALLOWLIST_ACKNOWLEDGE_NEEDED, + extension_prefs()->GetExtensionAllowlistAcknowledgeState(kExtensionId1)); + + // Re-enable the extension. + service()->EnableExtension(kExtensionId1); + + // The extensions should now be marked with + // `ALLOWLIST_ACKNOWLEDGE_ENABLED_BY_USER'. + EXPECT_EQ( + ALLOWLIST_ACKNOWLEDGE_ENABLED_BY_USER, + extension_prefs()->GetExtensionAllowlistAcknowledgeState(kExtensionId1)); + EXPECT_TRUE(IsEnabled(kExtensionId1)); + EXPECT_EQ(ALLOWLIST_NOT_ALLOWLISTED, + extension_prefs()->GetExtensionAllowlistState(kExtensionId1)); +} + +TEST_F(ExtensionAllowlistUnitTest, ReenabledExtensionsAreNotReenforced) { + CreateExtensionService(/*enhanced_protection_enabled=*/true); + + // Start with a not allowlisted extension that was re-enabled by user. + extension_prefs()->SetExtensionAllowlistState(kExtensionId1, + ALLOWLIST_NOT_ALLOWLISTED); + extension_prefs()->SetExtensionAllowlistAcknowledgeState( + kExtensionId1, ALLOWLIST_ACKNOWLEDGE_ENABLED_BY_USER); + + // And an extension that became allowlisted after it was re-enabled by user. + extension_prefs()->SetExtensionAllowlistState(kExtensionId2, + ALLOWLIST_ALLOWLISTED); + extension_prefs()->SetExtensionAllowlistAcknowledgeState( + kExtensionId2, ALLOWLIST_ACKNOWLEDGE_ENABLED_BY_USER); + + service()->Init(); + // Even though ExtensionId1 is not allowlisted, it should stay enabled because + // it was re-enabled by user. + EXPECT_TRUE(IsEnabled(kExtensionId1)); + // Assert that ExtensionId2 is enabled before testing the allowlist state + // change. + EXPECT_TRUE(IsEnabled(kExtensionId2)); + + // If `kExtensionId2` becomes not allowlisted again, it should stay enabled + // because the user already chose to re-enable it in the past. + PerformActionBasedOnOmahaAttributes(kExtensionId2, + /*is_malware=*/false, + /*is_allowlisted=*/false); + EXPECT_TRUE(IsEnabled(kExtensionId2)); + EXPECT_EQ( + ALLOWLIST_ACKNOWLEDGE_ENABLED_BY_USER, + extension_prefs()->GetExtensionAllowlistAcknowledgeState(kExtensionId2)); + EXPECT_EQ(ALLOWLIST_NOT_ALLOWLISTED, + extension_prefs()->GetExtensionAllowlistState(kExtensionId2)); +} + +TEST_F(ExtensionAllowlistUnitTest, TurnOffEnhancedProtection) { + CreateExtensionService(/*enhanced_protection_enabled=*/true); + + // Start with 3 not allowlisted extensions. + extension_prefs()->SetExtensionAllowlistState(kExtensionId1, + ALLOWLIST_NOT_ALLOWLISTED); + extension_prefs()->SetExtensionAllowlistState(kExtensionId2, + ALLOWLIST_NOT_ALLOWLISTED); + extension_prefs()->SetExtensionAllowlistState(kExtensionId3, + ALLOWLIST_NOT_ALLOWLISTED); + extension_prefs()->SetExtensionAllowlistAcknowledgeState( + kExtensionId3, ALLOWLIST_ACKNOWLEDGE_ENABLED_BY_USER); + + // They should get disabled by allowlist enforcement and have their + // acknowledge state set (except the extension re-enabled by user). + service()->Init(); + EXPECT_TRUE(IsDisabled(kExtensionId1)); + EXPECT_EQ( + ALLOWLIST_ACKNOWLEDGE_NEEDED, + extension_prefs()->GetExtensionAllowlistAcknowledgeState(kExtensionId1)); + + EXPECT_TRUE(IsDisabled(kExtensionId2)); + EXPECT_EQ( + ALLOWLIST_ACKNOWLEDGE_NEEDED, + extension_prefs()->GetExtensionAllowlistAcknowledgeState(kExtensionId2)); + + EXPECT_TRUE(IsEnabled(kExtensionId3)); + EXPECT_EQ( + ALLOWLIST_ACKNOWLEDGE_ENABLED_BY_USER, + extension_prefs()->GetExtensionAllowlistAcknowledgeState(kExtensionId3)); + + // Leave `kExtensionId1` with acknowledge needed and acknowledge + // `kExtensionId2`. + extension_prefs()->SetExtensionAllowlistAcknowledgeState( + kExtensionId2, ALLOWLIST_ACKNOWLEDGE_DONE); + + // When turning off enhanced protection. + safe_browsing::SetSafeBrowsingState(profile()->GetPrefs(), + safe_browsing::STANDARD_PROTECTION); + + // 'kExtensionId1' and 'kExtensionId2' should be re-enabled and have their + // acknowledge state reset. + EXPECT_TRUE(IsEnabled(kExtensionId1)); + EXPECT_EQ( + ALLOWLIST_ACKNOWLEDGE_NONE, + extension_prefs()->GetExtensionAllowlistAcknowledgeState(kExtensionId1)); + + EXPECT_TRUE(IsEnabled(kExtensionId2)); + EXPECT_EQ( + ALLOWLIST_ACKNOWLEDGE_NONE, + extension_prefs()->GetExtensionAllowlistAcknowledgeState(kExtensionId2)); + + // 'kExtensionId3' should remain enabled because it was already re-enabled by + // user. + EXPECT_TRUE(IsEnabled(kExtensionId3)); + EXPECT_EQ( + ALLOWLIST_ACKNOWLEDGE_ENABLED_BY_USER, + extension_prefs()->GetExtensionAllowlistAcknowledgeState(kExtensionId3)); +} + +class ExtensionAllowlistWithFeatureDisabledUnitTest + : public ExtensionAllowlistUnitTestBase { + public: + ExtensionAllowlistWithFeatureDisabledUnitTest() { + feature_list_.InitAndDisableFeature( + extensions_features::kEnforceSafeBrowsingExtensionAllowlist); + } +}; + +TEST_F(ExtensionAllowlistWithFeatureDisabledUnitTest, + NoEnforcementWhenFeatureDisabled) { // Created with 3 installed extensions. CreateExtensionService(/*enhanced_protection_enabled=*/true);
diff --git a/chrome/browser/extensions/forced_extensions/DIR_METADATA b/chrome/browser/extensions/forced_extensions/DIR_METADATA index 6e4e82d..8edd370 100644 --- a/chrome/browser/extensions/forced_extensions/DIR_METADATA +++ b/chrome/browser/extensions/forced_extensions/DIR_METADATA
@@ -1,3 +1,3 @@ monorail { - component: "Enterprise>ExtensionReliability" + component: "OS>Software>Enterprise>ExtensionReliability" }
diff --git a/chrome/browser/flag-metadata.json b/chrome/browser/flag-metadata.json index 30ec624..14519b7 100644 --- a/chrome/browser/flag-metadata.json +++ b/chrome/browser/flag-metadata.json
@@ -1819,17 +1819,17 @@ { "name": "enable-google-srp-isolated-prerender-nsp", "owners": [ "//chrome/browser/prefetch/prefetch_proxy/OWNERS" ], - "expiry_milestone": 90 + "expiry_milestone": 95 }, { "name": "enable-google-srp-isolated-prerender-probing", "owners": [ "//chrome/browser/prefetch/prefetch_proxy/OWNERS" ], - "expiry_milestone": 90 + "expiry_milestone": 95 }, { "name": "enable-google-srp-isolated-prerenders", "owners": [ "//chrome/browser/prefetch/prefetch_proxy/OWNERS" ], - "expiry_milestone": 90 + "expiry_milestone": 95 }, { "name": "enable-gpu-rasterization", @@ -2412,7 +2412,7 @@ { "name": "enable-surface-control", "owners": [ "vikassoni", "vasilyt" ], - "expiry_milestone": 90 + "expiry_milestone": 100 }, { "name": "enable-swipe-to-move-cursor", @@ -3652,7 +3652,7 @@ { "name": "new-usb-backend", "owners": [ "reillyg@chromium.org" ], - "expiry_milestone": 90 + "expiry_milestone": 100 }, { "name": "notification-scheduler", @@ -3927,7 +3927,7 @@ { "name": "omnibox-refined-focus-state", "owners": [ "yoangela", "chrome-omnibox-team@google.com" ], - "expiry_milestone": 90 + "expiry_milestone": 95 }, { "name": "omnibox-rich-autocompletion", @@ -4968,11 +4968,6 @@ "expiry_milestone": 82 }, { - "name": "tabbed-app-overflow-menu-three-button-actionbar", - "owners": [ "gangwu" ], - "expiry_milestone": 91 - }, - { "name": "text-fragment-color-change", "owners": [ "cheickcisse@google.com" ], "expiry_milestone": 92
diff --git a/chrome/browser/flag_descriptions.cc b/chrome/browser/flag_descriptions.cc index 644ae74c..1d57cb4 100644 --- a/chrome/browser/flag_descriptions.cc +++ b/chrome/browser/flag_descriptions.cc
@@ -3426,11 +3426,6 @@ "Allows user to use touch gestures to move the text cursor around. This " "flag will only take effect on Android 11 and above."; -const char kTabbedAppOverflowMenuThreeButtonActionbarName[] = - "Android tabbed app overflow menu three buttons actionbar"; -const char kTabbedAppOverflowMenuThreeButtonActionbarDescription[] = - "If enabled, the actionbar in the overflow menu will have 3 buttons."; - const char kWalletRequiresFirstSyncSetupCompleteName[] = "Controls whether Wallet (GPay) integration on Android requires " "first-sync-setup to be complete";
diff --git a/chrome/browser/flag_descriptions.h b/chrome/browser/flag_descriptions.h index 2ad4087c..8e413317 100644 --- a/chrome/browser/flag_descriptions.h +++ b/chrome/browser/flag_descriptions.h
@@ -1986,9 +1986,6 @@ extern const char kSwipeToMoveCursorName[]; extern const char kSwipeToMoveCursorDescription[]; -extern const char kTabbedAppOverflowMenuThreeButtonActionbarName[]; -extern const char kTabbedAppOverflowMenuThreeButtonActionbarDescription[]; - extern const char kWalletRequiresFirstSyncSetupCompleteName[]; extern const char kWalletRequiresFirstSyncSetupCompleteDescription[];
diff --git a/chrome/browser/flags/android/chrome_feature_list.cc b/chrome/browser/flags/android/chrome_feature_list.cc index d85f0841..fb43573 100644 --- a/chrome/browser/flags/android/chrome_feature_list.cc +++ b/chrome/browser/flags/android/chrome_feature_list.cc
@@ -230,7 +230,6 @@ &kTabReparenting, &kTabSwitcherOnReturn, &kTabToGTSAnimation, - &kTabbedAppOverflowMenuThreeButtonActionbar, &kTestDefaultDisabled, &kTestDefaultEnabled, &kThemeRefactorAndroid, @@ -643,10 +642,6 @@ const base::Feature kTabToGTSAnimation{"TabToGTSAnimation", base::FEATURE_ENABLED_BY_DEFAULT}; -const base::Feature kTabbedAppOverflowMenuThreeButtonActionbar{ - "TabbedAppOverflowMenuThreeButtonActionbar", - base::FEATURE_DISABLED_BY_DEFAULT}; - const base::Feature kTestDefaultDisabled{"TestDefaultDisabled", base::FEATURE_DISABLED_BY_DEFAULT};
diff --git a/chrome/browser/flags/android/chrome_feature_list.h b/chrome/browser/flags/android/chrome_feature_list.h index 980f1cf..c1414aa 100644 --- a/chrome/browser/flags/android/chrome_feature_list.h +++ b/chrome/browser/flags/android/chrome_feature_list.h
@@ -121,7 +121,6 @@ extern const base::Feature kTabReparenting; extern const base::Feature kTabSwitcherOnReturn; extern const base::Feature kTabToGTSAnimation; -extern const base::Feature kTabbedAppOverflowMenuThreeButtonActionbar; extern const base::Feature kTestDefaultDisabled; extern const base::Feature kTestDefaultEnabled; extern const base::Feature kThemeRefactorAndroid;
diff --git a/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/CachedFeatureFlags.java b/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/CachedFeatureFlags.java index 3825a8b..b5275e41 100644 --- a/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/CachedFeatureFlags.java +++ b/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/CachedFeatureFlags.java
@@ -78,7 +78,6 @@ put(ChromeFeatureList.TEST_DEFAULT_ENABLED, true); put(ChromeFeatureList.REPORT_FEED_USER_ACTIONS, false); put(ChromeFeatureList.INTEREST_FEED_V2, true); - put(ChromeFeatureList.TABBED_APP_OVERFLOW_MENU_THREE_BUTTON_ACTIONBAR, false); put(ChromeFeatureList.THEME_REFACTOR_ANDROID, false); put(ChromeFeatureList.USE_CHIME_ANDROID_SDK, false); put(ChromeFeatureList.CCT_INCOGNITO_AVAILABLE_TO_THIRD_PARTY, false);
diff --git a/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/ChromeFeatureList.java b/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/ChromeFeatureList.java index 25acc16..ddf63ad 100644 --- a/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/ChromeFeatureList.java +++ b/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/ChromeFeatureList.java
@@ -448,8 +448,6 @@ public static final String TAB_REPARENTING = "TabReparenting"; public static final String TAB_SWITCHER_ON_RETURN = "TabSwitcherOnReturn"; public static final String TAB_TO_GTS_ANIMATION = "TabToGTSAnimation"; - public static final String TABBED_APP_OVERFLOW_MENU_THREE_BUTTON_ACTIONBAR = - "TabbedAppOverflowMenuThreeButtonActionbar"; public static final String TEST_DEFAULT_DISABLED = "TestDefaultDisabled"; public static final String TEST_DEFAULT_ENABLED = "TestDefaultEnabled"; public static final String THEME_REFACTOR_ANDROID = "ThemeRefactorAndroid";
diff --git a/chrome/browser/gcm/gcm_profile_service_factory.cc b/chrome/browser/gcm/gcm_profile_service_factory.cc index 429196f16..f156cfe 100644 --- a/chrome/browser/gcm/gcm_profile_service_factory.cc +++ b/chrome/browser/gcm/gcm_profile_service_factory.cc
@@ -106,7 +106,8 @@ // static GCMProfileServiceFactory* GCMProfileServiceFactory::GetInstance() { - return base::Singleton<GCMProfileServiceFactory>::get(); + static base::NoDestructor<GCMProfileServiceFactory> instance; + return instance.get(); } GCMProfileServiceFactory::GCMProfileServiceFactory()
diff --git a/chrome/browser/gcm/gcm_profile_service_factory.h b/chrome/browser/gcm/gcm_profile_service_factory.h index 8786f9f..a4f8d65 100644 --- a/chrome/browser/gcm/gcm_profile_service_factory.h +++ b/chrome/browser/gcm/gcm_profile_service_factory.h
@@ -7,7 +7,7 @@ #include "base/compiler_specific.h" #include "base/macros.h" -#include "base/memory/singleton.h" +#include "base/no_destructor.h" #include "components/gcm_driver/system_encryptor.h" #include "components/keyed_service/content/browser_context_keyed_service_factory.h" @@ -35,7 +35,7 @@ }; private: - friend struct base::DefaultSingletonTraits<GCMProfileServiceFactory>; + friend base::NoDestructor<GCMProfileServiceFactory>; GCMProfileServiceFactory(); ~GCMProfileServiceFactory() override;
diff --git a/chrome/browser/lacros/automation_manager_lacros.cc b/chrome/browser/lacros/automation_manager_lacros.cc new file mode 100644 index 0000000..2287c5b3 --- /dev/null +++ b/chrome/browser/lacros/automation_manager_lacros.cc
@@ -0,0 +1,118 @@ +// Copyright 2021 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/lacros/automation_manager_lacros.h" + +#include "base/pickle.h" +#include "chrome/browser/ui/aura/accessibility/automation_manager_aura.h" +#include "chrome/browser/ui/browser.h" +#include "chrome/browser/ui/browser_list.h" +#include "chrome/browser/ui/browser_window.h" +#include "chromeos/lacros/lacros_chrome_service_impl.h" +#include "extensions/browser/api/automation_internal/automation_event_router.h" +#include "extensions/browser/api/automation_internal/automation_internal_api.h" +#include "extensions/common/extension_messages.h" +#include "ui/accessibility/ax_tree_id.h" +#include "ui/aura/window.h" +#include "ui/aura/window_tree_host.h" +#include "ui/aura/window_tree_host_platform.h" +#include "ui/platform_window/platform_window.h" + +AutomationManagerLacros::AutomationManagerLacros() { + chromeos::LacrosChromeServiceImpl* impl = + chromeos::LacrosChromeServiceImpl::Get(); + if (!impl->IsAutomationAvailable()) + return; + id_ = base::UnguessableToken::Create(); + impl->automation_remote()->RegisterAutomationClient( + receiver_.BindNewPipeAndPassRemote(), id_); + + // TODO(https://crbug.com/1185764): Register as a receiver for events from + // AutomationEventRouter. +} + +AutomationManagerLacros::~AutomationManagerLacros() = default; + +void AutomationManagerLacros::DispatchAccessibilityEvents( + const ui::AXTreeID& tree_id, + std::vector<ui::AXTreeUpdate> updates, + const gfx::Point& mouse_location, + std::vector<ui::AXEvent> events) { + ExtensionMsg_AccessibilityEventBundleParams event_bundle; + event_bundle.tree_id = tree_id; + event_bundle.updates = std::move(updates); + event_bundle.mouse_location = mouse_location; + event_bundle.events = std::move(events); + + // TODO(https://crbug.com/1185764): We'll likely want to push this up to + // AutomationManagerAura/AXTreeViews where we can directly retrieve the root + // window given a view or widget on which we're serializing accessibility + // data. + BrowserList* list = BrowserList::GetInstance(); + std::string window_id; + if (!list->empty()) { + Browser* browser = list->get(0); + aura::Window* window = browser->window()->GetNativeWindow(); + + // On desktop aura there is one WindowTreeHost per top-level window. + aura::WindowTreeHost* window_tree_host = window->GetHost(); + DCHECK(window_tree_host); + // Lacros is based on Ozone/Wayland, which uses PlatformWindow and + // aura::WindowTreeHostPlatform. + aura::WindowTreeHostPlatform* window_tree_host_platform = + static_cast<aura::WindowTreeHostPlatform*>(window_tree_host); + window_id = + window_tree_host_platform->platform_window()->GetWindowUniqueId(); + } + bool is_root = + tree_id == + AutomationManagerAura::GetInstance()->get_root_tree_id_deprecated(); + base::Pickle pickle; + IPC::ParamTraits<ExtensionMsg_AccessibilityEventBundleParams>::Write( + &pickle, event_bundle); + std::string result(static_cast<const char*>(pickle.data()), pickle.size()); + chromeos::LacrosChromeServiceImpl::Get() + ->automation_remote() + ->ReceiveEventPrototype(std::move(result), is_root, id_, window_id); +} + +void AutomationManagerLacros::DispatchAccessibilityLocationChange( + const ExtensionMsg_AccessibilityLocationChangeParams& params) { + // TODO(https://crbug.com/1185764): Implement me. +} +void AutomationManagerLacros::DispatchTreeDestroyedEvent( + ui::AXTreeID tree_id, + content::BrowserContext* browser_context) { + // TODO(https://crbug.com/1185764): Implement me. +} +void AutomationManagerLacros::DispatchActionResult( + const ui::AXActionData& data, + bool result, + content::BrowserContext* browser_context) { + // TODO(https://crbug.com/1185764): Implement me. +} +void AutomationManagerLacros::DispatchGetTextLocationDataResult( + const ui::AXActionData& data, + const base::Optional<gfx::Rect>& rect) { + // TODO(https://crbug.com/1185764): Implement me. +} + +void AutomationManagerLacros::Enable() { + AutomationManagerAura::GetInstance()->Enable(); +} + +void AutomationManagerLacros::EnableTree(const base::UnguessableToken& token) { + // TODO(https://crbug.com/1185764): Plumb this into + // AutomationInternalEnableTreeFunction. +} + +void AutomationManagerLacros::PerformActionPrototype( + const base::UnguessableToken& token, + int32_t automation_node_id, + const std::string& action_type, + int32_t request_id, + base::Value optional_args) { + // TODO(https://crbug.com/1185764): Plumb this into + // AutomationInternalPerformActionFunction. +}
diff --git a/chrome/browser/lacros/automation_manager_lacros.h b/chrome/browser/lacros/automation_manager_lacros.h new file mode 100644 index 0000000..fe3f57a5e --- /dev/null +++ b/chrome/browser/lacros/automation_manager_lacros.h
@@ -0,0 +1,58 @@ +// Copyright 2021 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_LACROS_AUTOMATION_MANAGER_LACROS_H_ +#define CHROME_BROWSER_LACROS_AUTOMATION_MANAGER_LACROS_H_ + +#include "base/memory/weak_ptr.h" +#include "base/unguessable_token.h" +#include "chromeos/crosapi/mojom/automation.mojom.h" +#include "extensions/browser/api/automation_internal/automation_event_router_interface.h" +#include "mojo/public/cpp/bindings/receiver.h" + +// This class receives and forwards automation events to Ash. It can only be +// used on the main thread. +class AutomationManagerLacros + : public crosapi::mojom::AutomationClient, + public extensions::AutomationEventRouterInterface { + public: + AutomationManagerLacros(); + AutomationManagerLacros(const AutomationManagerLacros&) = delete; + AutomationManagerLacros& operator=(const AutomationManagerLacros&) = delete; + ~AutomationManagerLacros() override; + + private: + // extensions::AutomationEventRouterInterface: + void DispatchAccessibilityEvents(const ui::AXTreeID& tree_id, + std::vector<ui::AXTreeUpdate> updates, + const gfx::Point& mouse_location, + std::vector<ui::AXEvent> events) override; + void DispatchAccessibilityLocationChange( + const ExtensionMsg_AccessibilityLocationChangeParams& params) override; + void DispatchTreeDestroyedEvent( + ui::AXTreeID tree_id, + content::BrowserContext* browser_context) override; + void DispatchActionResult(const ui::AXActionData& data, + bool result, + content::BrowserContext* browser_context) override; + void DispatchGetTextLocationDataResult( + const ui::AXActionData& data, + const base::Optional<gfx::Rect>& rect) override; + + // AutomationClient: + void Enable() override; + void EnableTree(const base::UnguessableToken& token) override; + void PerformActionPrototype(const base::UnguessableToken& tree_id, + int32_t automation_node_id, + const std::string& action_type, + int32_t request_id, + base::Value optional_args) override; + + // A unique id that identifies this instance of Lacros. + base::UnguessableToken id_; + mojo::Receiver<crosapi::mojom::AutomationClient> receiver_{this}; + base::WeakPtrFactory<AutomationManagerLacros> weak_ptr_factory_{this}; +}; + +#endif // CHROME_BROWSER_LACROS_AUTOMATION_MANAGER_LACROS_H_
diff --git a/chrome/browser/lacros/chrome_browser_main_extra_parts_lacros.cc b/chrome/browser/lacros/chrome_browser_main_extra_parts_lacros.cc new file mode 100644 index 0000000..7b18928c --- /dev/null +++ b/chrome/browser/lacros/chrome_browser_main_extra_parts_lacros.cc
@@ -0,0 +1,16 @@ +// Copyright 2021 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/lacros/chrome_browser_main_extra_parts_lacros.h" + +#include "chrome/browser/lacros/automation_manager_lacros.h" + +ChromeBrowserMainExtraPartsLacros::ChromeBrowserMainExtraPartsLacros() = + default; +ChromeBrowserMainExtraPartsLacros::~ChromeBrowserMainExtraPartsLacros() = + default; + +void ChromeBrowserMainExtraPartsLacros::PostBrowserStart() { + automation_manager_ = std::make_unique<AutomationManagerLacros>(); +}
diff --git a/chrome/browser/lacros/chrome_browser_main_extra_parts_lacros.h b/chrome/browser/lacros/chrome_browser_main_extra_parts_lacros.h new file mode 100644 index 0000000..82fa9b3 --- /dev/null +++ b/chrome/browser/lacros/chrome_browser_main_extra_parts_lacros.h
@@ -0,0 +1,31 @@ +// Copyright 2021 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_LACROS_CHROME_BROWSER_MAIN_EXTRA_PARTS_LACROS_H_ +#define CHROME_BROWSER_LACROS_CHROME_BROWSER_MAIN_EXTRA_PARTS_LACROS_H_ + +#include "chrome/browser/chrome_browser_main_extra_parts.h" + +#include <memory> + +class AutomationManagerLacros; + +// Browser initialization for Lacros. +class ChromeBrowserMainExtraPartsLacros : public ChromeBrowserMainExtraParts { + public: + ChromeBrowserMainExtraPartsLacros(); + ChromeBrowserMainExtraPartsLacros(const ChromeBrowserMainExtraPartsLacros&) = + delete; + ChromeBrowserMainExtraPartsLacros& operator=( + const ChromeBrowserMainExtraPartsLacros&) = delete; + ~ChromeBrowserMainExtraPartsLacros() override; + + private: + // Overridden from ChromeBrowserMainExtraParts: + void PostBrowserStart() override; + + std::unique_ptr<AutomationManagerLacros> automation_manager_; +}; + +#endif // CHROME_BROWSER_LACROS_CHROME_BROWSER_MAIN_EXTRA_PARTS_LACROS_H_
diff --git a/chrome/browser/lifetime/application_lifetime.cc b/chrome/browser/lifetime/application_lifetime.cc index 67dbba0..8ce9c61 100644 --- a/chrome/browser/lifetime/application_lifetime.cc +++ b/chrome/browser/lifetime/application_lifetime.cc
@@ -344,10 +344,12 @@ // EndSession is invoked once per frame. Only do something the first time. static bool already_ended = false; - // We may get called in the middle of shutdown, e.g. http://crbug.com/70852 - // In this case, do nothing. - if (already_ended || !content::NotificationService::current()) + // We may get called in the middle of shutdown, e.g. https://crbug.com/70852 + // and https://crbug.com/1187418. In this case, do nothing. + if (already_ended || !content::NotificationService::current() || + !g_browser_process) { return; + } already_ended = true; // ~ShutdownWatcherHelper uses IO (it joins a thread). We'll only trigger that
diff --git a/chrome/browser/login_detection/login_detection_browsertest.cc b/chrome/browser/login_detection/login_detection_browsertest.cc index c4892d1..9994439 100644 --- a/chrome/browser/login_detection/login_detection_browsertest.cc +++ b/chrome/browser/login_detection/login_detection_browsertest.cc
@@ -8,10 +8,14 @@ #include "chrome/browser/login_detection/login_detection_tab_helper.h" #include "chrome/browser/login_detection/login_detection_type.h" #include "chrome/browser/login_detection/login_detection_util.h" +#include "chrome/browser/optimization_guide/optimization_guide_keyed_service.h" +#include "chrome/browser/optimization_guide/optimization_guide_keyed_service_factory.h" #include "chrome/browser/profiles/profile.h" #include "chrome/browser/ui/browser.h" #include "chrome/test/base/in_process_browser_test.h" #include "chrome/test/base/ui_test_utils.h" +#include "components/optimization_guide/content/browser/optimization_guide_decider.h" +#include "components/optimization_guide/core/optimization_guide_features.h" #include "components/site_isolation/features.h" #include "components/ukm/test_ukm_recorder.h" #include "content/public/test/browser_test.h" @@ -28,11 +32,19 @@ : https_test_server_(net::EmbeddedTestServer::TYPE_HTTPS) { scoped_feature_list_.InitWithFeaturesAndParameters( {{kLoginDetection, {}}, - {site_isolation::features::kSiteIsolationForPasswordSites, {}}}, + {site_isolation::features::kSiteIsolationForPasswordSites, {}}, + {optimization_guide::features::kOptimizationHints, {}}}, {}); } void SetUpOnMainThread() override { + auto* optimization_guide_decider = + OptimizationGuideKeyedServiceFactory::GetForProfile( + browser()->profile()); + optimization_guide_decider->AddHintForTesting( + GURL("https://www.optguideloggedin.com/page.html"), + optimization_guide::proto::LOGIN_DETECTION, base::nullopt); + https_test_server_.ServeFilesFromSourceDirectory("chrome/test/data"); ASSERT_TRUE(https_test_server_.Start()); histogram_tester_ = std::make_unique<base::HistogramTester>(); @@ -119,4 +131,12 @@ ExpectLoginDetectionTypeMetric(LoginDetectionType::kOauthLogin); } +IN_PROC_BROWSER_TEST_F(LoginDetectionBrowserTest, + OptimizationGuideDetectedBlacklist) { + ui_test_utils::NavigateToURL( + browser(), GURL("https://www.optguideloggedin.com/page.html")); + ExpectLoginDetectionTypeMetric( + LoginDetectionType::kOptimizationGuideDetected); +} + } // namespace login_detection
diff --git a/chrome/browser/login_detection/login_detection_keyed_service.cc b/chrome/browser/login_detection/login_detection_keyed_service.cc index 90c2c0b..0f7bc472 100644 --- a/chrome/browser/login_detection/login_detection_keyed_service.cc +++ b/chrome/browser/login_detection/login_detection_keyed_service.cc
@@ -6,6 +6,8 @@ #include "chrome/browser/login_detection/login_detection_prefs.h" #include "chrome/browser/login_detection/login_detection_util.h" +#include "chrome/browser/optimization_guide/optimization_guide_keyed_service.h" +#include "chrome/browser/optimization_guide/optimization_guide_keyed_service_factory.h" #include "chrome/browser/password_manager/account_password_store_factory.h" #include "chrome/browser/profiles/profile.h" #include "content/public/browser/child_process_security_policy.h" @@ -37,7 +39,13 @@ ServiceAccessType::EXPLICIT_ACCESS)), account_password_sites_(AccountPasswordStoreFactory::GetForProfile( profile, - ServiceAccessType::EXPLICIT_ACCESS)) {} + ServiceAccessType::EXPLICIT_ACCESS)) { + if (auto* optimization_guide_decider = + OptimizationGuideKeyedServiceFactory::GetForProfile(profile_)) { + optimization_guide_decider->RegisterOptimizationTypes( + {optimization_guide::proto::LOGIN_DETECTION}); + } +} LoginDetectionKeyedService::~LoginDetectionKeyedService() = default; @@ -74,6 +82,15 @@ return LoginDetectionType::kPreloadedPasswordSiteLogin; } + if (auto* optimization_guide_decider = + OptimizationGuideKeyedServiceFactory::GetForProfile(profile_)) { + if (optimization_guide_decider->CanApplyOptimization( + url, optimization_guide::proto::LOGIN_DETECTION, nullptr) == + optimization_guide::OptimizationGuideDecision::kTrue) { + return LoginDetectionType::kOptimizationGuideDetected; + } + } + // Check for sites saved in the password manager. if (profile_password_sites_.IsSiteInPasswordStore(url) || account_password_sites_.IsSiteInPasswordStore(url)) {
diff --git a/chrome/browser/login_detection/login_detection_keyed_service_factory.cc b/chrome/browser/login_detection/login_detection_keyed_service_factory.cc index 3884595..7cfb7bb 100644 --- a/chrome/browser/login_detection/login_detection_keyed_service_factory.cc +++ b/chrome/browser/login_detection/login_detection_keyed_service_factory.cc
@@ -6,6 +6,7 @@ #include "chrome/browser/login_detection/login_detection_keyed_service.h" #include "chrome/browser/login_detection/login_detection_util.h" +#include "chrome/browser/optimization_guide/optimization_guide_keyed_service_factory.h" #include "chrome/browser/password_manager/account_password_store_factory.h" #include "chrome/browser/password_manager/password_store_factory.h" #include "chrome/browser/profiles/profile.h" @@ -40,6 +41,7 @@ BrowserContextDependencyManager::GetInstance()) { DependsOn(AccountPasswordStoreFactory::GetInstance()); DependsOn(PasswordStoreFactory::GetInstance()); + DependsOn(OptimizationGuideKeyedServiceFactory::GetInstance()); } LoginDetectionKeyedServiceFactory::~LoginDetectionKeyedServiceFactory() =
diff --git a/chrome/browser/login_detection/login_detection_type.h b/chrome/browser/login_detection/login_detection_type.h index dba46436..2621c9a 100644 --- a/chrome/browser/login_detection/login_detection_type.h +++ b/chrome/browser/login_detection/login_detection_type.h
@@ -39,7 +39,11 @@ // Successful popup based OAuth login flow was detected. kOauthPopUpFirstTimeLoginFlow, - kMaxValue = kOauthPopUpFirstTimeLoginFlow + // Treated as logged-in since the site was detected as commonly logged-in from + // optimization guide hints. + kOptimizationGuideDetected, + + kMaxValue = kOptimizationGuideDetected }; } // namespace login_detection
diff --git a/chrome/browser/lookalikes/lookalike_url_navigation_throttle.cc b/chrome/browser/lookalikes/lookalike_url_navigation_throttle.cc index 10c17d1..8cad620 100644 --- a/chrome/browser/lookalikes/lookalike_url_navigation_throttle.cc +++ b/chrome/browser/lookalikes/lookalike_url_navigation_throttle.cc
@@ -230,6 +230,13 @@ LookalikeUrlMatchType match_type, bool validation_succeeded) { if (validation_succeeded) { + // Add the lookalike URL to the allowlist. + // TODO(meacer): Use a proper key for caching here. At the very least, we + // should allowlist (lookalike, target) pairs. We should also cache some of + // the failure cases, e.g. when the lookalike site serves a manifest but it + // doesn't have an entry for the target site. + ReputationService::Get(profile_)->SetUserIgnore(lookalike_domain); + Resume(); return; }
diff --git a/chrome/browser/lookalikes/lookalike_url_navigation_throttle_browsertest.cc b/chrome/browser/lookalikes/lookalike_url_navigation_throttle_browsertest.cc index af98d5c..4f59a3e 100644 --- a/chrome/browser/lookalikes/lookalike_url_navigation_throttle_browsertest.cc +++ b/chrome/browser/lookalikes/lookalike_url_navigation_throttle_browsertest.cc
@@ -1795,6 +1795,25 @@ histograms.ExpectBucketCount( DigitalAssetLinkCrossValidator::kEventHistogramName, DigitalAssetLinkCrossValidator::Event::kValidationSucceeded, 1); + + // Try again. The first try should have added the lookalike site to the + // allowlist so we shouldn't fetch the manifests again. + TestInterstitialNotShown(browser(), MakeURL("googlé.com")); + CheckNoUkm(); + // Ensure that there was indeed a lookalike match. + histograms.ExpectTotalCount(lookalikes::kHistogramName, 1); + histograms.ExpectBucketCount(lookalikes::kHistogramName, + NavigationSuggestionEvent::kMatchSkeletonTop500, + 1); + // Validator histogram should remain unchanged. + histograms.ExpectTotalCount( + DigitalAssetLinkCrossValidator::kEventHistogramName, 2); + histograms.ExpectBucketCount( + DigitalAssetLinkCrossValidator::kEventHistogramName, + DigitalAssetLinkCrossValidator::Event::kStarted, 1); + histograms.ExpectBucketCount( + DigitalAssetLinkCrossValidator::kEventHistogramName, + DigitalAssetLinkCrossValidator::Event::kValidationSucceeded, 1); } // Similar to ValidAssetLinks_IgnoreInterstitial, but the lookalike manifest
diff --git a/chrome/browser/media/wv_test_license_server_config.cc b/chrome/browser/media/wv_test_license_server_config.cc index 1bfcc28..f7aaf53 100644 --- a/chrome/browser/media/wv_test_license_server_config.cc +++ b/chrome/browser/media/wv_test_license_server_config.cc
@@ -129,11 +129,13 @@ } bool WVTestLicenseServerConfig::IsPlatformSupported() { -#if (defined(OS_LINUX) || defined(OS_CHROMEOS)) && defined(ARCH_CPU_X86_64) +// TODO(crbug.com/1175344): Reenable OS_LINUX once license server +// (or Widevine CDM) updated. +#if defined(OS_CHROMEOS) && defined(ARCH_CPU_X86_64) return true; #else return false; -#endif // (defined(OS_LINUX) || defined(OS_CHROMEOS)) && defined(ARCH_CPU_X86_64) +#endif // defined(OS_CHROMEOS) && defined(ARCH_CPU_X86_64) } std::string WVTestLicenseServerConfig::GetServerURL() {
diff --git a/chrome/browser/metrics/perf/profile_provider_unittest_main.cc b/chrome/browser/metrics/perf/profile_provider_unittest_main.cc index bfd2f115d..c2b8882 100644 --- a/chrome/browser/metrics/perf/profile_provider_unittest_main.cc +++ b/chrome/browser/metrics/perf/profile_provider_unittest_main.cc
@@ -166,6 +166,7 @@ StopSpinningCPU(); profile_provider_.reset(); + TestingBrowserProcess::DeleteInstance(); chromeos::LoginState::Shutdown(); chromeos::PowerManagerClient::Shutdown(); chromeos::DBusThreadManager::Shutdown();
diff --git a/chrome/browser/notifications/non_persistent_notification_handler.cc b/chrome/browser/notifications/non_persistent_notification_handler.cc index 34f88e0..082420f4 100644 --- a/chrome/browser/notifications/non_persistent_notification_handler.cc +++ b/chrome/browser/notifications/non_persistent_notification_handler.cc
@@ -6,7 +6,6 @@ #include "base/bind.h" #include "base/callback.h" -#include "base/strings/nullable_string16.h" #include "build/build_config.h" #include "chrome/browser/notifications/notification_common.h" #include "chrome/browser/notifications/notification_permission_context.h"
diff --git a/chrome/browser/notifications/notification_platform_bridge_android.cc b/chrome/browser/notifications/notification_platform_bridge_android.cc index 4260179..9490852 100644 --- a/chrome/browser/notifications/notification_platform_bridge_android.cc +++ b/chrome/browser/notifications/notification_platform_bridge_android.cc
@@ -15,7 +15,6 @@ #include "base/command_line.h" #include "base/files/file_path.h" #include "base/notreached.h" -#include "base/strings/nullable_string16.h" #include "base/strings/utf_string_conversions.h" #include "chrome/android/chrome_jni_headers/ActionInfo_jni.h" #include "chrome/android/chrome_jni_headers/NotificationPlatformBridge_jni.h"
diff --git a/chrome/browser/notifications/notification_platform_bridge_lacros.cc b/chrome/browser/notifications/notification_platform_bridge_lacros.cc index 36fcdd1..875cf4f 100644 --- a/chrome/browser/notifications/notification_platform_bridge_lacros.cc +++ b/chrome/browser/notifications/notification_platform_bridge_lacros.cc
@@ -64,8 +64,12 @@ mojo_note->timestamp = notification.timestamp(); if (!notification.image().IsEmpty()) mojo_note->image = notification.image().AsImageSkia(); - if (!notification.small_image().IsEmpty()) + if (!notification.small_image().IsEmpty()) { mojo_note->badge = notification.small_image().AsImageSkia(); + mojo_note->badge_needs_additional_masking_has_value = true; + mojo_note->badge_needs_additional_masking = + notification.small_image_needs_additional_masking(); + } for (const auto& item : notification.items()) { auto mojo_item = crosapi::mojom::NotificationItem::New(); mojo_item->title = item.title;
diff --git a/chrome/browser/notifications/notification_platform_bridge_lacros_unittest.cc b/chrome/browser/notifications/notification_platform_bridge_lacros_unittest.cc index 907c3e6..5af7bb1 100644 --- a/chrome/browser/notifications/notification_platform_bridge_lacros_unittest.cc +++ b/chrome/browser/notifications/notification_platform_bridge_lacros_unittest.cc
@@ -178,6 +178,7 @@ last_notification->fullscreen_visibility); ASSERT_FALSE(last_notification->badge.isNull()); + EXPECT_TRUE(last_notification->badge_needs_additional_masking_has_value); EXPECT_TRUE(last_notification->badge.HasRepresentation(1.0f)); EXPECT_TRUE(last_notification->badge.HasRepresentation(2.0f)); EXPECT_TRUE(AreImagesEqual(badge, gfx::Image(last_notification->badge)));
diff --git a/chrome/browser/notifications/notification_platform_bridge_linux.cc b/chrome/browser/notifications/notification_platform_bridge_linux.cc index 2258cdf4..a642b0a8 100644 --- a/chrome/browser/notifications/notification_platform_bridge_linux.cc +++ b/chrome/browser/notifications/notification_platform_bridge_linux.cc
@@ -22,7 +22,6 @@ #include "base/memory/ptr_util.h" #include "base/metrics/histogram_macros.h" #include "base/numerics/ranges.h" -#include "base/strings/nullable_string16.h" #include "base/strings/string_number_conversions.h" #include "base/strings/string_split.h" #include "base/strings/string_util.h"
diff --git a/chrome/browser/notifications/notification_platform_bridge_mac.mm b/chrome/browser/notifications/notification_platform_bridge_mac.mm index ce17b39..4151b15d3 100644 --- a/chrome/browser/notifications/notification_platform_bridge_mac.mm +++ b/chrome/browser/notifications/notification_platform_bridge_mac.mm
@@ -18,7 +18,6 @@ #include "base/mac/scoped_mach_port.h" #include "base/mac/scoped_nsobject.h" #include "base/optional.h" -#include "base/strings/nullable_string16.h" #include "base/strings/string_number_conversions.h" #include "base/strings/sys_string_conversions.h" #include "base/strings/utf_string_conversions.h"
diff --git a/chrome/browser/prefs/browser_prefs.cc b/chrome/browser/prefs/browser_prefs.cc index d170c8a..6b6c83fd 100644 --- a/chrome/browser/prefs/browser_prefs.cc +++ b/chrome/browser/prefs/browser_prefs.cc
@@ -55,7 +55,6 @@ #include "chrome/browser/prefetch/search_prefetch/search_prefetch_service.h" #include "chrome/browser/prefs/chrome_pref_service_factory.h" #include "chrome/browser/prefs/incognito_mode_prefs.h" -#include "chrome/browser/prefs/origin_trial_prefs.h" #include "chrome/browser/prefs/session_startup_pref.h" #include "chrome/browser/printing/print_preview_sticky_settings.h" #include "chrome/browser/profiles/chrome_version_service.h" @@ -101,6 +100,7 @@ #include "components/dom_distiller/core/distilled_page_prefs.h" #include "components/dom_distiller/core/dom_distiller_features.h" #include "components/dom_distiller/core/pref_names.h" +#include "components/embedder_support/origin_trials/origin_trial_prefs.h" #include "components/federated_learning/floc_id.h" #include "components/flags_ui/pref_service_flags_storage.h" #include "components/image_fetcher/core/cache/image_cache.h" @@ -680,6 +680,7 @@ ChromeMetricsServiceClient::RegisterPrefs(registry); ChromeTracingDelegate::RegisterPrefs(registry); component_updater::RegisterPrefs(registry); + embedder_support::OriginTrialPrefs::RegisterPrefs(registry); ExternalProtocolHandler::RegisterPrefs(registry); flags_ui::PrefServiceFlagsStorage::RegisterPrefs(registry); GpuModeManager::RegisterPrefs(registry); @@ -691,7 +692,6 @@ language::UlpLanguageCodeLocator::RegisterLocalStatePrefs(registry); memory::EnterpriseMemoryLimitPrefObserver::RegisterPrefs(registry); network_time::NetworkTimeTracker::RegisterPrefs(registry); - OriginTrialPrefs::RegisterPrefs(registry); password_manager::PasswordManager::RegisterLocalPrefs(registry); policy::BrowserPolicyConnector::RegisterPrefs(registry); policy::PolicyStatisticsCollector::RegisterPrefs(registry);
diff --git a/chrome/browser/prefs/origin_trial_prefs.h b/chrome/browser/prefs/origin_trial_prefs.h deleted file mode 100644 index 3e8baafb..0000000 --- a/chrome/browser/prefs/origin_trial_prefs.h +++ /dev/null
@@ -1,15 +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. - -#ifndef CHROME_BROWSER_PREFS_ORIGIN_TRIAL_PREFS_H_ -#define CHROME_BROWSER_PREFS_ORIGIN_TRIAL_PREFS_H_ - -class PrefRegistrySimple; - -class OriginTrialPrefs { - public: - static void RegisterPrefs(PrefRegistrySimple* registry); -}; - -#endif // CHROME_BROWSER_PREFS_ORIGIN_TRIAL_PREFS_H_
diff --git a/chrome/browser/query_tiles/tile_service_factory.cc b/chrome/browser/query_tiles/tile_service_factory.cc index 2a0279d4..734ecb0 100644 --- a/chrome/browser/query_tiles/tile_service_factory.cc +++ b/chrome/browser/query_tiles/tile_service_factory.cc
@@ -106,7 +106,8 @@ SystemNetworkContextManager::GetInstance()->GetSharedURLLoaderFactory(); base::Version version = version_info::GetVersion(); - std::string channel_name = chrome::GetChannelName(); + std::string channel_name = + chrome::GetChannelName(chrome::WithExtendedStable(true)); std::string client_version = base::StringPrintf("%d.%d.%d.%s.chrome", version.components()[0], // Major
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/background/background_test.js b/chrome/browser/resources/chromeos/accessibility/chromevox/background/background_test.js index d6b6efe..3a0cb59 100644 --- a/chrome/browser/resources/chromeos/accessibility/chromevox/background/background_test.js +++ b/chrome/browser/resources/chromeos/accessibility/chromevox/background/background_test.js
@@ -2671,14 +2671,12 @@ } }); - const evt2 = - new CustomAutomationEvent(EventType.FOCUS, group2, '', '', []); + const evt2 = new CustomAutomationEvent(EventType.FOCUS, group2); const currentRange = ChromeVoxState.instance.currentRange; DesktopAutomationHandler.instance.onFocus(evt2); assertEquals(currentRange, ChromeVoxState.instance.currentRange); - const evt1 = - new CustomAutomationEvent(EventType.FOCUS, group1, '', '', []); + const evt1 = new CustomAutomationEvent(EventType.FOCUS, group1); mockFeedback .call(DesktopAutomationHandler.instance.onFocus.bind( DesktopAutomationHandler.instance, evt1)) @@ -2905,8 +2903,7 @@ } }()); const button = root.find({role: RoleType.BUTTON}); - const alertEvt = - new CustomAutomationEvent(EventType.ALERT, button, '', '', []); + const alertEvt = new CustomAutomationEvent(EventType.ALERT, button); mockFeedback .call(DesktopAutomationHandler.instance.onAlert.bind( DesktopAutomationHandler.instance, alertEvt)) @@ -2929,8 +2926,7 @@ }()); const button = root.find({role: RoleType.BUTTON}); - const alertEvt = - new CustomAutomationEvent(EventType.ALERT, button, '', '', []); + const alertEvt = new CustomAutomationEvent(EventType.ALERT, button); mockFeedback .call(DesktopAutomationHandler.instance.onAlert.bind( DesktopAutomationHandler.instance, alertEvt))
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/background/command_handler.js b/chrome/browser/resources/chromeos/accessibility/chromevox/background/command_handler.js index 6506d77..0ae7911 100644 --- a/chrome/browser/resources/chromeos/accessibility/chromevox/background/command_handler.js +++ b/chrome/browser/resources/chromeos/accessibility/chromevox/background/command_handler.js
@@ -168,9 +168,9 @@ ChromeVoxState.isReadingContinuously = false; return false; case 'toggleEarcons': { - AbstractEarcons.enabled = !AbstractEarcons.enabled; - const announce = AbstractEarcons.enabled ? Msgs.getMsg('earcons_on') : - Msgs.getMsg('earcons_off'); + ChromeVox.earcons.enabled = !ChromeVox.earcons.enabled; + const announce = ChromeVox.earcons.enabled ? Msgs.getMsg('earcons_on') : + Msgs.getMsg('earcons_off'); ChromeVox.tts.speak( announce, QueueMode.FLUSH, AbstractTts.PERSONALITY_ANNOTATION); } @@ -1337,7 +1337,7 @@ CommandHandler.imageNode_ = imageNode; if (imageNode.imageDataUrl) { const event = new CustomAutomationEvent( - EventType.IMAGE_FRAME_UPDATED, imageNode, 'page', '', []); + EventType.IMAGE_FRAME_UPDATED, imageNode, {eventFrom: 'page'}); CommandHandler.onImageFrameUpdated_(event); } else { imageNode.getImageData(0, 0);
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/background/custom_automation_event.js b/chrome/browser/resources/chromeos/accessibility/chromevox/background/custom_automation_event.js index cbe1724..e78510c6 100644 --- a/chrome/browser/resources/chromeos/accessibility/chromevox/background/custom_automation_event.js +++ b/chrome/browser/resources/chromeos/accessibility/chromevox/background/custom_automation_event.js
@@ -24,18 +24,17 @@ /** * @param {chrome.automation.EventType} type The event type. * @param {!chrome.automation.AutomationNode} target The event target. - * @param {string} eventFrom The source of this event. - * @param {string} eventFromAction The accessibility action that caused this - * event. - * @param {!Array<chrome.automation.AutomationIntent>} intents Intents for - * this event. + * @param {!{eventFrom: (string|undefined), + * eventFromAction: (string|undefined), + * intents: (!Array<chrome.automation.AutomationIntent>|undefined) + * }} params */ - constructor(type, target, eventFrom, eventFromAction, intents) { + constructor(type, target, params = {}) { this.type = type; this.target = target; - this.eventFrom = eventFrom; - this.eventFromAction = eventFromAction; - this.intents = intents; + this.eventFrom = params.eventFrom || ''; + this.eventFromAction = params.eventFromAction || ''; + this.intents = params.intents || []; } /**
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/background/desktop_automation_handler.js b/chrome/browser/resources/chromeos/accessibility/chromevox/background/desktop_automation_handler.js index 5121e7f..e3db830 100644 --- a/chrome/browser/resources/chromeos/accessibility/chromevox/background/desktop_automation_handler.js +++ b/chrome/browser/resources/chromeos/accessibility/chromevox/background/desktop_automation_handler.js
@@ -97,8 +97,10 @@ chrome.automation.getFocus((function(focus) { if (focus) { const event = new CustomAutomationEvent( - EventType.FOCUS, focus, 'page', - ActionType.FOCUS, []); + EventType.FOCUS, focus, { + eventFrom: 'page', + eventFromAction: ActionType.FOCUS + }); this.onFocus(event); } }).bind(this)); @@ -216,9 +218,12 @@ if (selectionStart.state[StateType.EDITABLE]) { selectionStart = AutomationUtil.getEditableRoot(selectionStart) || selectionStart; - this.onEditableChanged_(new CustomAutomationEvent( - evt.type, selectionStart, evt.eventFrom, evt.eventFromAction, - evt.intents)); + this.onEditableChanged_( + new CustomAutomationEvent(evt.type, selectionStart, { + eventFrom: evt.eventFrom, + eventFromAction: evt.eventFromAction, + intents: evt.intents + })); } // Non-editable selections are handled in |Background|. @@ -274,8 +279,11 @@ // category flush here or the focus events will all queue up. Output.forceModeForNextSpeechUtterance(QueueMode.CATEGORY_FLUSH); - const event = new CustomAutomationEvent( - EventType.FOCUS, node, evt.eventFrom, evt.eventFromAction, evt.intents); + const event = new CustomAutomationEvent(EventType.FOCUS, node, { + eventFrom: evt.eventFrom, + eventFromAction: evt.eventFromAction, + intents: evt.intents + }); this.onEventDefault(event); // Refresh the handler, if needed, now that ChromeVox focus is up to date. @@ -604,7 +612,8 @@ chrome.automation.getFocus(function(focus) { if (focus) { const event = new CustomAutomationEvent( - EventType.FOCUS, focus, 'page', ActionType.FOCUS, []); + EventType.FOCUS, focus, + {eventFrom: 'page', eventFromAction: ActionType.FOCUS}); this.onFocus(event); } }.bind(this));
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/background/earcons.js b/chrome/browser/resources/chromeos/accessibility/chromevox/background/earcons.js index dffd3b41..d358663 100644 --- a/chrome/browser/resources/chromeos/accessibility/chromevox/background/earcons.js +++ b/chrome/browser/resources/chromeos/accessibility/chromevox/background/earcons.js
@@ -11,10 +11,6 @@ constructor() { super(); - if (localStorage['earcons'] === 'false') { - AbstractEarcons.enabled = false; - } - /** * @type {EarconEngine} * @private @@ -50,7 +46,7 @@ * @override */ playEarcon(earcon, opt_location) { - if (!AbstractEarcons.enabled) { + if (!this.enabled) { return; } if (localStorage['enableEarconLogging'] === 'true') {
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/background/pointer_handler.js b/chrome/browser/resources/chromeos/accessibility/chromevox/background/pointer_handler.js index 78a1c11..11ce705 100644 --- a/chrome/browser/resources/chromeos/accessibility/chromevox/background/pointer_handler.js +++ b/chrome/browser/resources/chromeos/accessibility/chromevox/background/pointer_handler.js
@@ -194,8 +194,8 @@ Output.forceModeForNextSpeechUtterance(QueueMode.FLUSH); DesktopAutomationHandler.instance.onEventDefault(new CustomAutomationEvent( - EventType.HOVER, target, '', chrome.automation.ActionType.HIT_TEST, - [])); + EventType.HOVER, target, + {eventFromAction: chrome.automation.ActionType.HIT_TEST})); } /**
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/background/range_automation_handler.js b/chrome/browser/resources/chromeos/accessibility/chromevox/background/range_automation_handler.js index 4cf8816f..2e3017b 100644 --- a/chrome/browser/resources/chromeos/accessibility/chromevox/background/range_automation_handler.js +++ b/chrome/browser/resources/chromeos/accessibility/chromevox/background/range_automation_handler.js
@@ -192,9 +192,12 @@ return; } - const event = new CustomAutomationEvent( - EventType.CHECKED_STATE_CHANGED, evt.target, evt.eventFrom, - evt.eventFromAction, evt.intents); + const event = + new CustomAutomationEvent(EventType.CHECKED_STATE_CHANGED, evt.target, { + eventFrom: evt.eventFrom, + eventFromAction: evt.eventFromAction, + intents: evt.intents + }); this.onEventIfInRange(event); }
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/common/abstract_earcons.js b/chrome/browser/resources/chromeos/accessibility/chromevox/common/abstract_earcons.js index ddf8e2c..769f1e9 100644 --- a/chrome/browser/resources/chromeos/accessibility/chromevox/common/abstract_earcons.js +++ b/chrome/browser/resources/chromeos/accessibility/chromevox/common/abstract_earcons.js
@@ -103,19 +103,18 @@ } /** - * Toggles earcons on or off. - * @return {boolean} True if earcons are now enabled; false otherwise. + * Whether or not earcons are enabled. + * @return {boolean} True if earcons are enabled. */ - toggle() { - AbstractEarcons.enabled = !AbstractEarcons.enabled; - return AbstractEarcons.enabled; + get enabled() { + return localStorage['earcons'] === 'true'; + } + + /** + * Set whether or not earcons are enabled. + * @param {Boolean} value True turns on earcons, false turns off earcons. + */ + set enabled(value) { + localStorage['earcons'] = value; } }; - - -/** - * Public static flag set to enable or disable earcons. Callers should prefer - * toggle(); however, this member is public for initialization. - * @type {boolean} - */ -AbstractEarcons.enabled = true;
diff --git a/chrome/browser/resources/management/management_browser_proxy.js b/chrome/browser/resources/management/management_browser_proxy.js index 4636c7d..ea104c3 100644 --- a/chrome/browser/resources/management/management_browser_proxy.js +++ b/chrome/browser/resources/management/management_browser_proxy.js
@@ -76,6 +76,7 @@ APP_INFO_AND_ACTIVITY: 'app info and activity', LOGS: 'logs', PRINT: 'print', + PRINT_JOBS: 'print jobs', CROSTINI: 'crostini', USERNAME: 'username', EXTENSION: 'extension',
diff --git a/chrome/browser/resources/management/management_ui.js b/chrome/browser/resources/management/management_ui.js index 90aa0b45..466680c 100644 --- a/chrome/browser/resources/management/management_ui.js +++ b/chrome/browser/resources/management/management_ui.js
@@ -262,6 +262,8 @@ return 'management:report'; case DeviceReportingType.PRINT: return 'cr:print'; + case DeviceReportingType.PRINT_JOBS: + return 'cr:print'; case DeviceReportingType.CROSTINI: return 'management:linux'; case DeviceReportingType.USERNAME:
diff --git a/chrome/browser/resources/memories/BUILD.gn b/chrome/browser/resources/memories/BUILD.gn index 3232fd0..9f46fe6 100644 --- a/chrome/browser/resources/memories/BUILD.gn +++ b/chrome/browser/resources/memories/BUILD.gn
@@ -41,11 +41,20 @@ js_library("memory_card") { deps = [ + ":top_visit", "//components/memories/core:mojo_bindings_webui_js", "//third_party/polymer/v3_0/components-chromium/polymer:polymer_bundled", ] } +js_library("page_favicon") { + deps = [ + "//third_party/polymer/v3_0/components-chromium/polymer:polymer_bundled", + "//ui/webui/resources/js:icon.m", + "//url/mojom:url_mojom_origin_webui_js", + ] +} + js_library("page_thumbnail") { deps = [ ":utils", @@ -56,15 +65,40 @@ ] } +js_library("top_visit") { + deps = [ + ":visit_row", + "//components/memories/core:mojo_bindings_webui_js", + "//third_party/polymer/v3_0/components-chromium/iron-collapse:iron-collapse", + "//third_party/polymer/v3_0/components-chromium/polymer:polymer_bundled", + "//ui/webui/resources/cr_elements/cr_expand_button:cr_expand_button.m", + ] +} + js_library("utils") { deps = [ "//mojo/public/mojom/base:base_webui_js" ] } +js_library("visit_row") { + deps = [ + ":page_favicon", + ":utils", + "//components/memories/core:mojo_bindings_webui_js", + "//mojo/public/mojom/base:base_webui_js", + "//third_party/polymer/v3_0/components-chromium/polymer:polymer_bundled", + "//url/mojom:url_mojom_origin_webui_js", + ] +} + html_to_js("web_components_local") { js_files = [ "app.js", "memory_card.js", + "page_favicon.js", "page_thumbnail.js", + "shared_vars.js", + "top_visit.js", + "visit_row.js", ] } @@ -97,7 +131,11 @@ in_files = [ "app.js", "memory_card.js", + "page_favicon.js", "page_thumbnail.js", + "shared_vars.js", + "top_visit.js", + "visit_row.js", ] deps = [ ":web_components" ] }
diff --git a/chrome/browser/resources/memories/app.html b/chrome/browser/resources/memories/app.html index d8bb9cb..dcd956e 100644 --- a/chrome/browser/resources/memories/app.html +++ b/chrome/browser/resources/memories/app.html
@@ -1,6 +1,6 @@ <style include="cr-shared-style"> :host { - --memory-card-width: 742px; + color: var(--cr-primary-text-color); } .container { @@ -18,9 +18,9 @@ } page-thumbnail { - --border-radius: 16px; - --max-height: 100px; - --max-width: 100px; + --thumbnail-border-radius: 16px; + --thumbnail-max-height: 100px; + --thumbnail-max-width: 100px; margin-inline-end: 25px; } @@ -30,7 +30,7 @@ } .title .description { - color: var(--google-grey-refresh-700); + color: var(--cr-secondary-text-color); font-size: 12px; line-height: 20px; } @@ -41,9 +41,9 @@ } </style> <div class="container"> - <div class="header" hidden$="[[!title_]]"> + <div class="header" hidden="[[!result_.title]]"> <page-thumbnail page="[[createPageWithThumbnail_(result_.thumbnailUrl)]]" - hidden$="[[!result_.thumbnailUrl]]"> + hidden="[[!result_.thumbnailUrl]]"> </page-thumbnail> <div class="title"> <span class="description">$i18n{memoryTitleDescription}</span>
diff --git a/chrome/browser/resources/memories/app.js b/chrome/browser/resources/memories/app.js index 46565e0e..7e9c235 100644 --- a/chrome/browser/resources/memories/app.js +++ b/chrome/browser/resources/memories/app.js
@@ -4,6 +4,7 @@ import './memory_card.js'; import './page_thumbnail.js'; +import './shared_vars.js'; import 'chrome://resources/cr_elements/shared_style_css.m.js'; import {MemoriesResult, PageCallbackRouter, PageHandlerRemote} from '/chrome/browser/ui/webui/memories/memories.mojom-webui.js';
diff --git a/chrome/browser/resources/memories/memory_card.html b/chrome/browser/resources/memories/memory_card.html index 844f85b..e4b09bdf3 100644 --- a/chrome/browser/resources/memories/memory_card.html +++ b/chrome/browser/resources/memories/memory_card.html
@@ -4,9 +4,21 @@ border: 1px solid var(--google-grey-refresh-300); border-radius: 16px; box-sizing: border-box; - color: var(--google-grey-900); margin: 24px 0; padding: 24px; width: var(--memory-card-width); } + + .section .header { + font-size: 14px; + line-height: 20px; + } </style> +<div class="section" hidden="[[!memory.topVisits.length]]"> + <div class="header">$i18n{topVisitsSectionHeader}</div> + <div class="top-visits"> + <template is="dom-repeat" items="[[memory.topVisits]]"> + <top-visit visit="[[item]]"></top-visit> + </template> + </div> +</div>
diff --git a/chrome/browser/resources/memories/memory_card.js b/chrome/browser/resources/memories/memory_card.js index e6b187b..c8dbe020 100644 --- a/chrome/browser/resources/memories/memory_card.js +++ b/chrome/browser/resources/memories/memory_card.js
@@ -2,6 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +import './shared_vars.js'; +import './top_visit.js'; import 'chrome://resources/cr_elements/shared_style_css.m.js'; import {Memory} from '/components/memories/core/memories.mojom-webui.js';
diff --git a/chrome/browser/resources/memories/page_favicon.html b/chrome/browser/resources/memories/page_favicon.html new file mode 100644 index 0000000..605e8141 --- /dev/null +++ b/chrome/browser/resources/memories/page_favicon.html
@@ -0,0 +1,6 @@ +<style> + :host { + background-position: center; + background-repeat: no-repeat; + } +</style>
diff --git a/chrome/browser/resources/memories/page_favicon.js b/chrome/browser/resources/memories/page_favicon.js new file mode 100644 index 0000000..9eb8cde --- /dev/null +++ b/chrome/browser/resources/memories/page_favicon.js
@@ -0,0 +1,57 @@ +// Copyright 2021 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 {getFaviconForPageURL} from 'chrome://resources/js/icon.m.js'; +import {Url} from 'chrome://resources/mojo/url/mojom/url.mojom-webui.js'; +import {html, PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js'; + +/** + * @fileoverview This file provides a custom element displaying a page favicon. + */ + +class PageFavicon extends PolymerElement { + static get is() { + return 'page-favicon'; + } + + static get template() { + return html`{__html_template__}`; + } + + static get properties() { + return { + //======================================================================== + // Public properties + //======================================================================== + + /** + * The URL for which the favicon is shown. + * @type {!Url} + */ + url: Object, + + /** + * The element's style attribute. + * @type {string} + */ + style: { + type: String, + reflectToAttribute: true, + computed: `computeStyle_(url)`, + }, + }; + } + + //============================================================================ + // Helper methods + //============================================================================ + + /** @private */ + computeStyle_() { + return `background-image:${ + getFaviconForPageURL(this.url.url, false, '', 24)}`; + } +} + +customElements.define(PageFavicon.is, PageFavicon);
diff --git a/chrome/browser/resources/memories/page_thumbnail.html b/chrome/browser/resources/memories/page_thumbnail.html index 956d456..61102cc 100644 --- a/chrome/browser/resources/memories/page_thumbnail.html +++ b/chrome/browser/resources/memories/page_thumbnail.html
@@ -1,10 +1,10 @@ -<style include="cr-shared-style"> +<style> :host { align-items: center; display: flex; - height: var(--max-height); + height: var(--thumbnail-max-height); justify-content: center; - width: var(--max-width); + width: var(--thumbnail-max-width); } a { @@ -17,12 +17,12 @@ } img { - border-radius: var(--border-radius, 0); - max-height: var(--max-height); - max-width: var(--max-width); + border-radius: var(--thumbnail-border-radius, 0); + max-height: var(--thumbnail-max-height); + max-width: var(--thumbnail-max-width); } </style> <a href$="[[page.url.url]]"> <img title="[[decodeMojoString16_(page.title)]]" - src$="[[thumbnailSrc_(page.thumbnailUrl)]]"> + src$="[[getThumbnailSrc_(page.thumbnailUrl)]]"> </a>
diff --git a/chrome/browser/resources/memories/page_thumbnail.js b/chrome/browser/resources/memories/page_thumbnail.js index 4ff87de5..780d6f4 100644 --- a/chrome/browser/resources/memories/page_thumbnail.js +++ b/chrome/browser/resources/memories/page_thumbnail.js
@@ -2,8 +2,6 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -import 'chrome://resources/cr_elements/shared_style_css.m.js'; - import {WebPage} from '/components/memories/core/memories.mojom-webui.js'; import {String16} from 'chrome://resources/mojo/mojo/public/mojom/base/string16.mojom-webui.js'; import {Url} from 'chrome://resources/mojo/url/mojom/url.mojom-webui.js'; @@ -48,7 +46,7 @@ * @return {string} * @private */ - thumbnailSrc_(thumbnailUrl) { + getThumbnailSrc_(thumbnailUrl) { return thumbnailUrl ? `chrome://image?${thumbnailUrl.url}` : ''; }
diff --git a/chrome/browser/resources/memories/shared_vars.html b/chrome/browser/resources/memories/shared_vars.html new file mode 100644 index 0000000..41e1cf8f --- /dev/null +++ b/chrome/browser/resources/memories/shared_vars.html
@@ -0,0 +1,9 @@ +<custom-style> +<style> + html { + --memory-card-width: 742px; + --visit-row-favicon-size: var(--visit-row-height); + --visit-row-height: 36px; + } +</style> +</custom-style>
diff --git a/chrome/browser/resources/memories/shared_vars.js b/chrome/browser/resources/memories/shared_vars.js new file mode 100644 index 0000000..098a438 --- /dev/null +++ b/chrome/browser/resources/memories/shared_vars.js
@@ -0,0 +1,10 @@ +// Copyright 2021 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://resources/cr_elements/shared_vars_css.m.js'; +import 'chrome://resources/polymer/v3_0/paper-styles/color.js'; + +const template = document.createElement('template'); +template.innerHTML = `{__html_template__}`; +document.head.appendChild(template.content.cloneNode(true));
diff --git a/chrome/browser/resources/memories/top_visit.html b/chrome/browser/resources/memories/top_visit.html new file mode 100644 index 0000000..8c4c3c8 --- /dev/null +++ b/chrome/browser/resources/memories/top_visit.html
@@ -0,0 +1,55 @@ +<style> + :host { + display: block; + padding: 10px 0; + position: relative; + } + + .vertical-line { + --vertical-line-width: 2px; + border-inline-start: var(--vertical-line-width) solid var(--google-grey-refresh-100); + height: 100%; + left: calc((var(--visit-row-favicon-size) - var(--vertical-line-width)) / 2); + position: absolute; + } + + :host-context([dir='rtl']) .vertical-line { + left: unset; + right: calc((var(--visit-row-favicon-size) - var(--vertical-line-width)) / 2); + } + + :host(:last-of-type) .vertical-line { + display: none; + } + + cr-expand-button { + --cr-active-background-color: transparent; + --cr-hover-background-color: transparent; + --cr-section-vertical-padding: 0; + } + + .related-visits { + margin-inline-start: 52px; + } + + .related-visits visit-row { + padding: 4px 0; + } +</style> +<div class="vertical-line"></div> +<template is="dom-if" if="[[visit.relatedVisits.length]]"> + <cr-expand-button expanded="{{expanded_}}"> + <visit-row is-top-visit visit="[[visit]]" on-visit-click="onVisitClick_"> + </visit-row> + </cr-expand-button> + <iron-collapse opened="[[expanded_]]"> + <div class="related-visits"> + <template is="dom-repeat" items="[[visit.relatedVisits]]"> + <visit-row visit="[[item]]"></visit-row> + </template> + </div> + </iron-collapse> +</template> +<template is="dom-if" if="[[!visit.relatedVisits.length]]"> + <visit-row is-top-visit visit="[[visit]]"></visit-row> +</template>
diff --git a/chrome/browser/resources/memories/top_visit.js b/chrome/browser/resources/memories/top_visit.js new file mode 100644 index 0000000..16c3d05 --- /dev/null +++ b/chrome/browser/resources/memories/top_visit.js
@@ -0,0 +1,66 @@ +// Copyright 2021 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 './shared_vars.js'; +import './visit_row.js'; +import 'chrome://resources/cr_elements/cr_expand_button/cr_expand_button.m.js'; +import 'chrome://resources/polymer/v3_0/iron-collapse/iron-collapse.js'; + +import {Visit} from '/components/memories/core/memories.mojom-webui.js'; +import {html, PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js'; + +/** + * @fileoverview This file provides a custom element displaying a top visit + * within a Memory. A top visit is a featured, i.e., visible, visit with an + * optional set of related visits which are not visible by default. + */ + +class TopVisitElement extends PolymerElement { + static get is() { + return 'top-visit'; + } + + static get template() { + return html`{__html_template__}`; + } + + static get properties() { + return { + //======================================================================== + // Public properties + //======================================================================== + + /** + * The top visit to display + * @type {!Visit} + */ + visit: Object, + + //======================================================================== + // Private properties + //======================================================================== + + /** + * Whether the related visits of the top visit are expanded/visible. + * @private {boolean} + */ + expanded_: Boolean, + }; + } + + //============================================================================ + // Event handlers + //============================================================================ + + /** + * @param {!CustomEvent<{event:!MouseEvent}>} e + * @private + */ + onVisitClick_(e) { + // Prevent the enclosing <cr-expand-button> from receiving this event. + e.detail.event.stopImmediatePropagation(); + } +} + +customElements.define(TopVisitElement.is, TopVisitElement);
diff --git a/chrome/browser/resources/memories/visit_row.html b/chrome/browser/resources/memories/visit_row.html new file mode 100644 index 0000000..d4d0b96 --- /dev/null +++ b/chrome/browser/resources/memories/visit_row.html
@@ -0,0 +1,69 @@ +<style include="cr-shared-style"> + :host { + display: block; + line-height: var(--visit-row-height); + position: relative; + } + + .header { + display: flex; + justify-content: space-between; + } + + .start-justtified { + color: inherit; + display: flex; + overflow: hidden; + text-decoration: none; + z-index: 1; + } + + .end-justified { + display: flex; + flex-shrink: 0; + } + + page-favicon { + flex-shrink: 0; + height: var(--visit-row-favicon-size); + margin-inline-end: 16px; + width: var(--visit-row-favicon-size); + } + + :host([is-top-visit]) page-favicon { + background-color: var(--google-grey-refresh-100); + border-radius: 8px; + } + + .title { + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + } + + .hostname, + .separator, + .timestamp { + flex-shrink: 0; + margin-inline-start: 8px; + } + + .hostname, + .timestamp { + color: var(--cr-secondary-text-color); + } +</style> +<div class="header"> + <a class="start-justtified" href="[[visit.url.url]]" on-click="onClick_"> + <page-favicon url="[[visit.url]]"></page-favicon> + <span class="title">[[decodeMojoString16_(visit.pageTitle)]]</span> + <span class="separator">–</span> + <span class="hostname">[[getHostnameFromUrl_(visit.url)]]</span> + </a> + <div class="end-justified"> + <div class="timestamp">[[getTimeOfVisit_(visit)]]</div> + <cr-icon-button id="action" class="icon-more-vert" + hidden="[[visit.relatedVisits.length]]"> + </cr-icon-button> + </div> +</div>
diff --git a/chrome/browser/resources/memories/visit_row.js b/chrome/browser/resources/memories/visit_row.js new file mode 100644 index 0000000..87cd375 --- /dev/null +++ b/chrome/browser/resources/memories/visit_row.js
@@ -0,0 +1,111 @@ +// Copyright 2021 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 './page_favicon.js'; +import './shared_vars.js'; +import 'chrome://resources/cr_elements/shared_style_css.m.js'; + +import {Visit} from '/components/memories/core/memories.mojom-webui.js'; +import {String16} from 'chrome://resources/mojo/mojo/public/mojom/base/string16.mojom-webui.js'; +import {Url} from 'chrome://resources/mojo/url/mojom/url.mojom-webui.js'; +import {html, PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js'; + +import {decodeMojoString16} from './utils.js'; + +/** + * @fileoverview This file provides a custom element displaying a visit to a + * page within a Memory. A visit features the page favicon, title, a timestamp, + * as well as an action menu. + */ + +class VisitRowElement extends PolymerElement { + static get is() { + return 'visit-row'; + } + + static get template() { + return html`{__html_template__}`; + } + + static get properties() { + return { + //======================================================================== + // Public properties + //======================================================================== + + /** + * The visit to display. + * @type {!Visit} + */ + visit: Object, + + /** + * Whether the visit is a top visit. + * @type {boolean} + */ + isTopVisit: { + type: Boolean, + reflectToAttribute: true, + }, + }; + } + + //============================================================================ + // Event handlers + //============================================================================ + + /** + * @param {!MouseEvent} event + * @private + */ + onClick_(event) { + // Only handle main (usually the left) and auxiliary (usually the wheel or + // the middle) button presses. + if (event.button > 1) { + return; + } + + this.dispatchEvent(new CustomEvent('visit-click', { + bubbles: true, + composed: true, + detail: {event}, + })); + } + + //============================================================================ + // Helper methods + //============================================================================ + + /** + * Converts a Mojo String16 to a JS string. + * @param {String16} str + * @return {string} + * @private + */ + decodeMojoString16_(str) { + return decodeMojoString16(str); + } + + /** + * @param {!Url} url + * @return {string} The domain name of the URL without the leading 'www.'. + * @private + */ + getHostnameFromUrl_(url) { + return new URL(url.url).hostname.replace(/^(www\.)/, ''); + } + + /** + * @param {!Visit} visit + * @return {string} Time of day or relative date of visit, e.g., "1 day ago", + * depending on if the visit is a top visit. + * @private + */ + getTimeOfVisit_(visit) { + return decodeMojoString16( + this.isTopVisit ? visit.relativeDate : visit.timeOfDay); + } +} + +customElements.define(VisitRowElement.is, VisitRowElement);
diff --git a/chrome/browser/resources/new_tab_page/BUILD.gn b/chrome/browser/resources/new_tab_page/BUILD.gn index c731aab4..637e406 100644 --- a/chrome/browser/resources/new_tab_page/BUILD.gn +++ b/chrome/browser/resources/new_tab_page/BUILD.gn
@@ -80,6 +80,7 @@ ":background_manager", ":browser_proxy", ":customize_dialog_types", + ":metrics_utils", ":one_google_bar_api", ":promo_browser_command_proxy", "modules:module_wrapper", @@ -188,6 +189,11 @@ js_library("utils") { } +js_library("metrics_utils") { + deps = [ "//ui/webui/resources/js:load_time_data.m" ] + externs_list = [ "//third_party/closure_compiler/externs/metrics_private.js" ] +} + js_library("iframe") { deps = [ "//third_party/polymer/v3_0/components-chromium/polymer:polymer_bundled", @@ -273,6 +279,7 @@ "new_tab_page.js", "promo_browser_command_proxy.js", "utils.js", + "metrics_utils.js", ] }
diff --git a/chrome/browser/resources/new_tab_page/app.js b/chrome/browser/resources/new_tab_page/app.js index 2863e7c..06f7eee 100644 --- a/chrome/browser/resources/new_tab_page/app.js +++ b/chrome/browser/resources/new_tab_page/app.js
@@ -23,6 +23,7 @@ import {BackgroundManager} from './background_manager.js'; import {BrowserProxy} from './browser_proxy.js'; import {BackgroundSelection, BackgroundSelectionType, CustomizeDialogPage} from './customize_dialog_types.js'; +import {recordLoadDuration} from './metrics_utils.js'; import {ModuleDescriptor} from './modules/module_descriptor.js'; import {ModuleRegistry} from './modules/module_registry.js'; import {oneGoogleBarApi} from './one_google_bar_api.js'; @@ -642,13 +643,16 @@ onModulesLoadedAndVisibilityDeterminedChange_() { if (this.modulesLoadedAndVisibilityDetermined_ && loadTimeData.getBoolean('modulesEnabled')) { - this.pageHandler_.onModulesRendered(BrowserProxy.getInstance().now()); + recordLoadDuration( + 'NewTabPage.Modules.ShownTime', BrowserProxy.getInstance().now()); this.moduleDescriptors_.forEach(({id}) => { chrome.metricsPrivate.recordBoolean( `NewTabPage.Modules.EnabledOnNTPLoad.${id}`, !this.disabledModules_.all && !this.disabledModules_.ids.includes(id)); }); + chrome.metricsPrivate.recordBoolean( + 'NewTabPage.Modules.VisibleOnNTPLoad', !this.disabledModules_.all); } }
diff --git a/chrome/browser/resources/new_tab_page/metrics_utils.js b/chrome/browser/resources/new_tab_page/metrics_utils.js new file mode 100644 index 0000000..4671db6 --- /dev/null +++ b/chrome/browser/resources/new_tab_page/metrics_utils.js
@@ -0,0 +1,70 @@ +// Copyright 2021 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 {loadTimeData} from 'chrome://resources/js/load_time_data.m.js'; + +/** + * Records |durationMs| in the |metricName| histogram. + * @param {string} metricName + * @param {number} durationMs + */ +export function recordDuration(metricName, durationMs) { + chrome.metricsPrivate.recordValue( + { + metricName, + type: chrome.metricsPrivate.MetricTypeType.HISTOGRAM_LOG, + min: 1, + max: 60000, // 60 seconds. + buckets: 100, + }, + Math.floor(durationMs)); +} + +/** + * Records the duration between navigation start and |msSinceEpoch| in the + * |metricName| histogram. + * @param {string} metricName + * @param {number} msSinceEpoch + */ +export function recordLoadDuration(metricName, msSinceEpoch) { + recordDuration( + metricName, + msSinceEpoch - /** @type {number} */ + (loadTimeData.getValue('navigationStartTime'))); +} + +/** + * Records |value| (expected to be between 0 and 10) into the ten-bucket + * |metricName| histogram. + * @param {string} metricName + * @param {number} value + */ +export function recordPerdecage(metricName, value) { + chrome.metricsPrivate.recordValue( + { + metricName, + type: chrome.metricsPrivate.MetricTypeType.HISTOGRAM_LINEAR, + min: 1, // Choose 1 if real min is 0. + max: 11, // Exclusive. + buckets: 12, // Numbers 0-10 and unused overflow bucket of 11. + }, + value); +} + +/** + * Records that an event has happened rather than a value in the |metricName| + * histogram. + * @param {string} metricName + */ +export function recordOccurence(metricName) { + chrome.metricsPrivate.recordValue( + { + metricName, + type: chrome.metricsPrivate.MetricTypeType.HISTOGRAM_LINEAR, + min: 1, + max: 1, + buckets: 1, + }, + 1); +}
diff --git a/chrome/browser/resources/new_tab_page/modules/BUILD.gn b/chrome/browser/resources/new_tab_page/modules/BUILD.gn index 907101b..d13f3b22 100644 --- a/chrome/browser/resources/new_tab_page/modules/BUILD.gn +++ b/chrome/browser/resources/new_tab_page/modules/BUILD.gn
@@ -10,7 +10,10 @@ import("//ui/webui/webui_features.gni") js_library("module_descriptor") { - deps = [ "..:browser_proxy" ] + deps = [ + "..:browser_proxy", + "..:metrics_utils", + ] } js_library("modules") { @@ -28,7 +31,10 @@ js_library("module_wrapper") { deps = [ ":module_descriptor", + "..:browser_proxy", + "..:metrics_utils", "//third_party/polymer/v3_0/components-chromium/polymer:polymer_bundled", + "//ui/webui/resources/js:assert.m", ] externs_list = [ "//third_party/closure_compiler/externs/metrics_private.js" ] }
diff --git a/chrome/browser/resources/new_tab_page/modules/module_descriptor.js b/chrome/browser/resources/new_tab_page/modules/module_descriptor.js index 879d47f..ba79866 100644 --- a/chrome/browser/resources/new_tab_page/modules/module_descriptor.js +++ b/chrome/browser/resources/new_tab_page/modules/module_descriptor.js
@@ -3,7 +3,7 @@ // found in the LICENSE file. import {BrowserProxy} from '../browser_proxy.js'; -import {mojoTimeDelta} from '../utils.js'; +import {recordDuration, recordLoadDuration} from '../metrics_utils.js'; /** * @fileoverview Provides the module descriptor. Each module must create a @@ -75,7 +75,10 @@ return; } const loadEndTime = BrowserProxy.getInstance().now(); - BrowserProxy.getInstance().handler.onModuleLoaded( - this.id_, mojoTimeDelta(loadEndTime - loadStartTime), loadEndTime); + const duration = loadEndTime - loadStartTime; + recordLoadDuration('NewTabPage.Modules.Loaded', loadEndTime); + recordLoadDuration(`NewTabPage.Modules.Loaded.${this.id_}`, loadEndTime); + recordDuration('NewTabPage.Modules.LoadDuration', duration); + recordDuration(`NewTabPage.Modules.LoadDuration.${this.id_}`, duration); } }
diff --git a/chrome/browser/resources/new_tab_page/modules/module_wrapper.js b/chrome/browser/resources/new_tab_page/modules/module_wrapper.js index c03d2c91..350cd20 100644 --- a/chrome/browser/resources/new_tab_page/modules/module_wrapper.js +++ b/chrome/browser/resources/new_tab_page/modules/module_wrapper.js
@@ -6,6 +6,8 @@ import {html, microTask, PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js'; import {BrowserProxy} from '../browser_proxy.js'; +import {recordLoadDuration, recordOccurence, recordPerdecage} from '../metrics_utils.js'; + import {ModuleDescriptor} from './module_descriptor.js'; /** @fileoverview Element that implements the common module UI. */ @@ -42,15 +44,19 @@ // Log at most one usage per module per NTP page load. This is possible, // if a user opens a link in a new tab. this.descriptor.element.addEventListener('usage', () => { - BrowserProxy.getInstance().handler.onModuleUsage(this.descriptor.id); + recordOccurence('NewTabPage.Modules.Usage'); + recordOccurence(`NewTabPage.Modules.Usage.${this.descriptor.id}`); }, {once: true}); // Install observer to log module header impression. const headerObserver = new IntersectionObserver(([{intersectionRatio}]) => { if (intersectionRatio >= 1.0) { headerObserver.disconnect(); - BrowserProxy.getInstance().handler.onModuleImpression( - this.descriptor.id, BrowserProxy.getInstance().now()); + const time = BrowserProxy.getInstance().now(); + recordLoadDuration('NewTabPage.Modules.Impression', time); + recordLoadDuration( + `NewTabPage.Modules.Impression.${this.descriptor.id}`, time); + this.dispatchEvent(new Event('detect-impression')); } }, {threshold: 1.0}); @@ -62,17 +68,6 @@ Math.floor(Math.max(intersectionPerdecage, intersectionRatio * 10)); }, {threshold: [0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1]}); window.addEventListener('unload', () => { - const recordPerdecage = (metricName, value) => { - chrome.metricsPrivate.recordValue( - { - metricName, - type: chrome.metricsPrivate.MetricTypeType.HISTOGRAM_LINEAR, - min: 1, // Choose 1 if real min is 0. - max: 11, // Exclusive. - buckets: 12, // Numbers 0-10 and unused overflow bucket of 11. - }, - value); - }; recordPerdecage( 'NewTabPage.Modules.ImpressionRatio', intersectionPerdecage); recordPerdecage(
diff --git a/chrome/browser/resources/new_tab_page/new_tab_page.js b/chrome/browser/resources/new_tab_page/new_tab_page.js index 98fa96e..05ce5c4 100644 --- a/chrome/browser/resources/new_tab_page/new_tab_page.js +++ b/chrome/browser/resources/new_tab_page/new_tab_page.js
@@ -16,6 +16,7 @@ export {BrowserProxy} from './browser_proxy.js'; export {BackgroundSelectionType, CustomizeDialogPage} from './customize_dialog_types.js'; export {ImgElement} from './img.js'; +export {recordDuration, recordLoadDuration, recordOccurence, recordPerdecage} from './metrics_utils.js'; export {ChromeCartProxy} from './modules/cart/chrome_cart_proxy.js'; export {chromeCartDescriptor} from './modules/cart/module.js'; export {DriveProxy} from './modules/drive/drive_module_proxy.js';
diff --git a/chrome/browser/resources/settings/chromeos/internet_page/internet_detail_page.js b/chrome/browser/resources/settings/chromeos/internet_page/internet_detail_page.js index f3696cd..40a430c 100644 --- a/chrome/browser/resources/settings/chromeos/internet_page/internet_detail_page.js +++ b/chrome/browser/resources/settings/chromeos/internet_page/internet_detail_page.js
@@ -793,7 +793,7 @@ } if (!properties) { - console.error('Details page: GUID no longer exists: ' + this.guid); + // Close the page if the network was removed and no longer exists. this.close(); return; }
diff --git a/chrome/browser/resources/settings/chromeos/os_people_page/account_manager.html b/chrome/browser/resources/settings/chromeos/os_people_page/account_manager.html index be0ce355..b226a4fd 100644 --- a/chrome/browser/resources/settings/chromeos/os_people_page/account_manager.html +++ b/chrome/browser/resources/settings/chromeos/os_people_page/account_manager.html
@@ -46,7 +46,7 @@ .profile-icon.device-account-icon { --profile-icon-size: 60px; - margin-top: 20px; + margin-top: 16px; } .device-account-container { @@ -57,7 +57,8 @@ .device-account-container .primary { font-weight: 500; - margin-top: 15px; + margin-bottom: 4px; + margin-top: 16px; } .account-list-item { @@ -112,7 +113,7 @@ #account-list-header > h2 { padding-bottom: 8px; - padding-top: 8px; + padding-top: 16px; } #account-list-header { @@ -187,6 +188,7 @@ .managed-message { color: var(--cr-secondary-text-color); justify-content: center; + margin-top: 16px; } .managed-message > iron-icon {
diff --git a/chrome/browser/speech/extension_api/tts_extension_api.cc b/chrome/browser/speech/extension_api/tts_extension_api.cc index 43c4b9b..711fe15ac 100644 --- a/chrome/browser/speech/extension_api/tts_extension_api.cc +++ b/chrome/browser/speech/extension_api/tts_extension_api.cc
@@ -309,11 +309,14 @@ extensions::ExtensionHost* host = extensions::ProcessManager::Get(browser_context()) ->GetBackgroundHostForExtension(extension()->id()); - utterance = content::TtsUtterance::Create(host->host_contents()); - } else { - utterance = content::TtsUtterance::Create(browser_context()); + + if (host && host->host_contents()) + utterance = content::TtsUtterance::Create(host->host_contents()); } + if (!utterance) + utterance = content::TtsUtterance::Create(browser_context()); + utterance->SetText(text); utterance->SetVoiceName(voice_name); utterance->SetSrcId(src_id);
diff --git a/chrome/browser/sync/test/integration/single_client_sessions_sync_test.cc b/chrome/browser/sync/test/integration/single_client_sessions_sync_test.cc index 5157f44..53383aa 100644 --- a/chrome/browser/sync/test/integration/single_client_sessions_sync_test.cc +++ b/chrome/browser/sync/test/integration/single_client_sessions_sync_test.cc
@@ -1001,8 +1001,14 @@ base::test::ScopedFeatureList features_; }; +// TODO(1185289): Fails under msan. +#if defined(MEMORY_SANITIZER) +#define MAYBE_ShouldNotDeleteLastClosedTab DISABLED_ShouldNotDeleteLastClosedTab +#else +#define MAYBE_ShouldNotDeleteLastClosedTab ShouldNotDeleteLastClosedTab +#endif IN_PROC_BROWSER_TEST_F(SingleClientSessionsWithDestroyProfileSyncTest, - ShouldNotDeleteLastClosedTab) { + MAYBE_ShouldNotDeleteLastClosedTab) { ASSERT_TRUE(SetupSync()) << "SetupSync() failed."; ASSERT_TRUE(CheckInitialState(0));
diff --git a/chrome/browser/sync/test/integration/two_client_sessions_sync_test.cc b/chrome/browser/sync/test/integration/two_client_sessions_sync_test.cc index baeb699..e4534cd 100644 --- a/chrome/browser/sync/test/integration/two_client_sessions_sync_test.cc +++ b/chrome/browser/sync/test/integration/two_client_sessions_sync_test.cc
@@ -269,8 +269,14 @@ base::test::ScopedFeatureList features_; }; +// TODO(1185289): Fails under msan. +#if defined(MEMORY_SANITIZER) +#define MAYBE_ShouldNotSyncLastClosedTab DISABLED_ShouldNotSyncLastClosedTab +#else +#define MAYBE_ShouldNotSyncLastClosedTab ShouldNotSyncLastClosedTab +#endif IN_PROC_BROWSER_TEST_F(TwoClientSessionsWithDestroyProfileSyncTest, - ShouldNotSyncLastClosedTab) { + MAYBE_ShouldNotSyncLastClosedTab) { ASSERT_TRUE(SetupSync()) << "SetupSync() failed."; ASSERT_TRUE(CheckInitialState(0));
diff --git a/chrome/browser/task_manager/sampling/task_manager_impl.cc b/chrome/browser/task_manager/sampling/task_manager_impl.cc index 8a13e64..c6733e68 100644 --- a/chrome/browser/task_manager/sampling/task_manager_impl.cc +++ b/chrome/browser/task_manager/sampling/task_manager_impl.cc
@@ -102,6 +102,11 @@ return lazy_task_manager_instance.Pointer(); } +bool TaskManagerImpl::IsCreated() { + DCHECK_CURRENTLY_ON(content::BrowserThread::UI); + return lazy_task_manager_instance.IsCreated(); +} + void TaskManagerImpl::ActivateTask(TaskId task_id) { GetTaskByTaskId(task_id)->Activate(); } @@ -447,24 +452,6 @@ return task->task_id(); } -void TaskManagerImpl::UpdateAccumulatedStatsNetworkForRoute( - int process_id, - int route_id, - int64_t recv_bytes, - int64_t sent_bytes) { - DCHECK_CURRENTLY_ON(content::BrowserThread::UI); - Task* task = GetTaskByRoute(process_id, route_id); - if (!task) { - // Orphaned/unaccounted activity is credited to the Browser process. - task = GetTaskByRoute(content::ChildProcessHost::kInvalidUniqueID, - MSG_ROUTING_NONE); - } - if (!task) - return; - task->OnNetworkBytesRead(recv_bytes); - task->OnNetworkBytesSent(sent_bytes); -} - void TaskManagerImpl::TaskAdded(Task* task) { DCHECK(task); @@ -529,6 +516,26 @@ NotifyObserversOnTaskUnresponsive(task->task_id()); } +void TaskManagerImpl::UpdateAccumulatedStatsNetworkForRoute( + int process_id, + int route_id, + int64_t recv_bytes, + int64_t sent_bytes) { + DCHECK_CURRENTLY_ON(content::BrowserThread::UI); + if (!is_running_) + return; + Task* task = GetTaskByRoute(process_id, route_id); + if (!task) { + // Orphaned/unaccounted activity is credited to the Browser process. + task = GetTaskByRoute(content::ChildProcessHost::kInvalidUniqueID, + MSG_ROUTING_NONE); + } + if (!task) + return; + task->OnNetworkBytesRead(recv_bytes); + task->OnNetworkBytesSent(sent_bytes); +} + void TaskManagerImpl::OnVideoMemoryUsageStatsUpdate( const gpu::VideoMemoryUsageStats& gpu_memory_stats) { DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
diff --git a/chrome/browser/task_manager/sampling/task_manager_impl.h b/chrome/browser/task_manager/sampling/task_manager_impl.h index 7ed26c3e..38f5db4 100644 --- a/chrome/browser/task_manager/sampling/task_manager_impl.h +++ b/chrome/browser/task_manager/sampling/task_manager_impl.h
@@ -42,6 +42,7 @@ ~TaskManagerImpl() override; static TaskManagerImpl* GetInstance(); + static bool IsCreated(); // task_manager::TaskManagerInterface: void ActivateTask(TaskId task_id) override; @@ -93,16 +94,17 @@ bool IsRunningInVM(TaskId task_id) const override; TaskId GetTaskIdForWebContents( content::WebContents* web_contents) const override; - void UpdateAccumulatedStatsNetworkForRoute(int process_id, - int route_id, - int64_t recv_bytes, - int64_t sent_bytes) override; // task_manager::TaskProviderObserver: void TaskAdded(Task* task) override; void TaskRemoved(Task* task) override; void TaskUnresponsive(Task* task) override; + void UpdateAccumulatedStatsNetworkForRoute(int process_id, + int route_id, + int64_t recv_bytes, + int64_t sent_bytes); + private: using PidToTaskGroupMap = std::map<base::ProcessId, std::unique_ptr<TaskGroup>>;
diff --git a/chrome/browser/task_manager/task_manager_interface.cc b/chrome/browser/task_manager/task_manager_interface.cc index b22f3f9..044443c 100644 --- a/chrome/browser/task_manager/task_manager_interface.cc +++ b/chrome/browser/task_manager/task_manager_interface.cc
@@ -40,6 +40,18 @@ return TaskManagerImpl::GetInstance(); } +void TaskManagerInterface::UpdateAccumulatedStatsNetworkForRoute( + int process_id, + int route_id, + int64_t recv_bytes, + int64_t sent_bytes) { + // Don't create a task manager if it hasn't already been created. + if (TaskManagerImpl::IsCreated()) { + TaskManagerImpl::GetInstance()->UpdateAccumulatedStatsNetworkForRoute( + process_id, route_id, recv_bytes, sent_bytes); + } +} + void TaskManagerInterface::AddObserver(TaskManagerObserver* observer) { observers_.AddObserver(observer); observer->observed_task_manager_ = this;
diff --git a/chrome/browser/task_manager/task_manager_interface.h b/chrome/browser/task_manager/task_manager_interface.h index b65e00a..8859bd9 100644 --- a/chrome/browser/task_manager/task_manager_interface.h +++ b/chrome/browser/task_manager/task_manager_interface.h
@@ -45,6 +45,14 @@ // create it first. Must be called on the UI thread. static TaskManagerInterface* GetTaskManager(); + // Update the accumulated network stats with additional data sent/received + // for a route described by |process_id| and |route_id|. If the associated + // task cannot be found it will be attributed to the browser process task. + static void UpdateAccumulatedStatsNetworkForRoute(int process_id, + int route_id, + int64_t recv_bytes, + int64_t sent_bytes); + void AddObserver(TaskManagerObserver* observer); void RemoveObserver(TaskManagerObserver* observer); @@ -224,14 +232,6 @@ virtual TaskId GetTaskIdForWebContents( content::WebContents* web_contents) const = 0; - // Update the accumulated network stats with additional data sent/received - // for a route described by |process_id| and |route_id|. If the associated - // task cannot be found it will be attributed to the browser process task. - virtual void UpdateAccumulatedStatsNetworkForRoute(int process_id, - int route_id, - int64_t recv_bytes, - int64_t sent_bytes) = 0; - // Returns true if the resource |type| usage calculation is enabled and // the implementation should refresh its value (this means that at least one // of the observers require this value). False otherwise.
diff --git a/chrome/browser/task_manager/test_task_manager.cc b/chrome/browser/task_manager/test_task_manager.cc index 3523dd1..76860a93 100644 --- a/chrome/browser/task_manager/test_task_manager.cc +++ b/chrome/browser/task_manager/test_task_manager.cc
@@ -182,12 +182,6 @@ return -1; } -void TestTaskManager::UpdateAccumulatedStatsNetworkForRoute( - int process_id, - int route_id, - int64_t recv_bytes, - int64_t sent_bytes) {} - base::TimeDelta TestTaskManager::GetRefreshTime() { return GetCurrentRefreshTime(); }
diff --git a/chrome/browser/task_manager/test_task_manager.h b/chrome/browser/task_manager/test_task_manager.h index 5aa17ab1..87ca264 100644 --- a/chrome/browser/task_manager/test_task_manager.h +++ b/chrome/browser/task_manager/test_task_manager.h
@@ -72,10 +72,6 @@ bool IsRunningInVM(TaskId task_id) const override; TaskId GetTaskIdForWebContents( content::WebContents* web_contents) const override; - void UpdateAccumulatedStatsNetworkForRoute(int process_id, - int route_id, - int64_t recv_bytes, - int64_t sent_bytes) override; base::TimeDelta GetRefreshTime(); int64_t GetEnabledFlags();
diff --git a/chrome/browser/ui/android/appmenu/internal/java/src/org/chromium/chrome/browser/ui/appmenu/AppMenu.java b/chrome/browser/ui/android/appmenu/internal/java/src/org/chromium/chrome/browser/ui/appmenu/AppMenu.java index 799252a..3cf99f06 100644 --- a/chrome/browser/ui/android/appmenu/internal/java/src/org/chromium/chrome/browser/ui/appmenu/AppMenu.java +++ b/chrome/browser/ui/android/appmenu/internal/java/src/org/chromium/chrome/browser/ui/appmenu/AppMenu.java
@@ -15,8 +15,6 @@ import android.os.Build; import android.os.SystemClock; import android.text.TextUtils; -import android.text.format.DateUtils; -import android.util.Pair; import android.view.Gravity; import android.view.KeyEvent; import android.view.LayoutInflater; @@ -46,10 +44,8 @@ import org.chromium.components.browser_ui.widget.highlight.ViewHighlighter; import org.chromium.ui.widget.Toast; -import java.util.ArrayDeque; import java.util.ArrayList; import java.util.List; -import java.util.Queue; /** * Shows a popup of menuitems anchored to a host view. When a item is selected we call @@ -59,8 +55,6 @@ */ class AppMenu implements OnItemClickListener, OnKeyListener, AppMenuClickHandler { private static final float LAST_ITEM_SHOW_FRACTION = 0.5f; - @VisibleForTesting - static final long RECENT_SELECTED_MENUITEM_EXPIRATION_MS = 10 * DateUtils.SECOND_IN_MILLIS; private final Menu mMenu; private final int mItemRowHeight; @@ -73,8 +67,7 @@ private PopupWindow mPopup; private ListView mListView; private AppMenuAdapter mAdapter; - @VisibleForTesting - AppMenuHandlerImpl mHandler; + private AppMenuHandlerImpl mHandler; private View mFooterView; private int mCurrentScreenRotation = -1; private boolean mIsByPermanentButton; @@ -82,9 +75,6 @@ private long mMenuShownTimeMs; private boolean mSelectedItemBeforeDismiss; - // Selected menu item id and the timestamp. - private final Queue<Pair<Integer, Long>> mRecentSelectedMenuItems = new ArrayDeque<>(); - /** * Creates and sets up the App Menu. * @param menu Original menu created by the framework. @@ -364,7 +354,6 @@ @Override public void onItemClick(MenuItem menuItem) { if (menuItem.isEnabled()) { - recordSelectedMenuItem(menuItem.getItemId(), SystemClock.elapsedRealtime()); mSelectedItemBeforeDismiss = true; dismiss(); mHandler.onOptionsItemSelected(menuItem); @@ -606,27 +595,4 @@ } return mItemRowHeight; } - - @VisibleForTesting - void recordSelectedMenuItem(int menuItemId, long timestamp) { - // Remove the selected MenuItems older than RECENT_SELECTED_MENUITEM_EXPIRATION_MS. - while (!mRecentSelectedMenuItems.isEmpty() - && (timestamp - mRecentSelectedMenuItems.peek().second - > RECENT_SELECTED_MENUITEM_EXPIRATION_MS)) { - mRecentSelectedMenuItems.remove(); - } - recordSelectionSequence(menuItemId); - - mRecentSelectedMenuItems.add(new Pair<Integer, Long>(menuItemId, timestamp)); - } - - private void recordSelectionSequence(int menuItemId) { - for (Pair<Integer, Long> previousSelectedMenuItem : mRecentSelectedMenuItems) { - if (mHandler.recordAppMenuSimilarSelectionIfNeeded( - previousSelectedMenuItem.first, menuItemId)) { - // Only record the similar selection once for one user action. - return; - } - } - } }
diff --git a/chrome/browser/ui/android/appmenu/internal/java/src/org/chromium/chrome/browser/ui/appmenu/AppMenuHandlerImpl.java b/chrome/browser/ui/android/appmenu/internal/java/src/org/chromium/chrome/browser/ui/appmenu/AppMenuHandlerImpl.java index 64c95890..e804da7 100644 --- a/chrome/browser/ui/android/appmenu/internal/java/src/org/chromium/chrome/browser/ui/appmenu/AppMenuHandlerImpl.java +++ b/chrome/browser/ui/android/appmenu/internal/java/src/org/chromium/chrome/browser/ui/appmenu/AppMenuHandlerImpl.java
@@ -349,16 +349,4 @@ AppMenuPropertiesDelegate getDelegateForTests() { return mDelegate; } - - /** - * Record the user selections if users make selected similar MenuItems. - * - * @param previousMenuItemId The previous selected MenuItem Id. - * @param currentMenuItemId The current selected MenuItem Id. - * @return Whether the selections is recorded. - */ - boolean recordAppMenuSimilarSelectionIfNeeded(int previousMenuItemId, int currentMenuItemId) { - return mDelegate.recordAppMenuSimilarSelectionIfNeeded( - previousMenuItemId, currentMenuItemId); - } }
diff --git a/chrome/browser/ui/android/appmenu/internal/java/src/org/chromium/chrome/browser/ui/appmenu/AppMenuTest.java b/chrome/browser/ui/android/appmenu/internal/java/src/org/chromium/chrome/browser/ui/appmenu/AppMenuTest.java index aeaa0518..79cd08b 100644 --- a/chrome/browser/ui/android/appmenu/internal/java/src/org/chromium/chrome/browser/ui/appmenu/AppMenuTest.java +++ b/chrome/browser/ui/android/appmenu/internal/java/src/org/chromium/chrome/browser/ui/appmenu/AppMenuTest.java
@@ -4,8 +4,6 @@ package org.chromium.chrome.browser.ui.appmenu; -import static org.mockito.ArgumentMatchers.anyInt; - import android.graphics.Rect; import android.view.KeyEvent; import android.view.Menu; @@ -32,6 +30,7 @@ import org.chromium.base.test.util.CommandLineFlags; import org.chromium.base.test.util.CriteriaHelper; import org.chromium.base.test.util.DisableIf; +import org.chromium.base.test.util.DisabledTest; import org.chromium.chrome.browser.flags.ChromeSwitches; import org.chromium.chrome.browser.lifecycle.ActivityLifecycleDispatcher; import org.chromium.chrome.browser.lifecycle.LifecycleObserver; @@ -604,7 +603,8 @@ @Test @MediumTest @DisableIf.Device(type = {UiDisableIf.TABLET}) - public void testDragHelper_ClickItem_Disabled() throws Exception { + @DisabledTest(message = "crbug.com/1186468") + public void testDragHelper_ClickItem() throws Exception { AppMenuButtonHelperImpl buttonHelper = (AppMenuButtonHelperImpl) mAppMenuHandler.createAppMenuButtonHelper(); @@ -767,30 +767,6 @@ Assert.assertEquals(15, height); } - @Test - @SmallTest - public void testRecordSelectedMenuItem() throws TimeoutException { - showMenuAndAssert(); - AppMenu appMenu = mAppMenuHandler.getAppMenu(); - AppMenuHandlerImpl spiedHandler = Mockito.spy(mAppMenuHandler); - appMenu.mHandler = spiedHandler; - - appMenu.recordSelectedMenuItem(R.id.menu_item_one, 1); - Mockito.verify(spiedHandler, Mockito.times(0)) - .recordAppMenuSimilarSelectionIfNeeded(anyInt(), anyInt()); - - appMenu.recordSelectedMenuItem(R.id.menu_item_two, 2); - Mockito.verify(spiedHandler, Mockito.times(1)) - .recordAppMenuSimilarSelectionIfNeeded(R.id.menu_item_one, R.id.menu_item_two); - - appMenu.recordSelectedMenuItem( - R.id.menu_item_three, AppMenu.RECENT_SELECTED_MENUITEM_EXPIRATION_MS + 3); - Mockito.verify(spiedHandler, Mockito.times(0)) - .recordAppMenuSimilarSelectionIfNeeded(R.id.menu_item_one, R.id.menu_item_three); - Mockito.verify(spiedHandler, Mockito.times(0)) - .recordAppMenuSimilarSelectionIfNeeded(R.id.menu_item_two, R.id.menu_item_three); - } - private void createMenuItem( List<MenuItem> menuItems, List<Integer> heightList, int id, int height) { Menu menu = mAppMenuHandler.getAppMenu().getMenu();
diff --git a/chrome/browser/ui/android/appmenu/internal/java/src/org/chromium/chrome/browser/ui/appmenu/TestAppMenuPropertiesDelegate.java b/chrome/browser/ui/android/appmenu/internal/java/src/org/chromium/chrome/browser/ui/appmenu/TestAppMenuPropertiesDelegate.java index 417b65df9..0300bdf 100644 --- a/chrome/browser/ui/android/appmenu/internal/java/src/org/chromium/chrome/browser/ui/appmenu/TestAppMenuPropertiesDelegate.java +++ b/chrome/browser/ui/android/appmenu/internal/java/src/org/chromium/chrome/browser/ui/appmenu/TestAppMenuPropertiesDelegate.java
@@ -27,7 +27,6 @@ public int groupDividerId; public boolean enableAppIconRow; public boolean iconBeforeItem; - public boolean recordAppMenuSimilarSelection; @Override public void destroy() {} @@ -115,10 +114,4 @@ public boolean shouldShowIconBeforeItem() { return iconBeforeItem; } - - @Override - public boolean recordAppMenuSimilarSelectionIfNeeded( - int previousMenuItemId, int currentMenuItemId) { - return recordAppMenuSimilarSelection; - } }
diff --git a/chrome/browser/ui/android/appmenu/java/src/org/chromium/chrome/browser/ui/appmenu/AppMenuPropertiesDelegate.java b/chrome/browser/ui/android/appmenu/java/src/org/chromium/chrome/browser/ui/appmenu/AppMenuPropertiesDelegate.java index 8a92f3c..cd24d38 100644 --- a/chrome/browser/ui/android/appmenu/java/src/org/chromium/chrome/browser/ui/appmenu/AppMenuPropertiesDelegate.java +++ b/chrome/browser/ui/android/appmenu/java/src/org/chromium/chrome/browser/ui/appmenu/AppMenuPropertiesDelegate.java
@@ -115,13 +115,4 @@ * should show the icon before the text. */ boolean shouldShowIconBeforeItem(); - - /** - * Record the user selections if users make selected similar MenuItems. - * - * @param previousMenuItemId The previous selected MenuItem Id. - * @param currentMenuItemId The current selected MenuItem Id. - * @return Whether the pattern is recorded. - */ - boolean recordAppMenuSimilarSelectionIfNeeded(int previousMenuItemId, int currentMenuItemId); }
diff --git a/chrome/browser/ui/ash/arc_custom_tab_modal_dialog_host.cc b/chrome/browser/ui/ash/arc_custom_tab_modal_dialog_host.cc index 32f5b1a..2c44518 100644 --- a/chrome/browser/ui/ash/arc_custom_tab_modal_dialog_host.cc +++ b/chrome/browser/ui/ash/arc_custom_tab_modal_dialog_host.cc
@@ -29,6 +29,9 @@ ArcCustomTabModalDialogHost::~ArcCustomTabModalDialogHost() { for (auto& observer : observer_list_) observer.OnHostDestroying(); + + // |web_contents_| is deleted by the base class, so there's no need to call + // WebContentsModalDialogManager::SetDelegate(nullptr) } void ArcCustomTabModalDialogHost::MainFrameWasResized(bool width_changed) {
diff --git a/chrome/browser/ui/ash/arc_custom_tab_modal_dialog_host.h b/chrome/browser/ui/ash/arc_custom_tab_modal_dialog_host.h index 4057248..d88cf78 100644 --- a/chrome/browser/ui/ash/arc_custom_tab_modal_dialog_host.h +++ b/chrome/browser/ui/ash/arc_custom_tab_modal_dialog_host.h
@@ -33,7 +33,6 @@ // Implements a WebContentsModalDialogHost for an ARC Custom Tab. This allows a // web contents modal dialog to be drawn in the ARC Custom Tab. -// The WebContents hosted by this object must outlive it. class ArcCustomTabModalDialogHost : public content::WebContentsObserver, public web_modal::WebContentsModalDialogHost,
diff --git a/chrome/browser/ui/aura/accessibility/automation_manager_aura.h b/chrome/browser/ui/aura/accessibility/automation_manager_aura.h index 73e2c81..48647d9 100644 --- a/chrome/browser/ui/aura/accessibility/automation_manager_aura.h +++ b/chrome/browser/ui/aura/accessibility/automation_manager_aura.h
@@ -85,6 +85,10 @@ cache_ = std::move(cache); } + // TODO(https://crbug.com/1185764): Figure out an appropriate design for + // Lacros a11y that does not require exposing the root tree id. + ui::AXTreeID get_root_tree_id_deprecated() { return tree_->tree_id(); } + private: friend class base::NoDestructor<AutomationManagerAura>;
diff --git a/chrome/browser/ui/browser.cc b/chrome/browser/ui/browser.cc index 19ca9ee..354072b 100644 --- a/chrome/browser/ui/browser.cc +++ b/chrome/browser/ui/browser.cc
@@ -2354,8 +2354,8 @@ // RenderWidgetHostView. RenderWidgetHostView* old_view = old_contents->GetMainFrame()->GetView(); RenderWidgetHostView* new_view = new_contents->GetMainFrame()->GetView(); - if (old_view && new_view && old_view->GetBackgroundColor()) - new_view->SetBackgroundColor(*old_view->GetBackgroundColor()); + if (old_view && new_view) + new_view->CopyBackgroundColorIfPresentFrom(*old_view); } #endif
diff --git a/chrome/browser/ui/search/ntp_user_data_logger.cc b/chrome/browser/ui/search/ntp_user_data_logger.cc index 10e963f..78b71e4 100644 --- a/chrome/browser/ui/search/ntp_user_data_logger.cc +++ b/chrome/browser/ui/search/ntp_user_data_logger.cc
@@ -368,7 +368,6 @@ NTPUserDataLogger::NTPUserDataLogger(Profile* profile, const GURL& ntp_url) : has_emitted_(false), should_record_doodle_load_time_(true), - modules_visible_(false), during_startup_(!AfterStartupTaskUtils::IsBrowserStartupComplete()), ntp_url_(ntp_url), profile_(profile) {} @@ -522,9 +521,6 @@ case NTP_CUSTOMIZE_SHORTCUT_VISIBILITY_TOGGLE_CLICKED: RecordAction(LoggingEventToShortcutUserActionName(event)); break; - case NTP_MODULES_SHOWN: - UMA_HISTOGRAM_LOAD_TIME("NewTabPage.Modules.ShownTime", time); - break; case NTP_APP_RENDERED: UMA_HISTOGRAM_LOAD_TIME("NewTabPage.MainUi.ShownTime", time); break; @@ -569,37 +565,6 @@ base::RecordAction(base::UserMetricsAction("MostVisited_Clicked")); } -void NTPUserDataLogger::LogModuleImpression(const std::string& id, - base::TimeDelta time) { - UMA_HISTOGRAM_LOAD_TIME("NewTabPage.Modules.Impression", time); - base::UmaHistogramCustomTimes("NewTabPage.Modules.Impression." + id, time, - base::TimeDelta::FromMilliseconds(1), - base::TimeDelta::FromSeconds(60), 100); -} - -void NTPUserDataLogger::LogModuleLoaded(const std::string& id, - base::TimeDelta duration, - base::TimeDelta time_since_navigation) { - UMA_HISTOGRAM_LOAD_TIME("NewTabPage.Modules.Loaded", time_since_navigation); - base::UmaHistogramCustomTimes("NewTabPage.Modules.Loaded." + id, - time_since_navigation, - base::TimeDelta::FromMilliseconds(1), - base::TimeDelta::FromSeconds(60), 100); - UMA_HISTOGRAM_LOAD_TIME("NewTabPage.Modules.LoadDuration", duration); - base::UmaHistogramCustomTimes("NewTabPage.Modules.LoadDuration." + id, - duration, base::TimeDelta::FromMilliseconds(1), - base::TimeDelta::FromSeconds(60), 100); -} - -void NTPUserDataLogger::LogModuleUsage(const std::string& id) { - UMA_HISTOGRAM_EXACT_LINEAR("NewTabPage.Modules.Usage", 1, 1); - base::UmaHistogramExactLinear("NewTabPage.Modules.Usage." + id, 1, 1); -} - -void NTPUserDataLogger::SetModulesVisible(bool visible) { - modules_visible_ = visible; -} - bool NTPUserDataLogger::DefaultSearchProviderIsGoogle() const { return search::DefaultSearchProviderIsGoogle(profile_); } @@ -700,11 +665,6 @@ } } - if (base::FeatureList::IsEnabled(ntp_features::kModules)) { - base::UmaHistogramBoolean("NewTabPage.Modules.VisibleOnNTPLoad", - modules_visible_); - } - has_emitted_ = true; during_startup_ = false; }
diff --git a/chrome/browser/ui/search/ntp_user_data_logger.h b/chrome/browser/ui/search/ntp_user_data_logger.h index db352a6..9501278 100644 --- a/chrome/browser/ui/search/ntp_user_data_logger.h +++ b/chrome/browser/ui/search/ntp_user_data_logger.h
@@ -40,18 +40,6 @@ // all others require Google as the default search provider. void LogEvent(NTPLoggingEventType event, base::TimeDelta time); - // Logs a module impression. Called when a module is loaded and can be seen by - // the user (scrolled into view). - void LogModuleImpression(const std::string& id, base::TimeDelta time); - - // Logs a module is loaded on the NTP. - void LogModuleLoaded(const std::string& id, - base::TimeDelta duration, - base::TimeDelta time_since_navigation); - - // Logs when a user interacts with a module which will result in a navigation. - void LogModuleUsage(const std::string& id); - // Called when a search suggestion event occurs on the NTP that has an integer // value associated with it; N suggestions were shown on this NTP load, the // Nth suggestion was clicked, etc. |time| is the delta time from navigation @@ -67,9 +55,6 @@ // Logs a navigation on one of the NTP tiles by a given impression. void LogMostVisitedNavigation(const ntp_tiles::NTPTileImpression& impression); - // Sets visibility of modules to be later logged. - void SetModulesVisible(bool visible); - private: // Returns whether Google is selected as the default search engine. Virtual // for testing. @@ -116,8 +101,6 @@ bool should_record_doodle_load_time_; - bool modules_visible_; - // Are stats being logged during Chrome startup? bool during_startup_;
diff --git a/chrome/browser/ui/startup/startup_browser_creator.cc b/chrome/browser/ui/startup/startup_browser_creator.cc index e9781479..bfb6024 100644 --- a/chrome/browser/ui/startup/startup_browser_creator.cc +++ b/chrome/browser/ui/startup/startup_browser_creator.cc
@@ -819,7 +819,7 @@ silent_launch = true; } -#if !BUILDFLAG(IS_CHROMEOS_ASH) && !BUILDFLAG(IS_CHROMEOS_LACROS) +#if !BUILDFLAG(IS_CHROMEOS_ASH) if (base::FeatureList::IsEnabled(features::kOnConnectNative) && command_line.HasSwitch(switches::kNativeMessagingConnectHost) && command_line.HasSwitch(switches::kNativeMessagingConnectExtension)) {
diff --git a/chrome/browser/ui/webui/autofill_and_password_manager_internals/internals_ui_handler.cc b/chrome/browser/ui/webui/autofill_and_password_manager_internals/internals_ui_handler.cc index e617165..ed72eda 100644 --- a/chrome/browser/ui/webui/autofill_and_password_manager_internals/internals_ui_handler.cc +++ b/chrome/browser/ui/webui/autofill_and_password_manager_internals/internals_ui_handler.cc
@@ -39,7 +39,8 @@ source->AddString(version_ui::kOfficial, version_info::IsOfficialBuild() ? "official" : "Developer build"); - source->AddString(version_ui::kVersionModifier, chrome::GetChannelName()); + source->AddString(version_ui::kVersionModifier, + chrome::GetChannelName(chrome::WithExtendedStable(true))); source->AddString(version_ui::kCL, version_info::GetLastChange()); source->AddString(version_ui::kUserAgent, embedder_support::GetUserAgent()); source->AddString("app_locale", g_browser_process->GetApplicationLocale());
diff --git a/chrome/browser/ui/webui/management/management_ui.cc b/chrome/browser/ui/webui/management/management_ui.cc index e00fd8c9..19c4f9c 100644 --- a/chrome/browser/ui/webui/management/management_ui.cc +++ b/chrome/browser/ui/webui/management/management_ui.cc
@@ -66,6 +66,7 @@ {kManagementReportAppInfoAndActivity, IDS_MANAGEMENT_REPORT_APP_INFO_AND_ACTIVITY}, {kManagementPrinting, IDS_MANAGEMENT_REPORT_PRINTING}, + {kManagementReportPrintJobs, IDS_MANAGEMENT_REPORT_PRINT_JOBS}, {kManagementCrostini, IDS_MANAGEMENT_CROSTINI}, {kManagementCrostiniContainerConfiguration, IDS_MANAGEMENT_CROSTINI_CONTAINER_CONFIGURATION},
diff --git a/chrome/browser/ui/webui/management/management_ui_handler.cc b/chrome/browser/ui/webui/management/management_ui_handler.cc index e91b74e..2df1d67 100644 --- a/chrome/browser/ui/webui/management/management_ui_handler.cc +++ b/chrome/browser/ui/webui/management/management_ui_handler.cc
@@ -167,6 +167,7 @@ const char kManagementReportExtensions[] = "managementReportExtensions"; const char kManagementReportAndroidApplications[] = "managementReportAndroidApplications"; +const char kManagementReportPrintJobs[] = "managementReportPrintJobs"; const char kManagementPrinting[] = "managementPrinting"; const char kManagementCrostini[] = "managementCrostini"; const char kManagementCrostiniContainerConfiguration[] = @@ -210,6 +211,7 @@ kAppInfoAndActivity, kLogs, kPrint, + kPrintJobs, kCrostini, kUsername, kExtensions, @@ -235,6 +237,8 @@ return "logs"; case DeviceReportingType::kPrint: return "print"; + case DeviceReportingType::kPrintJobs: + return "print jobs"; case DeviceReportingType::kCrostini: return "crostini"; case DeviceReportingType::kUsername: @@ -583,8 +587,17 @@ DeviceReportingType::kLogs); } - if (profile->GetPrefs()->GetBoolean( - prefs::kPrintingSendUsernameAndFilenameEnabled)) { + bool report_print_jobs = false; + chromeos::CrosSettings::Get()->GetBoolean(chromeos::kReportDevicePrintJobs, + &report_print_jobs); + if (report_print_jobs) { + AddDeviceReportingElement(report_sources, kManagementReportPrintJobs, + DeviceReportingType::kPrintJobs); + } + + bool report_print_username = profile->GetPrefs()->GetBoolean( + prefs::kPrintingSendUsernameAndFilenameEnabled); + if (report_print_username && !report_print_jobs) { AddDeviceReportingElement(report_sources, kManagementPrinting, DeviceReportingType::kPrint); }
diff --git a/chrome/browser/ui/webui/management/management_ui_handler.h b/chrome/browser/ui/webui/management/management_ui_handler.h index 781778e..a3bea9f 100644 --- a/chrome/browser/ui/webui/management/management_ui_handler.h +++ b/chrome/browser/ui/webui/management/management_ui_handler.h
@@ -34,6 +34,7 @@ extern const char kManagementReportUsers[]; extern const char kManagementReportCrashReports[]; extern const char kManagementReportAppInfoAndActivity[]; +extern const char kManagementReportPrintJobs[]; extern const char kManagementPrinting[]; extern const char kManagementCrostini[]; extern const char kManagementCrostiniContainerConfiguration[];
diff --git a/chrome/browser/ui/webui/memories/memories_ui.cc b/chrome/browser/ui/webui/memories/memories_ui.cc index 91857ef..9879cf20 100644 --- a/chrome/browser/ui/webui/memories/memories_ui.cc +++ b/chrome/browser/ui/webui/memories/memories_ui.cc
@@ -35,11 +35,19 @@ // TODO(crbug.com/1173908): Replace these with localized strings. source->AddString("memoryTitleDescription", base::UTF8ToUTF16("Based on previous web activity")); + source->AddString("topVisitsSectionHeader", + base::UTF8ToUTF16("From Chrome History")); webui::SetupWebUIDataSource( source, base::make_span(kMemoriesResources, kMemoriesResourcesSize), IDR_MEMORIES_MEMORIES_HTML); + content::URLDataSource::Add(profile, + std::make_unique<SanitizedImageSource>(profile)); + content::URLDataSource::Add( + profile, std::make_unique<FaviconSource>( + profile, chrome::FaviconUrlFormat::kFavicon2)); + return source; }
diff --git a/chrome/browser/ui/webui/nacl_ui.cc b/chrome/browser/ui/webui/nacl_ui.cc index d7a26e0..c684be3 100644 --- a/chrome/browser/ui/webui/nacl_ui.cc +++ b/chrome/browser/ui/webui/nacl_ui.cc
@@ -187,8 +187,9 @@ void NaClDomHandler::AddOperatingSystemInfo(base::ListValue* list) { // Obtain the Chrome version info. AddPair(list, l10n_util::GetStringUTF16(IDS_PRODUCT_NAME), - ASCIIToUTF16(version_info::GetVersionNumber() + " (" + - chrome::GetChannelName() + ")")); + ASCIIToUTF16( + version_info::GetVersionNumber() + " (" + + chrome::GetChannelName(chrome::WithExtendedStable(true)) + ")")); // OS version information. // TODO(jvoung): refactor this to share the extra windows labeling
diff --git a/chrome/browser/ui/webui/new_tab_page/new_tab_page.mojom b/chrome/browser/ui/webui/new_tab_page/new_tab_page.mojom index 1783caf2..3aed7f8 100644 --- a/chrome/browser/ui/webui/new_tab_page/new_tab_page.mojom +++ b/chrome/browser/ui/webui/new_tab_page/new_tab_page.mojom
@@ -374,17 +374,6 @@ OnVoiceSearchAction(VoiceSearchAction action); // Logs an error occurred while using voice search. OnVoiceSearchError(VoiceSearchError error); - // Logs a module impression. Called when a module is loaded and can be seen by - // the user (scrolled into view). - OnModuleImpression(string module_id, double time); - // Logs that a module loaded data for |duration| and finished successfully at - // |time|. - OnModuleLoaded(string module_id, mojo_base.mojom.TimeDelta duration, - double time); - // Logs when a user interacts with a module which will result in a navigation. - OnModuleUsage(string module_id); - // Logs that the modules have been shown at |time|. - OnModulesRendered(double time); // Logs that the <ntp-app> element's |ready| callback method was called. OnAppRendered(double time); };
diff --git a/chrome/browser/ui/webui/new_tab_page/new_tab_page_handler.cc b/chrome/browser/ui/webui/new_tab_page/new_tab_page_handler.cc index 0ec186df..9233d02 100644 --- a/chrome/browser/ui/webui/new_tab_page/new_tab_page_handler.cc +++ b/chrome/browser/ui/webui/new_tab_page/new_tab_page_handler.cc
@@ -357,8 +357,6 @@ instant_service_->UpdateNtpTheme(); promo_service_observation_.Observe(promo_service_); one_google_bar_service_observation_.Observe(one_google_bar_service_); - logger_.SetModulesVisible( - profile_->GetPrefs()->GetBoolean(prefs::kNtpModulesVisible)); } NewTabPageHandler::~NewTabPageHandler() { @@ -966,29 +964,6 @@ LogEvent(event); } -void NewTabPageHandler::OnModuleImpression(const std::string& module_id, - double time) { - logger_.LogModuleImpression( - module_id, base::Time::FromJsTime(time) - ntp_navigation_start_time_); -} - -void NewTabPageHandler::OnModuleLoaded(const std::string& module_id, - base::TimeDelta duration, - double time) { - logger_.LogModuleLoaded( - module_id, base::Time::FromJsTime(time) - ntp_navigation_start_time_, - duration); -} - -void NewTabPageHandler::OnModuleUsage(const std::string& module_id) { - logger_.LogModuleUsage(module_id); -} - -void NewTabPageHandler::OnModulesRendered(double time) { - logger_.LogEvent(NTP_MODULES_SHOWN, - base::Time::FromJsTime(time) - ntp_navigation_start_time_); -} - void NewTabPageHandler::NtpThemeChanged(const NtpTheme& ntp_theme) { page_->SetTheme(MakeTheme(ntp_theme)); }
diff --git a/chrome/browser/ui/webui/new_tab_page/new_tab_page_handler.h b/chrome/browser/ui/webui/new_tab_page/new_tab_page_handler.h index 865b538..b6e8734d 100644 --- a/chrome/browser/ui/webui/new_tab_page/new_tab_page_handler.h +++ b/chrome/browser/ui/webui/new_tab_page/new_tab_page_handler.h
@@ -131,12 +131,6 @@ void OnVoiceSearchAction( new_tab_page::mojom::VoiceSearchAction action) override; void OnVoiceSearchError(new_tab_page::mojom::VoiceSearchError error) override; - void OnModuleImpression(const std::string& module_id, double time) override; - void OnModuleLoaded(const std::string& module_id, - base::TimeDelta duration, - double time) override; - void OnModuleUsage(const std::string& module_id) override; - void OnModulesRendered(double time) override; private: // InstantServiceObserver:
diff --git a/chrome/browser/ui/webui/new_tab_page/new_tab_page_ui.cc b/chrome/browser/ui/webui/new_tab_page/new_tab_page_ui.cc index 375fcec..e44c5cfe 100644 --- a/chrome/browser/ui/webui/new_tab_page/new_tab_page_ui.cc +++ b/chrome/browser/ui/webui/new_tab_page/new_tab_page_ui.cc
@@ -57,7 +57,9 @@ namespace { -content::WebUIDataSource* CreateNewTabPageUiHtmlSource(Profile* profile) { +content::WebUIDataSource* CreateNewTabPageUiHtmlSource( + Profile* profile, + const base::Time& navigation_start_time) { content::WebUIDataSource* source = content::WebUIDataSource::Create(chrome::kChromeUINewTabPageHost); @@ -70,6 +72,7 @@ ->search_terms_data() .GoogleBaseURLValue()) .spec()); + source->AddDouble("navigationStartTime", navigation_start_time.ToJsTime()); // Realbox. source->AddBoolean( @@ -322,7 +325,7 @@ // for the unlikely case where the NewTabPageHandler is created before we // received the DidStartNavigation event. navigation_start_time_(base::Time::Now()) { - auto* source = CreateNewTabPageUiHtmlSource(profile_); + auto* source = CreateNewTabPageUiHtmlSource(profile_, navigation_start_time_); source->AddBoolean("customBackgroundDisabledByPolicy", instant_service_->IsCustomBackgroundDisabledByPolicy()); source->AddBoolean( @@ -451,6 +454,10 @@ content::NavigationHandle* navigation_handle) { if (navigation_handle->IsInMainFrame()) { navigation_start_time_ = base::Time::Now(); + std::unique_ptr<base::DictionaryValue> update(new base::DictionaryValue); + update->SetDouble("navigationStartTime", navigation_start_time_.ToJsTime()); + content::WebUIDataSource::Update(profile_, chrome::kChromeUINewTabPageHost, + std::move(update)); } }
diff --git a/chrome/browser/ui/webui/policy/policy_ui_handler.cc b/chrome/browser/ui/webui/policy/policy_ui_handler.cc index 19333765..373e1d1b 100644 --- a/chrome/browser/ui/webui/policy/policy_ui_handler.cc +++ b/chrome/browser/ui/webui/policy/policy_ui_handler.cc
@@ -1247,7 +1247,8 @@ " %s", base::UTF16ToUTF8(cohort_version_info).c_str()); } #endif - std::string channel_name = chrome::GetChannelName(); + std::string channel_name = + chrome::GetChannelName(chrome::WithExtendedStable(true)); std::string version = base::StringPrintf( "%s (%s)%s %s%s", version_info::GetVersionNumber().c_str(), l10n_util::GetStringUTF8(version_info::IsOfficialBuild()
diff --git a/chrome/browser/ui/webui/sanitized_image_source.cc b/chrome/browser/ui/webui/sanitized_image_source.cc index c3873e6..ba7aa5d 100644 --- a/chrome/browser/ui/webui/sanitized_image_source.cc +++ b/chrome/browser/ui/webui/sanitized_image_source.cc
@@ -97,6 +97,12 @@ return "image/png"; } +bool SanitizedImageSource::ShouldReplaceExistingSource() { + // Leave the existing DataSource in place, otherwise we'll drop any pending + // requests on the floor. + return false; +} + void SanitizedImageSource::OnImageLoaded( network::SimpleURLLoader* loader, content::URLDataSource::GotDataCallback callback,
diff --git a/chrome/browser/ui/webui/sanitized_image_source.h b/chrome/browser/ui/webui/sanitized_image_source.h index 6a873acc..e07120d 100644 --- a/chrome/browser/ui/webui/sanitized_image_source.h +++ b/chrome/browser/ui/webui/sanitized_image_source.h
@@ -61,6 +61,7 @@ const content::WebContents::Getter& wc_getter, content::URLDataSource::GotDataCallback callback) override; std::string GetMimeType(const std::string& path) override; + bool ShouldReplaceExistingSource() override; private: void OnImageLoaded(network::SimpleURLLoader* loader,
diff --git a/chrome/browser/ui/webui/settings/safety_check_handler.cc b/chrome/browser/ui/webui/settings/safety_check_handler.cc index 02e5b65c..b87fd1d 100644 --- a/chrome/browser/ui/webui/settings/safety_check_handler.cc +++ b/chrome/browser/ui/webui/settings/safety_check_handler.cc
@@ -599,7 +599,8 @@ l10n_util::GetStringUTF16(version_info::IsOfficialBuild() ? IDS_VERSION_UI_OFFICIAL : IDS_VERSION_UI_UNOFFICIAL), - base::UTF8ToUTF16(chrome::GetChannelName()), + base::UTF8ToUTF16( + chrome::GetChannelName(chrome::WithExtendedStable(true))), l10n_util::GetStringUTF16(VersionUI::VersionProcessorVariation())); // This state is only used on Android for recording metrics. This codepath // is unreachable.
diff --git a/chrome/browser/ui/webui/settings/safety_check_handler_unittest.cc b/chrome/browser/ui/webui/settings/safety_check_handler_unittest.cc index cccd942..4089c3e 100644 --- a/chrome/browser/ui/webui/settings/safety_check_handler_unittest.cc +++ b/chrome/browser/ui/webui/settings/safety_check_handler_unittest.cc
@@ -531,7 +531,9 @@ event, "Version " + version_info::GetVersionNumber() + " (" + (version_info::IsOfficialBuild() ? "Official Build" : "Developer Build") + - ") " + chrome::GetChannelName() + processor_variation); + ") " + + chrome::GetChannelName(chrome::WithExtendedStable(true)) + + processor_variation); histogram_tester_.ExpectBucketCount( "Settings.SafetyCheck.UpdatesResult", SafetyCheckHandler::UpdateStatus::kUnknown, 1);
diff --git a/chrome/browser/ui/webui/settings/settings_localized_strings_provider.cc b/chrome/browser/ui/webui/settings/settings_localized_strings_provider.cc index a990d7b..051c252 100644 --- a/chrome/browser/ui/webui/settings/settings_localized_strings_provider.cc +++ b/chrome/browser/ui/webui/settings/settings_localized_strings_provider.cc
@@ -280,7 +280,8 @@ l10n_util::GetStringUTF16(version_info::IsOfficialBuild() ? IDS_VERSION_UI_OFFICIAL : IDS_VERSION_UI_UNOFFICIAL), - base::UTF8ToUTF16(chrome::GetChannelName()), + base::UTF8ToUTF16( + chrome::GetChannelName(chrome::WithExtendedStable(true))), l10n_util::GetStringUTF16(VersionUI::VersionProcessorVariation()))); html_source->AddString( "aboutProductCopyright",
diff --git a/chrome/browser/ui/webui/version/version_ui.cc b/chrome/browser/ui/webui/version/version_ui.cc index a2d5348f..44a0003 100644 --- a/chrome/browser/ui/webui/version/version_ui.cc +++ b/chrome/browser/ui/webui/version/version_ui.cc
@@ -162,8 +162,9 @@ // Data strings. html_source->AddString(version_ui::kVersion, version_info::GetVersionNumber()); - html_source->AddString(version_ui::kVersionModifier, - chrome::GetChannelName()); + html_source->AddString( + version_ui::kVersionModifier, + chrome::GetChannelName(chrome::WithExtendedStable(true))); html_source->AddString(version_ui::kJSEngine, "V8"); html_source->AddString(version_ui::kJSVersion, V8_VERSION_STRING); html_source->AddString(
diff --git a/chrome/browser/video_tutorials/video_tutorial_service_factory.cc b/chrome/browser/video_tutorials/video_tutorial_service_factory.cc index bed153b1..2fcfd72 100644 --- a/chrome/browser/video_tutorials/video_tutorial_service_factory.cc +++ b/chrome/browser/video_tutorials/video_tutorial_service_factory.cc
@@ -83,7 +83,8 @@ SystemNetworkContextManager::GetInstance()->GetSharedURLLoaderFactory(); base::Version version = version_info::GetVersion(); - std::string channel_name = chrome::GetChannelName(); + std::string channel_name = + chrome::GetChannelName(chrome::WithExtendedStable(true)); std::string client_version = base::StringPrintf("%d.%d.%d.%s.chrome", version.components()[0], // Major
diff --git a/chrome/build/mac.pgo.txt b/chrome/build/mac.pgo.txt index 81d65be..1627d569 100644 --- a/chrome/build/mac.pgo.txt +++ b/chrome/build/mac.pgo.txt
@@ -1 +1 @@ -chrome-mac-master-1615895772-90c2bf8c2781a73f4a574078d5f63a9efff1fc46.profdata +chrome-mac-master-1615917424-c202f8fcc4f5829e9d5aef5ed69c9eb60ac98eed.profdata
diff --git a/chrome/common/search/ntp_logging_events.h b/chrome/common/search/ntp_logging_events.h index 4b1c909b..4db85c6 100644 --- a/chrome/common/search/ntp_logging_events.h +++ b/chrome/common/search/ntp_logging_events.h
@@ -23,6 +23,7 @@ // Deleted: NTP_MOUSEOVER = 9 // Deleted: NTP_TILE_LOADED = 10, // Deleted: NTP_ALL_TILES_RECEIVED = 12, + // Deleted: NTP_MODULES_SHOWN = 83, // All NTP tiles have finished loading (successfully or failing). Logged only // by the single-iframe version of the NTP. @@ -173,9 +174,6 @@ // Daily refresh was enabled by clicked 'Done' in the richer picker. NTP_BACKGROUND_DAILY_REFRESH_ENABLED = 82, - // The NTP modules were shown. - NTP_MODULES_SHOWN = 83, - // The NTP <ntp-app> element was created and ready() was called. NTP_APP_RENDERED = 84,
diff --git a/chrome/renderer/media/chrome_speech_recognition_client.cc b/chrome/renderer/media/chrome_speech_recognition_client.cc index cb356af..57d6e13 100644 --- a/chrome/renderer/media/chrome_speech_recognition_client.cc +++ b/chrome/renderer/media/chrome_speech_recognition_client.cc
@@ -166,6 +166,7 @@ void ChromeSpeechRecognitionClient::Reset() { is_recognizer_bound_ = false; + is_browser_requesting_transcription_ = true; speech_recognition_context_.reset(); speech_recognition_recognizer_.reset(); speech_recognition_client_receiver_.reset();
diff --git a/chrome/services/speech/soda/soda_client_unittest.cc b/chrome/services/speech/soda/soda_client_unittest.cc index 2fde2fd4..04af72f 100644 --- a/chrome/services/speech/soda/soda_client_unittest.cc +++ b/chrome/services/speech/soda/soda_client_unittest.cc
@@ -106,7 +106,8 @@ config_msg.set_language_pack_directory(config_file_path.value().c_str()); config_msg.set_simulate_realtime_testonly(false); config_msg.set_enable_lang_id(false); - config_msg.set_recognition_mode(soda::api::SerializedSodaConfigMsg::CAPTION); + config_msg.set_recognition_mode( + speech::soda::chrome::ExtendedSodaConfigMsg::CAPTION); // The test binary does not verify the execution context. config_msg.set_api_key("");
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn index 4f0238c7..0c51b3a 100644 --- a/chrome/test/BUILD.gn +++ b/chrome/test/BUILD.gn
@@ -2593,6 +2593,7 @@ "../browser/ash/login/test/user_adding_screen_utils.h", "../browser/ash/login/test/webview_content_extractor.cc", "../browser/ash/login/test/webview_content_extractor.h", + "../browser/ash/login/ui/captive_portal_dialog_delegate_browsertest.cc", "../browser/ash/login/ui/captive_portal_window_browsertest.cc", "../browser/ash/login/ui/login_feedback_browsertest.cc", "../browser/ash/login/ui/login_web_dialog_browsertest.cc", @@ -5640,20 +5641,15 @@ } else { sources += [ "../browser/extensions/api/enterprise_reporting_private/enterprise_reporting_private_unittest.cc", + "../browser/extensions/api/messaging/native_message_process_host_unittest.cc", "../browser/extensions/api/messaging/native_messaging_host_manifest_unittest.cc", + "../browser/extensions/api/messaging/native_messaging_launch_from_native_unittest.cc", "../browser/extensions/api/messaging/native_messaging_policy_handler_unittest.cc", ] deps += [ "//components/enterprise:test_support" ] } - if (!is_chromeos_ash && !is_chromeos_lacros) { - sources += [ - "../browser/extensions/api/messaging/native_message_process_host_unittest.cc", - "../browser/extensions/api/messaging/native_messaging_launch_from_native_unittest.cc", - ] - } } - if (use_aura) { deps += [ "//ui/aura:test_support",
diff --git a/chrome/test/data/extensions/api_test/notifications/api/basic_app/index.js b/chrome/test/data/extensions/api_test/notifications/api/basic_app/index.js index db2d7d8..ebac424c 100644 --- a/chrome/test/data/extensions/api_test/notifications/api/basic_app/index.js +++ b/chrome/test/data/extensions/api_test/notifications/api/basic_app/index.js
@@ -11,6 +11,7 @@ title: 'hi', message: 'there', type: 'basic', + appIconMaskUrl: redDot, buttons: [{title: 'Button'}, {title: 'Button'}] };
diff --git a/chrome/test/data/webui/cr_components/chromeos/cellular_setup/esim_flow_ui_test.js b/chrome/test/data/webui/cr_components/chromeos/cellular_setup/esim_flow_ui_test.js index 13735e9..b7f0a21 100644 --- a/chrome/test/data/webui/cr_components/chromeos/cellular_setup/esim_flow_ui_test.js +++ b/chrome/test/data/webui/cr_components/chromeos/cellular_setup/esim_flow_ui_test.js
@@ -274,7 +274,8 @@ assertActivationCodePage(/*forwardButtonShouldBeEnabled=*/ true); }); - test('Invalid activation code', async function() { + // TODO(crbug.com/1188665) Renable flaky test + test.skip('Invalid activation code', async function() { activationCodePage.barcodeDetectorClass_ = FakeBarcodeDetector; activationCodePage.initBarcodeDetector(); await flushAsync();
diff --git a/chrome/test/data/webui/js/icon_test.js b/chrome/test/data/webui/js/icon_test.js index 4281410..b94977e 100644 --- a/chrome/test/data/webui/js/icon_test.js +++ b/chrome/test/data/webui/js/icon_test.js
@@ -4,22 +4,26 @@ function testGetFaviconForPageURL() { var url = 'http://foo.com'; - var expectedDesktop = '-webkit-image-set(' + - 'url("chrome://favicon2/?size=16&scale_factor=1x&page_url=' + - encodeURIComponent('http://foo.com') + - '&allow_google_server_fallback=0") 1x, ' + - 'url("chrome://favicon2/?size=16&scale_factor=2x&page_url=' + - encodeURIComponent('http://foo.com') + - '&allow_google_server_fallback=0") 2x)'; - var expectedOther = '-webkit-image-set(' + - 'url("chrome://favicon2/?size=16&scale_factor=1x&page_url=' + - encodeURIComponent('http://foo.com') + - '&allow_google_server_fallback=0") ' + window.devicePixelRatio + 'x)'; + function getExpectedImageSet(size) { + var expectedDesktop = '-webkit-image-set(' + + `url("chrome://favicon2/?size=${size}&scale_factor=1x&page_url=` + + encodeURIComponent(url) + '&allow_google_server_fallback=0") 1x, ' + + `url("chrome://favicon2/?size=${size}&scale_factor=2x&page_url=` + + encodeURIComponent(url) + '&allow_google_server_fallback=0") 2x)'; + var expectedOther = '-webkit-image-set(' + + `url("chrome://favicon2/?size=${size}&scale_factor=1x&page_url=` + + encodeURIComponent(url) + '&allow_google_server_fallback=0") ' + + window.devicePixelRatio + 'x)'; - var isDesktop = - cr.isMac || cr.isChromeOS || cr.isWindows || cr.isLinux || cr.isLacros; - var expected = isDesktop ? expectedDesktop : expectedOther; - assertEquals(expected, cr.icon.getFaviconForPageURL(url)); + var isDesktop = + cr.isMac || cr.isChromeOS || cr.isWindows || cr.isLinux || cr.isLacros; + return isDesktop ? expectedDesktop : expectedOther; + } + + assertEquals(getExpectedImageSet(16), cr.icon.getFaviconForPageURL(url)); + assertEquals( + getExpectedImageSet(24), + cr.icon.getFaviconForPageURL(url, false, '', 24)); } function testGetFavicon() {
diff --git a/chrome/test/data/webui/new_tab_page/BUILD.gn b/chrome/test/data/webui/new_tab_page/BUILD.gn index 178f5394..abbccd1 100644 --- a/chrome/test/data/webui/new_tab_page/BUILD.gn +++ b/chrome/test/data/webui/new_tab_page/BUILD.gn
@@ -12,6 +12,7 @@ ] deps = [ ":img_test", + ":metrics_utils_test", "modules:module_header_test", ] } @@ -23,3 +24,11 @@ js_library("metrics_test_support") { } + +js_library("metrics_utils_test") { + deps = [ + ":metrics_test_support", + "//chrome/browser/resources/new_tab_page", + "//ui/webui/resources/js:load_time_data.m", + ] +}
diff --git a/chrome/test/data/webui/new_tab_page/app_test.js b/chrome/test/data/webui/new_tab_page/app_test.js index 73883187..a42201e 100644 --- a/chrome/test/data/webui/new_tab_page/app_test.js +++ b/chrome/test/data/webui/new_tab_page/app_test.js
@@ -6,7 +6,7 @@ import {isMac} from 'chrome://resources/js/cr.m.js'; import {loadTimeData} from 'chrome://resources/js/load_time_data.m.js'; import {PromiseResolver} from 'chrome://resources/js/promise_resolver.m.js'; -import {FakeMetricsPrivate} from 'chrome://test/new_tab_page/metrics_test_support.js'; +import {fakeMetricsPrivate, MetricsTracker} from 'chrome://test/new_tab_page/metrics_test_support.js'; import {assertNotStyle, assertStyle, createTestProxy, createTheme} from 'chrome://test/new_tab_page/test_support.js'; import {TestBrowserProxy} from 'chrome://test/test_browser_proxy.m.js'; import {eventToPromise, flushTasks} from 'chrome://test/test_util.m.js'; @@ -21,7 +21,7 @@ */ let testProxy; - /** @type {FakeMetricsPrivate} */ + /** @type {MetricsTracker} */ let metrics; /** @@ -68,8 +68,7 @@ moduleRegistry.setResultFor('getDescriptors', []); moduleRegistry.setResultFor('initializeModules', moduleResolver.promise); ModuleRegistry.instance_ = moduleRegistry; - metrics = new FakeMetricsPrivate(); - chrome.metricsPrivate = metrics; + metrics = fakeMetricsPrivate(); app = document.createElement('ntp-app'); document.body.appendChild(app); @@ -453,6 +452,13 @@ [true, false].forEach(visible => { test(`modules appended to page if visibility ${visible}`, async () => { + // Arrange. + loadTimeData.overrideValues({ + navigationStartTime: 0.0, + }); + testProxy.setResultFor('now', 123); + + // Act. moduleResolver.resolve([ { @@ -474,11 +480,14 @@ // Assert. const modules = app.shadowRoot.querySelectorAll('ntp-module-wrapper'); assertEquals(2, modules.length); - assertEquals(1, testProxy.handler.getCallCount('onModulesRendered')); + assertEquals(1, metrics.count('NewTabPage.Modules.ShownTime')); + assertEquals(1, metrics.count('NewTabPage.Modules.ShownTime', 123)); const histogram = 'NewTabPage.Modules.EnabledOnNTPLoad'; assertEquals(1, metrics.count(`${histogram}.foo`, visible)); assertEquals(1, metrics.count(`${histogram}.bar`, false)); assertEquals( + 1, metrics.count('NewTabPage.Modules.VisibleOnNTPLoad', visible)); + assertEquals( 1, testProxy.handler.getCallCount('updateDisabledModules')); }); });
diff --git a/chrome/test/data/webui/new_tab_page/customize_modules_test.js b/chrome/test/data/webui/new_tab_page/customize_modules_test.js index c5cdb3d..7bce7ad 100644 --- a/chrome/test/data/webui/new_tab_page/customize_modules_test.js +++ b/chrome/test/data/webui/new_tab_page/customize_modules_test.js
@@ -6,7 +6,7 @@ import {$$, BrowserProxy, ModuleDescriptor, ModuleRegistry} from 'chrome://new-tab-page/new_tab_page.js'; import {loadTimeData} from 'chrome://resources/js/load_time_data.m.js'; -import {FakeMetricsPrivate} from 'chrome://test/new_tab_page/metrics_test_support.js'; +import {fakeMetricsPrivate, MetricsTracker} from 'chrome://test/new_tab_page/metrics_test_support.js'; import {assertNotStyle, assertStyle, createTestProxy} from 'chrome://test/new_tab_page/test_support.js'; import {TestBrowserProxy} from 'chrome://test/test_browser_proxy.m.js'; @@ -32,7 +32,7 @@ */ let moduleRegistry; - /** @type {FakeMetricsPrivate} */ + /** @type {MetricsTracker} */ let metrics; /** @@ -61,8 +61,7 @@ BrowserProxy.instance_ = testProxy; moduleRegistry = TestBrowserProxy.fromClass(ModuleRegistry); ModuleRegistry.instance_ = moduleRegistry; - metrics = new FakeMetricsPrivate(); - chrome.metricsPrivate = metrics; + metrics = fakeMetricsPrivate(); loadTimeData.overrideValues({modulesVisibleManagedByPolicy: false}); });
diff --git a/chrome/test/data/webui/new_tab_page/metrics_test_support.js b/chrome/test/data/webui/new_tab_page/metrics_test_support.js index 059c3b3..3fc5692 100644 --- a/chrome/test/data/webui/new_tab_page/metrics_test_support.js +++ b/chrome/test/data/webui/new_tab_page/metrics_test_support.js
@@ -2,19 +2,16 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -/** - * A fake to intercept metrics logging calls and verify how many times they were - * called. - */ -export class FakeMetricsPrivate { +/** Tracks metrics calls to verify metric logging in tests. */ +export class MetricsTracker { constructor() { - /** @private {!Map<string, !Array>} */ + /** @private {!Map<string, !Array<*>>} */ this.histogramMap_ = new Map(); } /** * @param {string} metricName - * @param {string=} value + * @param {*=} value * @return {number} */ count(metricName, value) { @@ -23,30 +20,17 @@ .length; } - /** @param {string} metricName */ - recordUserAction(metricName) { - this.get_(metricName).push(0); - } - /** * @param {string} metricName - * @param {string} value + * @param {*} value */ - recordSparseHashable(metricName, value) { + record(metricName, value) { this.get_(metricName).push(value); } /** * @param {string} metricName - * @param {boolean} value - */ - recordBoolean(metricName, value) { - this.get_(metricName).push(value); - } - - /** - * @param {string} metricName - * @return {!Map<string, !Array>} + * @return {!Array<*>} * @private */ get_(metricName) { @@ -56,3 +40,17 @@ return this.histogramMap_.get(metricName); } } + +/** + * Installs interceptors to metrics logging calls and forwards them to the + * returned |MetricsTracker| object. + * @return {MetricsTracker} + */ +export function fakeMetricsPrivate() { + const metrics = new MetricsTracker(); + chrome.metricsPrivate.recordUserAction = (m) => metrics.record(m, 0); + chrome.metricsPrivate.recordSparseHashable = (m, v) => metrics.record(m, v); + chrome.metricsPrivate.recordBoolean = (m, v) => metrics.record(m, v); + chrome.metricsPrivate.recordValue = (m, v) => metrics.record(m.metricName, v); + return metrics; +}
diff --git a/chrome/test/data/webui/new_tab_page/metrics_utils_test.js b/chrome/test/data/webui/new_tab_page/metrics_utils_test.js new file mode 100644 index 0000000..0beab8e --- /dev/null +++ b/chrome/test/data/webui/new_tab_page/metrics_utils_test.js
@@ -0,0 +1,44 @@ +// Copyright 2021 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 {recordDuration, recordLoadDuration, recordPerdecage, recordOccurence} from 'chrome://new-tab-page/new_tab_page.js'; +import {fakeMetricsPrivate, MetricsTracker} from './metrics_test_support.js'; +import {assertEquals} from '../chai_assert.js'; +import {loadTimeData} from 'chrome://resources/js/load_time_data.m.js'; + +suite('NewTabPageMetricsUtilsTest', () => { + /** @type {MetricsTracker} */ + let metrics; + + setup(() => { + metrics = fakeMetricsPrivate(); + }); + + test('recordDuration', () => { + recordDuration('foo.metric', 123.0); + assertEquals(1, metrics.count('foo.metric')); + assertEquals(1, metrics.count('foo.metric', 123)); + }); + + test('recordLoadDuration', () => { + loadTimeData.overrideValues({ + navigationStartTime: 5.0, + }); + recordLoadDuration('foo.metric', 123.0); + assertEquals(1, metrics.count('foo.metric')); + assertEquals(1, metrics.count('foo.metric', 118)); + }); + + test('recordPerdecage', () => { + recordPerdecage('foo.metric', 5); + assertEquals(1, metrics.count('foo.metric')); + assertEquals(1, metrics.count('foo.metric', 5)); + }); + + test('recordOccurence', () => { + recordOccurence('foo.metric'); + assertEquals(1, metrics.count('foo.metric')); + assertEquals(1, metrics.count('foo.metric', 1)); + }); +});
diff --git a/chrome/test/data/webui/new_tab_page/modules/cart/module_test.js b/chrome/test/data/webui/new_tab_page/modules/cart/module_test.js index 1f7eae1f..154032f 100644 --- a/chrome/test/data/webui/new_tab_page/modules/cart/module_test.js +++ b/chrome/test/data/webui/new_tab_page/modules/cart/module_test.js
@@ -4,7 +4,7 @@ import {$$, chromeCartDescriptor, ChromeCartProxy} from 'chrome://new-tab-page/new_tab_page.js'; import {loadTimeData} from 'chrome://resources/js/load_time_data.m.js'; -import {FakeMetricsPrivate} from 'chrome://test/new_tab_page/metrics_test_support.js'; +import {fakeMetricsPrivate, MetricsTracker} from 'chrome://test/new_tab_page/metrics_test_support.js'; import {assertNotStyle, assertStyle} from 'chrome://test/new_tab_page/test_support.js'; import {TestBrowserProxy} from 'chrome://test/test_browser_proxy.m.js'; import {eventToPromise, flushTasks, isVisible} from 'chrome://test/test_util.m.js'; @@ -16,8 +16,8 @@ */ let testProxy; - /** @type {FakeMetricsPrivate} */ - let fakeMetricsPrivate; + /** @type {MetricsTracker} */ + let metrics; setup(() => { PolymerTest.clearBody(); @@ -26,8 +26,7 @@ testProxy.handler = TestBrowserProxy.fromClass(chromeCart.mojom.CartHandlerRemote); ChromeCartProxy.instance_ = testProxy; - fakeMetricsPrivate = new FakeMetricsPrivate(); - chrome.metricsPrivate = fakeMetricsPrivate; + metrics = fakeMetricsPrivate(); // Not show welcome surface by default. testProxy.handler.setResultFor( 'getWarmWelcomeVisible', Promise.resolve({visible: false})); @@ -204,15 +203,14 @@ loadTimeData.getString('modulesCartModuleMenuHideToastMessage'), hideToastMessage); assertEquals(1, testProxy.handler.getCallCount('hideCartModule')); - assertEquals(1, fakeMetricsPrivate.count('NewTabPage.Carts.HideModule')); + assertEquals(1, metrics.count('NewTabPage.Carts.HideModule')); // Act. hideRestoreCallback(); // Assert. assertEquals(1, testProxy.handler.getCallCount('restoreHiddenCartModule')); - assertEquals( - 1, fakeMetricsPrivate.count('NewTabPage.Carts.UndoHideModule')); + assertEquals(1, metrics.count('NewTabPage.Carts.UndoHideModule')); // Act. const waitForDisableEvent = eventToPromise('disable-module', moduleElement); @@ -224,14 +222,13 @@ // Assert. assertEquals('hello world', disableToastMessage); - assertEquals(1, fakeMetricsPrivate.count('NewTabPage.Carts.RemoveModule')); + assertEquals(1, metrics.count('NewTabPage.Carts.RemoveModule')); // Act. disableRestoreCallback(); // Assert. - assertEquals( - 1, fakeMetricsPrivate.count('NewTabPage.Carts.UndoRemoveModule')); + assertEquals(1, metrics.count('NewTabPage.Carts.UndoRemoveModule')); }); test('dismiss and undo single cart item in module', async () => { @@ -382,8 +379,7 @@ // Assert. checkScrollButtonVisibility(moduleElement, true, true); checkVisibleRange(moduleElement, 4, 7); - assertEquals( - 1, fakeMetricsPrivate.count('NewTabPage.Carts.RightScrollClick')); + assertEquals(1, metrics.count('NewTabPage.Carts.RightScrollClick')); // Act. waitForRightScrollVisibilityChange = @@ -396,8 +392,7 @@ // Assert. checkScrollButtonVisibility(moduleElement, true, false); checkVisibleRange(moduleElement, 6, 9); - assertEquals( - 2, fakeMetricsPrivate.count('NewTabPage.Carts.RightScrollClick')); + assertEquals(2, metrics.count('NewTabPage.Carts.RightScrollClick')); // Act. waitForRightScrollVisibilityChange = @@ -410,8 +405,7 @@ // Assert. checkScrollButtonVisibility(moduleElement, true, true); checkVisibleRange(moduleElement, 2, 5); - assertEquals( - 1, fakeMetricsPrivate.count('NewTabPage.Carts.LeftScrollClick')); + assertEquals(1, metrics.count('NewTabPage.Carts.LeftScrollClick')); // Act. waitForLeftScrollVisibilityChange = @@ -424,8 +418,7 @@ // Assert. checkScrollButtonVisibility(moduleElement, false, true); checkVisibleRange(moduleElement, 0, 3); - assertEquals( - 2, fakeMetricsPrivate.count('NewTabPage.Carts.LeftScrollClick')); + assertEquals(2, metrics.count('NewTabPage.Carts.LeftScrollClick')); // Remove the observer. cartCarousel.removeEventListener('scroll', onScroll);
diff --git a/chrome/test/data/webui/new_tab_page/modules/module_descriptor_test.js b/chrome/test/data/webui/new_tab_page/modules/module_descriptor_test.js index 14c0966..2b812d9 100644 --- a/chrome/test/data/webui/new_tab_page/modules/module_descriptor_test.js +++ b/chrome/test/data/webui/new_tab_page/modules/module_descriptor_test.js
@@ -3,6 +3,8 @@ // found in the LICENSE file. import {BrowserProxy, ModuleDescriptor} from 'chrome://new-tab-page/new_tab_page.js'; +import {loadTimeData} from 'chrome://resources/js/load_time_data.m.js'; +import {fakeMetricsPrivate, MetricsTracker} from 'chrome://test/new_tab_page/metrics_test_support.js'; import {createTestProxy} from 'chrome://test/new_tab_page/test_support.js'; suite('NewTabPageModulesModuleDescriptorTest', () => { @@ -12,8 +14,15 @@ */ let testProxy; + /** @type {MetricsTracker} */ + let metrics; + setup(() => { PolymerTest.clearBody(); + loadTimeData.overrideValues({ + navigationStartTime: 0.0, + }); + metrics = fakeMetricsPrivate(); testProxy = createTestProxy(); BrowserProxy.instance_ = testProxy; }); @@ -33,11 +42,14 @@ // Assert. assertEquals(element, moduleDescriptor.element); - assertEquals(1, testProxy.handler.getCallCount('onModuleLoaded')); - const [[id, delta, now]] = testProxy.handler.getArgs('onModuleLoaded'); - assertEquals('foo', id); - assertEquals(128, now); - assertEquals(5000n, delta.microseconds); // 128ms - 123ms === 5000µs. + assertEquals(1, metrics.count('NewTabPage.Modules.Loaded')); + assertEquals(1, metrics.count('NewTabPage.Modules.Loaded', 128)); + assertEquals(1, metrics.count('NewTabPage.Modules.Loaded.foo')); + assertEquals(1, metrics.count('NewTabPage.Modules.Loaded.foo', 128)); + assertEquals(1, metrics.count('NewTabPage.Modules.LoadDuration')); + assertEquals(1, metrics.count('NewTabPage.Modules.LoadDuration', 5)); + assertEquals(1, metrics.count('NewTabPage.Modules.LoadDuration.foo')); + assertEquals(1, metrics.count('NewTabPage.Modules.LoadDuration.foo', 5)); }); test('instantiate module without data', async () => { @@ -50,7 +62,10 @@ // Assert. assertEquals(null, moduleDescriptor.element); - assertEquals(0, testProxy.handler.getCallCount('onModuleLoaded')); + assertEquals(0, metrics.count('NewTabPage.Modules.Loaded')); + assertEquals(0, metrics.count('NewTabPage.Modules.Loaded.foo')); + assertEquals(0, metrics.count('NewTabPage.Modules.LoadDuration')); + assertEquals(0, metrics.count('NewTabPage.Modules.LoadDuration.foo')); }); test('module load times out', async () => {
diff --git a/chrome/test/data/webui/new_tab_page/modules/module_registry_test.js b/chrome/test/data/webui/new_tab_page/modules/module_registry_test.js index d191c8f..9b5d2f3 100644 --- a/chrome/test/data/webui/new_tab_page/modules/module_registry_test.js +++ b/chrome/test/data/webui/new_tab_page/modules/module_registry_test.js
@@ -4,7 +4,9 @@ import {BrowserProxy, ModuleDescriptor, ModuleRegistry} from 'chrome://new-tab-page/new_tab_page.js'; import {PromiseResolver} from 'chrome://resources/js/promise_resolver.m.js'; +import {fakeMetricsPrivate, MetricsTracker} from 'chrome://test/new_tab_page/metrics_test_support.js'; import {createTestProxy} from 'chrome://test/new_tab_page/test_support.js'; +import {flushTasks} from 'chrome://test/test_util.m.js'; suite('NewTabPageModulesModuleRegistryTest', () => { /** @@ -13,9 +15,16 @@ */ let testProxy; + /** @type {MetricsTracker} */ + let metrics; + setup(async () => { + PolymerTest.clearBody(); + loadTimeData.overrideValues({ + navigationStartTime: 0.0, + }); + metrics = fakeMetricsPrivate(); testProxy = createTestProxy(); - testProxy.setResultFor('now', 0); BrowserProxy.instance_ = testProxy; }); @@ -30,10 +39,15 @@ new ModuleDescriptor('baz', 'bla', 300, () => bazModuleResolver.promise), new ModuleDescriptor('buz', 'blo', 400, () => Promise.resolve(fooModule)), ]); + testProxy.setResultFor('now', 5.0); // Act. const modulesPromise = ModuleRegistry.getInstance().initializeModules(0); testProxy.callbackRouterRemote.setDisabledModules(false, ['buz']); + // Wait for first batch of modules. + await flushTasks(); + // Move time forward to test metrics. + testProxy.setResultFor('now', 123.0); // Delayed promise resolution to test async module instantiation. bazModuleResolver.resolve(bazModule); const modules = await modulesPromise; @@ -47,5 +61,19 @@ assertEquals('baz', modules[1].id); assertEquals(300, modules[1].heightPx); assertDeepEquals(bazModule, modules[1].element); + assertEquals(2, metrics.count('NewTabPage.Modules.Loaded')); + assertEquals(1, metrics.count('NewTabPage.Modules.Loaded', 5)); + assertEquals(1, metrics.count('NewTabPage.Modules.Loaded', 123)); + assertEquals(1, metrics.count('NewTabPage.Modules.Loaded.foo')); + assertEquals(1, metrics.count('NewTabPage.Modules.Loaded.foo', 5)); + assertEquals(1, metrics.count('NewTabPage.Modules.Loaded.baz')); + assertEquals(1, metrics.count('NewTabPage.Modules.Loaded.baz', 123)); + assertEquals(2, metrics.count('NewTabPage.Modules.LoadDuration')); + assertEquals(1, metrics.count('NewTabPage.Modules.LoadDuration', 0)); + assertEquals(1, metrics.count('NewTabPage.Modules.LoadDuration', 118)); + assertEquals(1, metrics.count('NewTabPage.Modules.LoadDuration.foo')); + assertEquals(1, metrics.count('NewTabPage.Modules.LoadDuration.foo', 0)); + assertEquals(1, metrics.count('NewTabPage.Modules.LoadDuration.baz')); + assertEquals(1, metrics.count('NewTabPage.Modules.LoadDuration.baz', 118)); }); });
diff --git a/chrome/test/data/webui/new_tab_page/modules/module_wrapper_test.js b/chrome/test/data/webui/new_tab_page/modules/module_wrapper_test.js index 66ba2cf..4c98eb2 100644 --- a/chrome/test/data/webui/new_tab_page/modules/module_wrapper_test.js +++ b/chrome/test/data/webui/new_tab_page/modules/module_wrapper_test.js
@@ -2,14 +2,33 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -import {$$} from 'chrome://new-tab-page/new_tab_page.js'; +import {$$, BrowserProxy} from 'chrome://new-tab-page/new_tab_page.js'; +import {loadTimeData} from 'chrome://resources/js/load_time_data.m.js'; +import {fakeMetricsPrivate, MetricsTracker} from 'chrome://test/new_tab_page/metrics_test_support.js'; +import {createTestProxy} from 'chrome://test/new_tab_page/test_support.js'; +import {eventToPromise} from 'chrome://test/test_util.m.js'; suite('NewTabPageModulesModuleWrapperTest', () => { /** @type {!ModuleWrapperElement} */ let moduleWrapper; + /** @type {MetricsTracker} */ + let metrics; + + /** + * @implements {BrowserProxy} + * @extends {TestBrowserProxy} + */ + let testProxy; + setup(() => { PolymerTest.clearBody(); + loadTimeData.overrideValues({ + navigationStartTime: 0.0, + }); + metrics = fakeMetricsPrivate(); + testProxy = createTestProxy(); + BrowserProxy.instance_ = testProxy; moduleWrapper = document.createElement('ntp-module-wrapper'); document.body.appendChild(moduleWrapper); }); @@ -17,6 +36,9 @@ test('renders module descriptor', async () => { // Arrange. const moduleElement = document.createElement('div'); + const detectedImpression = + eventToPromise('detect-impression', moduleWrapper); + testProxy.setResultFor('now', 123); // Act. moduleWrapper.descriptor = { @@ -24,11 +46,16 @@ heightPx: 100, element: moduleElement, }; + await detectedImpression; // Assert. assertEquals(100, $$(moduleWrapper, '#moduleElement').offsetHeight); assertDeepEquals( moduleElement, $$(moduleWrapper, '#moduleElement').children[0]); + assertEquals(1, metrics.count('NewTabPage.Modules.Impression')); + assertEquals(1, metrics.count('NewTabPage.Modules.Impression.foo')); + assertEquals(1, metrics.count('NewTabPage.Modules.Impression', 123)); + assertEquals(1, metrics.count('NewTabPage.Modules.Impression.foo', 123)); }); test('descriptor can only be set once', () => { @@ -46,4 +73,21 @@ }; }); }); + + test('receiving usage events records usage', () => { + // Arrange. + const moduleElement = document.createElement('div'); + moduleWrapper.descriptor = { + id: 'foo', + heightPx: 100, + element: moduleElement, + }; + + // Act. + moduleElement.dispatchEvent(new Event('usage')); + + // Assert. + assertEquals(1, metrics.count('NewTabPage.Modules.Usage')); + assertEquals(1, metrics.count('NewTabPage.Modules.Usage.foo')); + }); });
diff --git a/chrome/test/data/webui/new_tab_page/new_tab_page_browsertest.js b/chrome/test/data/webui/new_tab_page/new_tab_page_browsertest.js index a97e747..a5445e7 100644 --- a/chrome/test/data/webui/new_tab_page/new_tab_page_browsertest.js +++ b/chrome/test/data/webui/new_tab_page/new_tab_page_browsertest.js
@@ -64,6 +64,18 @@ }); // eslint-disable-next-line no-var +var NewTabPageMetricsUtilsTest = class extends NewTabPageBrowserTest { + /** @override */ + get browsePreload() { + return 'chrome://new-tab-page/test_loader.html?module=new_tab_page/metrics_utils_test.js'; + } +}; + +TEST_F('NewTabPageMetricsUtilsTest', 'All', function() { + mocha.run(); +}); + +// eslint-disable-next-line no-var var NewTabPageCustomizeShortcutsTest = class extends NewTabPageBrowserTest { /** @override */ get browsePreload() {
diff --git a/chromecast/browser/webview/proto/webview.proto b/chromecast/browser/webview/proto/webview.proto index 582c1c0..a20d56b 100644 --- a/chromecast/browser/webview/proto/webview.proto +++ b/chromecast/browser/webview/proto/webview.proto
@@ -294,6 +294,12 @@ int32 right = 4; } +message GetUserAgentRequest {} + +message GetUserAgentResponse { + string user_agent = 1; +} + message WebviewRequest { // Unique identifier for the request. For requests that will have a response, // the response id will match the request id. @@ -341,6 +347,7 @@ ResizeRequest resize = 21; ClearCookiesRequest clear_cookies = 22; SetInsetsRequest set_insets = 23; + GetUserAgentRequest get_user_agent = 24; } } @@ -365,6 +372,7 @@ ClearCookiesResponse clear_cookies = 11; TextInputFocusEvent input_focus_event = 12; AssociateCastAppWindowResponse associate = 20; + GetUserAgentResponse get_user_agent = 21; } }
diff --git a/chromecast/browser/webview/web_content_controller.cc b/chromecast/browser/webview/web_content_controller.cc index 8608900..34f502cd 100644 --- a/chromecast/browser/webview/web_content_controller.cc +++ b/chromecast/browser/webview/web_content_controller.cc
@@ -13,6 +13,7 @@ #include "chromecast/browser/webview/proto/webview.pb.h" #include "chromecast/browser/webview/webview_input_method_observer.h" #include "chromecast/browser/webview/webview_navigation_throttle.h" +#include "chromecast/common/cast_content_client.h" #include "chromecast/graphics/cast_focus_client_aura.h" #include "content/public/browser/browser_context.h" #include "content/public/browser/browsing_data_remover.h" @@ -184,6 +185,10 @@ } break; + case webview::WebviewRequest::kGetUserAgent: + HandleGetUserAgent(request.id()); + break; + default: client_->OnError("Unknown request code"); break; @@ -529,6 +534,15 @@ contents->GetTopLevelRenderWidgetHostView()->SetInsets(insets); } +void WebContentController::HandleGetUserAgent(int64_t id) { + std::unique_ptr<webview::WebviewResponse> response = + std::make_unique<webview::WebviewResponse>(); + + response->set_id(id); + response->mutable_get_user_agent()->set_user_agent(shell::GetUserAgent()); + client_->EnqueueSend(std::move(response)); +} + viz::SurfaceId WebContentController::GetSurfaceId() { content::WebContents* web_contents = GetWebContents(); // Web contents are destroyed before controller for cast apps.
diff --git a/chromecast/browser/webview/web_content_controller.h b/chromecast/browser/webview/web_content_controller.h index f7a5117c..16a9bb8 100644 --- a/chromecast/browser/webview/web_content_controller.h +++ b/chromecast/browser/webview/web_content_controller.h
@@ -116,6 +116,7 @@ void HandleGetTitle(int64_t id); void HandleResize(const gfx::Size& size); void HandleSetInsets(const gfx::Insets& insets); + void HandleGetUserAgent(int64_t id); viz::SurfaceId GetSurfaceId(); void ChannelModified(content::RenderFrameHost* frame,
diff --git a/chromecast/browser/webview/webview_browsertest.cc b/chromecast/browser/webview/webview_browsertest.cc index b42c721..dbeb39a 100644 --- a/chromecast/browser/webview/webview_browsertest.cc +++ b/chromecast/browser/webview/webview_browsertest.cc
@@ -420,4 +420,24 @@ RunMessageLoop(); } +IN_PROC_BROWSER_TEST_F(WebviewTest, GetUserAgent) { + auto check = [](const std::unique_ptr<webview::WebviewResponse>& response) { + return response->has_get_user_agent(); + }; + EXPECT_CALL(client_, EnqueueSend(_)).Times(testing::AnyNumber()); + EXPECT_CALL(client_, EnqueueSend(Truly(check))) + .Times(testing::AtLeast(1)) + .WillOnce([this](std::unique_ptr<webview::WebviewResponse> response) { + EXPECT_NE(response->get_user_agent().user_agent(), ""); + Quit(); + }); + WebviewController webview(context_.get(), &client_, true); + + webview::WebviewRequest request; + request.mutable_get_user_agent(); + SubmitWebviewRequest(&webview, request); + + RunMessageLoop(); +} + } // namespace chromecast
diff --git a/chromeos/crosapi/mojom/BUILD.gn b/chromeos/crosapi/mojom/BUILD.gn index 5df0b042..7cdb1cd 100644 --- a/chromeos/crosapi/mojom/BUILD.gn +++ b/chromeos/crosapi/mojom/BUILD.gn
@@ -7,6 +7,7 @@ mojom("mojom") { sources = [ "account_manager.mojom", + "automation.mojom", "bitmap.mojom", "cert_database.mojom", "clipboard.mojom",
diff --git a/chromeos/crosapi/mojom/automation.mojom b/chromeos/crosapi/mojom/automation.mojom new file mode 100644 index 0000000..d629655 --- /dev/null +++ b/chromeos/crosapi/mojom/automation.mojom
@@ -0,0 +1,72 @@ +// Copyright 2021 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 crosapi.mojom; + +import "mojo/public/mojom/base/unguessable_token.mojom"; +import "mojo/public/mojom/base/values.mojom"; + +// Interface for automation clients. Implemented by lacros-chrome. Used by +// ash-chrome to enable automation and to perform actions. +// Next version: 1 +// Next method id: 3 +[Stable, Uuid="8dd5f2a7-c24b-47c3-a096-a5d28c4764bb"] +interface AutomationClient { + // Enables automation for the client. This will result in the client + // repeatedly calling ReceiveEventPrototype() on the Automation interface. + Enable@0(); + + // Enables automation for a particular subtree of the client. This will + // result in the client repeatedly calling ReceiveEventPrototype() on the + // Automation interface. + EnableTree@1(mojo_base.mojom.UnguessableToken token); + + // All actions are forwarded to all clients. If the client has no matching + // |tree_id|, then it should do nothing. If |request_id| is -1, then there is + // no corresponding request_id -- mojo does not support optional primitives, + // and the underlying a11y code uses the same semantics for -1. + // + // This method is purely for prototyping and should not be called on + // production devices. The main problem is that |optional_args| should be a + // stable mojom struct, but we're not sure yet exactly what interface to + // stabilize for a11y. + PerformActionPrototype@2( + mojo_base.mojom.UnguessableToken tree_id, int32 automation_node_id, + string action_type, int32 request_id, + mojo_base.mojom.DictionaryValue optional_args); +}; + +// Interface for automation. Implemented by ash-chrome. +// Next version: 1 +// Next method id: 2 +[Stable, Uuid="356a895e-b41a-4c45-9336-d8dc6d332f98"] +interface Automation { + // A crosapi client can register itself as a automation client. This + // provides ash a mechanism to enable automation and send actions to the + // client. The client is uniquely identified by |token|. + RegisterAutomationClient@0( + pending_remote<AutomationClient> client, + mojo_base.mojom.UnguessableToken token); + + // |event_bundle| is a serialized instance of + // |ExtensionMsg_AutomationEventBundleParams|. This is potentially not + // compatible across lacros/ash version skew. https://crbug.com/1182926. + // |root| refers to whether the associated accessibility tree is the root + // accessibility tree for Lacros. Ash needs this information so as to + // appropriately glue in Lacros's accessibility trees with the rest of ash's + // accessibility trees. + // |token| uniquely identifies the client. + // |window_id| is a short-term placeholder that identifies a lacros window in + // which to glue the entire a11y tree (which may contain the contents of more + // than 1 lacros window). This relies on the assumption that lacros has a + // window open. + // + // This method is purely for prototyping and should not be called on + // production devices. The main problem is we're not sure yet what interface + // to stabilize for a11y. + ReceiveEventPrototype@1( + string event_bundle, bool root, mojo_base.mojom.UnguessableToken token, + string window_id); +}; +
diff --git a/chromeos/crosapi/mojom/crosapi.mojom b/chromeos/crosapi/mojom/crosapi.mojom index 317072e..be069236 100644 --- a/chromeos/crosapi/mojom/crosapi.mojom +++ b/chromeos/crosapi/mojom/crosapi.mojom
@@ -5,6 +5,7 @@ module crosapi.mojom; import "chromeos/components/sensors/mojom/cros_sensor_service.mojom"; +import "chromeos/crosapi/mojom/automation.mojom"; import "chromeos/crosapi/mojom/account_manager.mojom"; import "chromeos/crosapi/mojom/cert_database.mojom"; import "chromeos/crosapi/mojom/clipboard.mojom"; @@ -49,11 +50,17 @@ // please note the milestone when you added it, to help us reason about // compatibility between the client applications and older ash-chrome binaries. // -// Next version: 18 -// Next method id: 23 +// Next version: 19 +// Next method id: 24 [Stable, Uuid="8b79c34f-2bf8-4499-979a-b17cac522c1e", RenamedFrom="crosapi.mojom.AshChromeService"] interface Crosapi { + // Binds the automation interface which allows ash to enableautomation + // for Lacros. + // Added in M91. + [MinVersion=18] + BindAutomation@23(pending_receiver<Automation> receiver); + // Binds Chrome OS Account Manager for Identity management. // Added in M87. [MinVersion=4]
diff --git a/chromeos/crosapi/mojom/notification.mojom b/chromeos/crosapi/mojom/notification.mojom index dd98b09..617a58b 100644 --- a/chromeos/crosapi/mojom/notification.mojom +++ b/chromeos/crosapi/mojom/notification.mojom
@@ -144,4 +144,12 @@ // Badge to show the source of the notification (e.g. a 16x16 browser icon). [MinVersion=1] gfx.mojom.ImageSkia? badge@23; + + [MinVersion=2] + // Whether the bool badge_needs_additional_masking is set. + bool badge_needs_additional_masking_has_value@24; + + [MinVersion=2] + // Whether the badge needs additional masking. + bool badge_needs_additional_masking@25; };
diff --git a/chromeos/lacros/lacros_chrome_service_impl.cc b/chromeos/lacros/lacros_chrome_service_impl.cc index b5dfbbf..eb6a246 100644 --- a/chromeos/lacros/lacros_chrome_service_impl.cc +++ b/chromeos/lacros/lacros_chrome_service_impl.cc
@@ -220,6 +220,12 @@ crosapi_->BindFeedback(std::move(pending_receiver)); } + void BindAutomationReceiver( + mojo::PendingReceiver<crosapi::mojom::Automation> pending_receiver) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + crosapi_->BindAutomation(std::move(pending_receiver)); + } + void BindCertDbReceiver( mojo::PendingReceiver<crosapi::mojom::CertDatabase> pending_receiver) { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); @@ -485,6 +491,15 @@ delegate_->OnInitialized(*init_params_); did_bind_receiver_ = true; + if (IsAutomationAvailable()) { + never_blocking_sequence_->PostTask( + FROM_HERE, + base::BindOnce( + &LacrosChromeServiceNeverBlockingState::BindAutomationReceiver, + weak_sequenced_state_, + automation_remote_.BindNewPipeAndPassReceiver())); + } + if (IsCertDbAvailable()) { never_blocking_sequence_->PostTask( FROM_HERE, @@ -634,6 +649,12 @@ g_disable_all_crosapi_for_tests = true; } +bool LacrosChromeServiceImpl::IsAutomationAvailable() const { + base::Optional<uint32_t> version = CrosapiVersion(); + return version && version.value() >= + Crosapi::MethodMinVersions::kBindAutomationMinVersion; +} + bool LacrosChromeServiceImpl::IsAccountManagerAvailable() const { base::Optional<uint32_t> version = CrosapiVersion(); return version &&
diff --git a/chromeos/lacros/lacros_chrome_service_impl.h b/chromeos/lacros/lacros_chrome_service_impl.h index 7f90cfd..078abd6 100644 --- a/chromeos/lacros/lacros_chrome_service_impl.h +++ b/chromeos/lacros/lacros_chrome_service_impl.h
@@ -19,6 +19,7 @@ #include "base/sequenced_task_runner.h" #include "chromeos/components/sensors/mojom/cros_sensor_service.mojom.h" #include "chromeos/crosapi/mojom/account_manager.mojom.h" +#include "chromeos/crosapi/mojom/automation.mojom.h" #include "chromeos/crosapi/mojom/cert_database.mojom.h" #include "chromeos/crosapi/mojom/crosapi.mojom.h" #include "chromeos/crosapi/mojom/device_attributes.mojom.h" @@ -102,6 +103,7 @@ // Each of these functions guards usage of access to the corresponding remote. // Keep these in alphabetical order. + bool IsAutomationAvailable() const; bool IsAccountManagerAvailable() const; bool IsCertDbAvailable() const; bool IsClipboardAvailable() const; @@ -134,6 +136,13 @@ // -------------------------------------------------------------------------- // This must be called on the affine sequence. + mojo::Remote<crosapi::mojom::Automation>& automation_remote() { + DCHECK_CALLED_ON_VALID_SEQUENCE(affine_sequence_checker_); + DCHECK(IsAutomationAvailable()); + return automation_remote_; + } + + // This must be called on the affine sequence. mojo::Remote<crosapi::mojom::CertDatabase>& cert_database_remote() { DCHECK_CALLED_ON_VALID_SEQUENCE(affine_sequence_checker_); DCHECK(IsCertDbAvailable()); @@ -349,6 +358,7 @@ // These members are affine to the affine sequence. They are initialized in // the constructor and are immediately available for use. + mojo::Remote<crosapi::mojom::Automation> automation_remote_; mojo::Remote<crosapi::mojom::CertDatabase> cert_database_remote_; mojo::Remote<crosapi::mojom::Clipboard> clipboard_remote_; mojo::Remote<crosapi::mojom::DeviceAttributes> device_attributes_remote_;
diff --git a/chromeos/network/cellular_metrics_logger.cc b/chromeos/network/cellular_metrics_logger.cc index 3e1d2e06..7e472e1 100644 --- a/chromeos/network/cellular_metrics_logger.cc +++ b/chromeos/network/cellular_metrics_logger.cc
@@ -30,6 +30,22 @@ } // namespace // static +const char CellularMetricsLogger::kSimPinLockSuccessHistogram[] = + "Network.Cellular.Pin.LockSuccess"; + +// static +const char CellularMetricsLogger::kSimPinUnlockSuccessHistogram[] = + "Network.Cellular.Pin.UnlockSuccess"; + +// static +const char CellularMetricsLogger::kSimPinUnblockSuccessHistogram[] = + "Network.Cellular.Pin.UnblockSuccess"; + +// static +const char CellularMetricsLogger::kSimPinChangeSuccessHistogram[] = + "Network.Cellular.Pin.ChangeSuccess"; + +// static const base::TimeDelta CellularMetricsLogger::kInitializationTimeout = base::TimeDelta::FromSeconds(15); @@ -37,6 +53,52 @@ const base::TimeDelta CellularMetricsLogger::kDisconnectRequestTimeout = base::TimeDelta::FromSeconds(5); +// static +CellularMetricsLogger::SimPinOperationResult +CellularMetricsLogger::GetSimPinOperationResultForShillError( + const std::string& shill_error_name) { + if (shill_error_name == shill::kErrorResultFailure || + shill_error_name == shill::kErrorResultInvalidArguments) { + return SimPinOperationResult::kErrorFailure; + } + if (shill_error_name == shill::kErrorResultNotSupported) + return SimPinOperationResult::kErrorNotSupported; + if (shill_error_name == shill::kErrorResultIncorrectPin) + return SimPinOperationResult::kErrorIncorrectPin; + if (shill_error_name == shill::kErrorResultPinBlocked) + return SimPinOperationResult::kErrorPinBlocked; + if (shill_error_name == shill::kErrorResultPinRequired) + return SimPinOperationResult::kErrorPinRequired; + if (shill_error_name == shill::kErrorResultNotFound) + return SimPinOperationResult::kErrorDeviceMissing; + return SimPinOperationResult::kErrorUnknown; +} + +// static +void CellularMetricsLogger::RecordSimPinOperationResult( + const SimPinOperation& pin_operation, + const base::Optional<std::string>& shill_error_name) { + SimPinOperationResult result = + shill_error_name.has_value() + ? GetSimPinOperationResultForShillError(*shill_error_name) + : SimPinOperationResult::kSuccess; + + switch (pin_operation) { + case SimPinOperation::kLock: + UMA_HISTOGRAM_ENUMERATION(kSimPinLockSuccessHistogram, result); + return; + case SimPinOperation::kUnlock: + UMA_HISTOGRAM_ENUMERATION(kSimPinUnlockSuccessHistogram, result); + return; + case SimPinOperation::kUnblock: + UMA_HISTOGRAM_ENUMERATION(kSimPinUnblockSuccessHistogram, result); + return; + case SimPinOperation::kChange: + UMA_HISTOGRAM_ENUMERATION(kSimPinChangeSuccessHistogram, result); + return; + } +} + CellularMetricsLogger::ConnectionInfo::ConnectionInfo( const std::string& network_guid, bool is_connected)
diff --git a/chromeos/network/cellular_metrics_logger.h b/chromeos/network/cellular_metrics_logger.h index 7fa7a217..8169254e 100644 --- a/chromeos/network/cellular_metrics_logger.h +++ b/chromeos/network/cellular_metrics_logger.h
@@ -39,6 +39,25 @@ public LoginState::Observer, public NetworkConnectionObserver { public: + // Histograms associated with SIM Pin operations. + static const char kSimPinLockSuccessHistogram[]; + static const char kSimPinUnlockSuccessHistogram[]; + static const char kSimPinUnblockSuccessHistogram[]; + static const char kSimPinChangeSuccessHistogram[]; + + // PIN operations that are tracked by metrics. + enum class SimPinOperation { + kLock = 0, + kUnlock = 1, + kUnblock = 2, + kChange = 3, + }; + + // Records the result of pin operations performed. + static void RecordSimPinOperationResult( + const SimPinOperation& pin_operation, + const base::Optional<std::string>& shill_error_name = base::nullopt); + CellularMetricsLogger(); ~CellularMetricsLogger() override; @@ -74,6 +93,11 @@ FRIEND_TEST_ALL_PREFIXES(CellularMetricsLoggerTest, CellularDisconnectionsTest); + FRIEND_TEST_ALL_PREFIXES(NetworkDeviceHandlerTest, RequirePin); + FRIEND_TEST_ALL_PREFIXES(NetworkDeviceHandlerTest, EnterPin); + FRIEND_TEST_ALL_PREFIXES(NetworkDeviceHandlerTest, UnblockPin); + FRIEND_TEST_ALL_PREFIXES(NetworkDeviceHandlerTest, ChangePin); + // The amount of time after cellular device is added to device list, // after which cellular device is considered initialized. static const base::TimeDelta kInitializationTimeout; @@ -141,6 +165,29 @@ kMaxValue = kDisconnected }; + // Result of PIN operations. + // These values are persisted to logs. Entries should not be renumbered + // and numeric values should never be reused. + // Note: With the exception of Success, enums should match the + // error names listed near the top of NetworkDeviceHandler. + enum class SimPinOperationResult { + kSuccess = 0, + kErrorDeviceMissing = 1, + kErrorFailure = 2, + kErrorIncorrectPin = 3, + kErrorNotFound = 4, + kErrorNotSupported = 5, + kErrorPinBlocked = 6, + kErrorPinRequired = 7, + kErrorTimeout = 8, + kErrorUnknown = 9, + kMaxValue = kErrorUnknown, + }; + + // Convert shill error name string to SimPinOperationResult enum. + static SimPinOperationResult GetSimPinOperationResultForShillError( + const std::string& shill_error_name); + // Convert shill activation state string to PSimActivationState enum PSimActivationState PSimActivationStateToEnum(const std::string& state);
diff --git a/chromeos/network/network_device_handler_impl.cc b/chromeos/network/network_device_handler_impl.cc index 25180122..a9e1c289 100644 --- a/chromeos/network/network_device_handler_impl.cc +++ b/chromeos/network/network_device_handler_impl.cc
@@ -25,6 +25,7 @@ #include "base/values.h" #include "chromeos/dbus/shill/shill_device_client.h" #include "chromeos/dbus/shill/shill_ipconfig_client.h" +#include "chromeos/network/cellular_metrics_logger.h" #include "chromeos/network/device_state.h" #include "chromeos/network/network_event_log.h" #include "chromeos/network/network_state_handler.h" @@ -100,6 +101,25 @@ return error_data; } +void HandleSimPinOperationSuccess( + const CellularMetricsLogger::SimPinOperation& pin_operation, + base::OnceClosure callback) { + CellularMetricsLogger::RecordSimPinOperationResult(pin_operation); + std::move(callback).Run(); +} + +void HandleSimPinOperationFailure( + const CellularMetricsLogger::SimPinOperation& pin_operation, + const std::string& device_path, + network_handler::ErrorCallback error_callback, + const std::string& shill_error_name, + const std::string& shill_error_message) { + CellularMetricsLogger::RecordSimPinOperationResult(pin_operation, + shill_error_message); + HandleShillCallFailure(device_path, std::move(error_callback), + shill_error_name, shill_error_message); +} + } // namespace NetworkDeviceHandlerImpl::NetworkDeviceHandlerImpl() = default; @@ -161,8 +181,12 @@ network_handler::ErrorCallback error_callback) { NET_LOG(USER) << "Device.RequirePin: " << device_path << ": " << require_pin; ShillDeviceClient::Get()->RequirePin( - dbus::ObjectPath(device_path), pin, require_pin, std::move(callback), - base::BindOnce(&HandleShillCallFailure, device_path, + dbus::ObjectPath(device_path), pin, require_pin, + base::BindOnce(&HandleSimPinOperationSuccess, + CellularMetricsLogger::SimPinOperation::kLock, + std::move(callback)), + base::BindOnce(&HandleSimPinOperationFailure, + CellularMetricsLogger::SimPinOperation::kLock, device_path, std::move(error_callback))); } @@ -173,9 +197,13 @@ network_handler::ErrorCallback error_callback) { NET_LOG(USER) << "Device.EnterPin: " << device_path; ShillDeviceClient::Get()->EnterPin( - dbus::ObjectPath(device_path), pin, std::move(callback), - base::BindOnce(&HandleShillCallFailure, device_path, - std::move(error_callback))); + dbus::ObjectPath(device_path), pin, + base::BindOnce(&HandleSimPinOperationSuccess, + CellularMetricsLogger::SimPinOperation::kUnlock, + std::move(callback)), + base::BindOnce(&HandleSimPinOperationFailure, + CellularMetricsLogger::SimPinOperation::kUnlock, + device_path, std::move(error_callback))); } void NetworkDeviceHandlerImpl::UnblockPin( @@ -186,9 +214,13 @@ network_handler::ErrorCallback error_callback) { NET_LOG(USER) << "Device.UnblockPin: " << device_path; ShillDeviceClient::Get()->UnblockPin( - dbus::ObjectPath(device_path), puk, new_pin, std::move(callback), - base::BindOnce(&HandleShillCallFailure, device_path, - std::move(error_callback))); + dbus::ObjectPath(device_path), puk, new_pin, + base::BindOnce(&HandleSimPinOperationSuccess, + CellularMetricsLogger::SimPinOperation::kUnblock, + std::move(callback)), + base::BindOnce(&HandleSimPinOperationFailure, + CellularMetricsLogger::SimPinOperation::kUnblock, + device_path, std::move(error_callback))); } void NetworkDeviceHandlerImpl::ChangePin( @@ -199,9 +231,13 @@ network_handler::ErrorCallback error_callback) { NET_LOG(USER) << "Device.ChangePin: " << device_path; ShillDeviceClient::Get()->ChangePin( - dbus::ObjectPath(device_path), old_pin, new_pin, std::move(callback), - base::BindOnce(&HandleShillCallFailure, device_path, - std::move(error_callback))); + dbus::ObjectPath(device_path), old_pin, new_pin, + base::BindOnce(&HandleSimPinOperationSuccess, + CellularMetricsLogger::SimPinOperation::kChange, + std::move(callback)), + base::BindOnce(&HandleSimPinOperationFailure, + CellularMetricsLogger::SimPinOperation::kChange, + device_path, std::move(error_callback))); } void NetworkDeviceHandlerImpl::SetCellularAllowRoaming(
diff --git a/chromeos/network/network_device_handler_unittest.cc b/chromeos/network/network_device_handler_unittest.cc index e15cf850..f9ed547 100644 --- a/chromeos/network/network_device_handler_unittest.cc +++ b/chromeos/network/network_device_handler_unittest.cc
@@ -8,12 +8,14 @@ #include "base/logging.h" #include "base/macros.h" #include "base/run_loop.h" +#include "base/test/metrics/histogram_tester.h" #include "base/test/task_environment.h" #include "base/values.h" #include "chromeos/dbus/dbus_thread_manager.h" #include "chromeos/dbus/shill/fake_shill_device_client.h" #include "chromeos/dbus/shill/shill_clients.h" #include "chromeos/dbus/shill/shill_manager_client.h" +#include "chromeos/network/cellular_metrics_logger.h" #include "chromeos/network/network_device_handler_impl.h" #include "chromeos/network/network_handler_callbacks.h" #include "chromeos/network/network_state_handler.h" @@ -429,12 +431,19 @@ } TEST_F(NetworkDeviceHandlerTest, RequirePin) { + base::HistogramTester histogram_tester; + // Test that the success callback gets called. network_device_handler_->RequirePin(kDefaultCellularDevicePath, true, kDefaultPin, GetSuccessCallback(), GetErrorCallback()); base::RunLoop().RunUntilIdle(); EXPECT_EQ(kResultSuccess, result_); + histogram_tester.ExpectTotalCount( + CellularMetricsLogger::kSimPinLockSuccessHistogram, 1); + histogram_tester.ExpectBucketCount( + CellularMetricsLogger::kSimPinLockSuccessHistogram, + CellularMetricsLogger::SimPinOperationResult::kSuccess, 1); // Test that the shill error propagates to the error callback. network_device_handler_->RequirePin(kUnknownCellularDevicePath, true, @@ -442,23 +451,42 @@ GetErrorCallback()); base::RunLoop().RunUntilIdle(); EXPECT_EQ(NetworkDeviceHandler::kErrorDeviceMissing, result_); + + histogram_tester.ExpectTotalCount( + CellularMetricsLogger::kSimPinLockSuccessHistogram, 2); + histogram_tester.ExpectBucketCount( + CellularMetricsLogger::kSimPinLockSuccessHistogram, + CellularMetricsLogger::SimPinOperationResult::kErrorUnknown, 1); } TEST_F(NetworkDeviceHandlerTest, EnterPin) { + base::HistogramTester histogram_tester; + // Test that the success callback gets called. network_device_handler_->EnterPin(kDefaultCellularDevicePath, kDefaultPin, GetSuccessCallback(), GetErrorCallback()); base::RunLoop().RunUntilIdle(); EXPECT_EQ(kResultSuccess, result_); + histogram_tester.ExpectTotalCount( + CellularMetricsLogger::kSimPinUnlockSuccessHistogram, 1); + histogram_tester.ExpectBucketCount( + CellularMetricsLogger::kSimPinUnlockSuccessHistogram, + CellularMetricsLogger::SimPinOperationResult::kSuccess, 1); // Test that the shill error propagates to the error callback. network_device_handler_->EnterPin(kUnknownCellularDevicePath, kDefaultPin, GetSuccessCallback(), GetErrorCallback()); base::RunLoop().RunUntilIdle(); EXPECT_EQ(NetworkDeviceHandler::kErrorDeviceMissing, result_); + histogram_tester.ExpectTotalCount( + CellularMetricsLogger::kSimPinUnlockSuccessHistogram, 2); + histogram_tester.ExpectBucketCount( + CellularMetricsLogger::kSimPinUnlockSuccessHistogram, + CellularMetricsLogger::SimPinOperationResult::kErrorUnknown, 1); } TEST_F(NetworkDeviceHandlerTest, UnblockPin) { + base::HistogramTester histogram_tester; const char kPuk[] = "12345678"; const char kPin[] = "1234"; @@ -467,15 +495,26 @@ GetSuccessCallback(), GetErrorCallback()); base::RunLoop().RunUntilIdle(); EXPECT_EQ(kResultSuccess, result_); + histogram_tester.ExpectTotalCount( + CellularMetricsLogger::kSimPinUnblockSuccessHistogram, 1); + histogram_tester.ExpectBucketCount( + CellularMetricsLogger::kSimPinUnblockSuccessHistogram, + CellularMetricsLogger::SimPinOperationResult::kSuccess, 1); // Test that the shill error propagates to the error callback. network_device_handler_->UnblockPin(kUnknownCellularDevicePath, kPin, kPuk, GetSuccessCallback(), GetErrorCallback()); base::RunLoop().RunUntilIdle(); EXPECT_EQ(NetworkDeviceHandler::kErrorDeviceMissing, result_); + histogram_tester.ExpectTotalCount( + CellularMetricsLogger::kSimPinUnblockSuccessHistogram, 2); + histogram_tester.ExpectBucketCount( + CellularMetricsLogger::kSimPinUnblockSuccessHistogram, + CellularMetricsLogger::SimPinOperationResult::kErrorUnknown, 1); } TEST_F(NetworkDeviceHandlerTest, ChangePin) { + base::HistogramTester histogram_tester; const char kNewPin[] = "1234"; const char kIncorrectPin[] = "9999"; @@ -488,6 +527,11 @@ kNewPin, GetSuccessCallback(), GetErrorCallback()); base::RunLoop().RunUntilIdle(); EXPECT_EQ(kResultSuccess, result_); + histogram_tester.ExpectTotalCount( + CellularMetricsLogger::kSimPinChangeSuccessHistogram, 1); + histogram_tester.ExpectBucketCount( + CellularMetricsLogger::kSimPinChangeSuccessHistogram, + CellularMetricsLogger::SimPinOperationResult::kSuccess, 1); // Test that the shill error propagates to the error callback. network_device_handler_->ChangePin(kDefaultCellularDevicePath, kIncorrectPin, @@ -495,6 +539,11 @@ GetErrorCallback()); base::RunLoop().RunUntilIdle(); EXPECT_EQ(NetworkDeviceHandler::kErrorIncorrectPin, result_); + histogram_tester.ExpectTotalCount( + CellularMetricsLogger::kSimPinChangeSuccessHistogram, 2); + histogram_tester.ExpectBucketCount( + CellularMetricsLogger::kSimPinChangeSuccessHistogram, + CellularMetricsLogger::SimPinOperationResult::kErrorUnknown, 1); } TEST_F(NetworkDeviceHandlerTest, AddWifiWakeOnPacketOfTypes) {
diff --git a/chromeos/profiles/atom.afdo.newest.txt b/chromeos/profiles/atom.afdo.newest.txt index 10d21ca2..3297fe5 100644 --- a/chromeos/profiles/atom.afdo.newest.txt +++ b/chromeos/profiles/atom.afdo.newest.txt
@@ -1 +1 @@ -chromeos-chrome-amd64-atom-90-4405.2-1614595422-benchmark-91.0.4435.0-r1-redacted.afdo.xz +chromeos-chrome-amd64-atom-91-4430.19-1615803233-benchmark-91.0.4448.0-r1-redacted.afdo.xz
diff --git a/chromeos/profiles/bigcore.afdo.newest.txt b/chromeos/profiles/bigcore.afdo.newest.txt index 27ce6fa..c48f52a 100644 --- a/chromeos/profiles/bigcore.afdo.newest.txt +++ b/chromeos/profiles/bigcore.afdo.newest.txt
@@ -1 +1 @@ -chromeos-chrome-amd64-bigcore-90-4405.2-1614599389-benchmark-91.0.4435.0-r1-redacted.afdo.xz +chromeos-chrome-amd64-bigcore-91-4430.19-1615806685-benchmark-91.0.4448.0-r1-redacted.afdo.xz
diff --git a/chromeos/services/assistant/assistant_manager_service_impl.cc b/chromeos/services/assistant/assistant_manager_service_impl.cc index ac8dc5e..615953c 100644 --- a/chromeos/services/assistant/assistant_manager_service_impl.cc +++ b/chromeos/services/assistant/assistant_manager_service_impl.cc
@@ -269,7 +269,6 @@ media_host_->Stop(); scoped_app_list_event_subscriber_.Reset(); - scoped_action_observer_.Reset(); // When user disables the feature, we also delete all data. if (!assistant_state()->settings_enabled().value()) @@ -499,32 +498,6 @@ receive_url_response_ = url.spec(); } -void AssistantManagerServiceImpl::OnVerifyAndroidApp( - const std::vector<AndroidAppInfo>& apps_info, - const InteractionInfo& interaction) { - ENSURE_MAIN_THREAD(&AssistantManagerServiceImpl::OnVerifyAndroidApp, - apps_info, interaction); - std::vector<AndroidAppInfo> result_apps_info; - for (auto& app_info : apps_info) { - AndroidAppInfo result_app_info(app_info); - AppStatus status = device_actions()->GetAndroidAppStatus(app_info); - result_app_info.status = status; - result_apps_info.emplace_back(result_app_info); - } - std::string interaction_proto = CreateVerifyProviderResponseInteraction( - interaction.interaction_id, result_apps_info); - - assistant_client::VoicelessOptions options; - options.obfuscated_gaia_id = interaction.user_id; - // Set the request to be user initiated so that a new conversation will be - // created to handle the client OPs in the response of this request. - options.is_user_initiated = true; - - assistant_manager_internal()->SendVoicelessInteraction( - interaction_proto, /*description=*/"verify_provider_response", options, - [](auto) {}); -} - void AssistantManagerServiceImpl::OnStateChanged( chromeos::libassistant::mojom::ServiceState new_state) { using chromeos::libassistant::mojom::ServiceState; @@ -572,8 +545,6 @@ if (base::FeatureList::IsEnabled(assistant::features::kAssistantAppSupport)) scoped_app_list_event_subscriber_.Observe(device_actions()); - - scoped_action_observer_.Observe(action_module()); } bool AssistantManagerServiceImpl::IsServiceStarted() const {
diff --git a/chromeos/services/assistant/assistant_manager_service_impl.h b/chromeos/services/assistant/assistant_manager_service_impl.h index 42e16dc..5dad430 100644 --- a/chromeos/services/assistant/assistant_manager_service_impl.h +++ b/chromeos/services/assistant/assistant_manager_service_impl.h
@@ -18,7 +18,6 @@ #include "base/scoped_observation.h" #include "base/synchronization/lock.h" #include "base/threading/thread.h" -#include "chromeos/assistant/internal/action/assistant_action_observer.h" #include "chromeos/assistant/internal/action/cros_action_module.h" #include "chromeos/services/assistant/assistant_manager_service.h" #include "chromeos/services/assistant/assistant_settings_impl.h" @@ -99,7 +98,6 @@ // enabled/disabled in settings or switches to a non-primary profile. class COMPONENT_EXPORT(ASSISTANT_SERVICE) AssistantManagerServiceImpl : public AssistantManagerService, - public chromeos::assistant::action::AssistantActionObserver, public assistant_client::ConversationStateListener, public AppListEventSubscriber, private chromeos::libassistant::mojom::StateObserver, @@ -168,10 +166,6 @@ mojo::PendingReceiver<chromeos::libassistant::mojom::NotificationDelegate> GetPendingNotificationDelegate() override; - // AssistantActionObserver overrides: - void OnVerifyAndroidApp(const std::vector<AndroidAppInfo>& apps_info, - const InteractionInfo& interaction) override; - // chromeos::assistant::ConversationObserver overrides: void OnInteractionStarted( const AssistantInteractionMetadata& metadata) override; @@ -295,9 +289,6 @@ &DeviceActions::RemoveAppListEventSubscriber> scoped_app_list_event_subscriber_{this}; base::ObserverList<AssistantManagerService::StateObserver> state_observers_; - base::ScopedObservation<action::CrosActionModule, - action::AssistantActionObserver> - scoped_action_observer_{this}; base::WeakPtrFactory<AssistantManagerServiceImpl> weak_factory_;
diff --git a/chromeos/services/cellular_setup/esim_test_base.cc b/chromeos/services/cellular_setup/esim_test_base.cc index 34e48fcd..bcbe579 100644 --- a/chromeos/services/cellular_setup/esim_test_base.cc +++ b/chromeos/services/cellular_setup/esim_test_base.cc
@@ -29,7 +29,8 @@ const char* ESimTestBase::kTestEuiccPath = "/org/chromium/Hermes/Euicc/0"; const char* ESimTestBase::kTestEid = "12345678901234567890123456789012"; -ESimTestBase::ESimTestBase() { +ESimTestBase::ESimTestBase() + : task_environment_(base::test::TaskEnvironment::TimeSource::MOCK_TIME) { if (!ShillManagerClient::Get()) shill_clients::InitializeFakes(); if (!HermesManagerClient::Get())
diff --git a/chromeos/services/cellular_setup/esim_test_base.h b/chromeos/services/cellular_setup/esim_test_base.h index c0cd307e..7e0e6d3 100644 --- a/chromeos/services/cellular_setup/esim_test_base.h +++ b/chromeos/services/cellular_setup/esim_test_base.h
@@ -66,6 +66,8 @@ return network_state_handler_.get(); } + base::test::TaskEnvironment* task_environment() { return &task_environment_; } + private: std::unique_ptr<NetworkStateHandler> network_state_handler_; std::unique_ptr<NetworkDeviceHandler> network_device_handler_;
diff --git a/chromeos/services/cellular_setup/euicc.cc b/chromeos/services/cellular_setup/euicc.cc index 2abf100..af9a33a0 100644 --- a/chromeos/services/cellular_setup/euicc.cc +++ b/chromeos/services/cellular_setup/euicc.cc
@@ -7,8 +7,10 @@ #include <cstdint> #include <memory> +#include "base/metrics/histogram_macros.h" #include "base/optional.h" #include "base/strings/strcat.h" +#include "base/time/time.h" #include "chromeos/network/cellular_esim_connection_handler.h" #include "chromeos/network/cellular_esim_profile.h" #include "chromeos/network/cellular_inhibitor.h" @@ -23,15 +25,35 @@ #include "dbus/object_path.h" #include "mojo/public/cpp/bindings/pending_remote.h" +namespace chromeos { +namespace cellular_setup { namespace { // Prefix for EID when encoded in QR Code. const char kEidQrCodePrefix[] = "EID:"; -} // namespace +// Measures the time from which this function is called to when |callback| +// is expected to run. The measured time difference should capture the time it +// took for a profile to be fully downloaded from a provided activation code. +Euicc::InstallProfileFromActivationCodeCallback +CreateTimedInstallProfileCallback( + Euicc::InstallProfileFromActivationCodeCallback callback) { + return base::BindOnce( + [](Euicc::InstallProfileFromActivationCodeCallback callback, + base::Time installation_start_time, mojom::ProfileInstallResult result, + mojo::PendingRemote<mojom::ESimProfile> esim_profile_pending_remote) + -> void { + std::move(callback).Run(result, std::move(esim_profile_pending_remote)); + if (result != mojom::ProfileInstallResult::kSuccess) + return; + UMA_HISTOGRAM_MEDIUM_TIMES( + "Network.Cellular.ESim.ProfileDownload.ActivationCode.Latency", + base::Time::Now() - installation_start_time); + }, + std::move(callback), base::Time::Now()); +} -namespace chromeos { -namespace cellular_setup { +} // namespace Euicc::Euicc(const dbus::ObjectPath& path, ESimManager* esim_manager) : esim_manager_(esim_manager), @@ -89,10 +111,10 @@ // currently being installed to prevent multiple attempts for the same // activation code. NET_LOG(USER) << "Attempting installation with code " << activation_code; - esim_manager_->cellular_inhibitor()->InhibitCellularScanning( - base::BindOnce(&Euicc::PerformInstallProfileFromActivationCode, - weak_ptr_factory_.GetWeakPtr(), activation_code, - confirmation_code, std::move(callback))); + esim_manager_->cellular_inhibitor()->InhibitCellularScanning(base::BindOnce( + &Euicc::PerformInstallProfileFromActivationCode, + weak_ptr_factory_.GetWeakPtr(), activation_code, confirmation_code, + CreateTimedInstallProfileCallback(std::move(callback)))); } void Euicc::RequestPendingProfiles(RequestPendingProfilesCallback callback) {
diff --git a/chromeos/services/cellular_setup/euicc_unittest.cc b/chromeos/services/cellular_setup/euicc_unittest.cc index 8c1e9c9..a817f58 100644 --- a/chromeos/services/cellular_setup/euicc_unittest.cc +++ b/chromeos/services/cellular_setup/euicc_unittest.cc
@@ -6,6 +6,7 @@ #include "base/run_loop.h" #include "base/test/bind.h" +#include "base/test/metrics/histogram_tester.h" #include "chromeos/dbus/hermes/hermes_euicc_client.h" #include "chromeos/dbus/hermes/hermes_profile_client.h" #include "chromeos/network/fake_network_connection_handler.h" @@ -56,7 +57,8 @@ const std::string& activation_code, const std::string& confirmation_code, bool wait_for_connect, - bool fail_connect) { + bool fail_connect, + const uint64_t& connect_latency_in_ms = 0) { mojom::ProfileInstallResult out_install_result; mojo::PendingRemote<mojom::ESimProfile> out_esim_profile; @@ -73,6 +75,8 @@ if (wait_for_connect) { base::RunLoop().RunUntilIdle(); + task_environment()->AdvanceClock( + base::TimeDelta::FromMilliseconds(connect_latency_in_ms)); EXPECT_LE(1u, network_connection_handler()->connect_calls().size()); if (fail_connect) { network_connection_handler() @@ -146,13 +150,23 @@ EXPECT_EQ(mojom::ProfileInstallResult::kFailure, result_pair.first); ASSERT_FALSE(result_pair.second.is_valid()); + base::HistogramTester histogram_tester; + // Verify that install succeeds when valid activation code is passed. + const uint64_t connect_latency_in_ms = 3000; result_pair = InstallProfileFromActivationCode( euicc, euicc_test->GenerateFakeActivationCode(), /*confirmation_code=*/std::string(), /*wait_for_connect=*/true, - /*fail_connect=*/false); + /*fail_connect=*/false, connect_latency_in_ms); EXPECT_EQ(mojom::ProfileInstallResult::kSuccess, result_pair.first); ASSERT_TRUE(result_pair.second.is_valid()); + + histogram_tester.ExpectTimeBucketCount( + "Network.Cellular.ESim.ProfileDownload.ActivationCode.Latency", + base::TimeDelta::FromMilliseconds(connect_latency_in_ms), 1); + + histogram_tester.ExpectTotalCount( + "Network.Cellular.ESim.ProfileDownload.ActivationCode.Latency", 1); } TEST_F(EuiccTest, InstallPendingProfileFromActivationCode) {
diff --git a/chromeos/services/libassistant/conversation_controller.cc b/chromeos/services/libassistant/conversation_controller.cc index 3f588213..7cbcbefe8 100644 --- a/chromeos/services/libassistant/conversation_controller.cc +++ b/chromeos/services/libassistant/conversation_controller.cc
@@ -398,6 +398,9 @@ } // Called from Libassistant thread. +// Note that OnVerifyAndroidApp() will be handled by |DisplayController| +// directly since it stores an updated list of all installed Android Apps on the +// device. void ConversationController::OnOpenAndroidApp( const chromeos::assistant::AndroidAppInfo& app_info, const chromeos::assistant::InteractionInfo& interaction) {
diff --git a/chromeos/services/libassistant/display_connection_impl.h b/chromeos/services/libassistant/display_connection_impl.h index 4d24de5c..8b8737b2 100644 --- a/chromeos/services/libassistant/display_connection_impl.h +++ b/chromeos/services/libassistant/display_connection_impl.h
@@ -42,6 +42,10 @@ void OnAndroidAppListRefreshed( const std::vector<assistant::AndroidAppInfo>& apps_info); + const std::vector<assistant::AndroidAppInfo>& GetCachedAndroidAppList() { + return apps_info_; + } + private: void SendDisplayRequestLocked();
diff --git a/chromeos/services/libassistant/display_controller.cc b/chromeos/services/libassistant/display_controller.cc index fb1ce4a..cb765b4 100644 --- a/chromeos/services/libassistant/display_controller.cc +++ b/chromeos/services/libassistant/display_controller.cc
@@ -6,6 +6,7 @@ #include <memory> +#include "chromeos/assistant/internal/internal_util.h" #include "chromeos/services/assistant/public/cpp/features.h" #include "chromeos/services/libassistant/display_connection_impl.h" #include "chromeos/services/libassistant/public/mojom/speech_recognition_observer.mojom.h" @@ -14,6 +15,17 @@ namespace chromeos { namespace libassistant { +namespace { +// A macro which ensures we are running on the main thread. +#define ENSURE_MOJOM_THREAD(method, ...) \ + if (!mojom_task_runner_->RunsTasksInCurrentSequence()) { \ + mojom_task_runner_->PostTask( \ + FROM_HERE, \ + base::BindOnce(method, weak_factory_.GetWeakPtr(), ##__VA_ARGS__)); \ + return; \ + } +} // namespace + class DisplayController::EventObserver : public DisplayConnectionObserver { public: explicit EventObserver(DisplayController* parent) : parent_(parent) {} @@ -69,7 +81,60 @@ void DisplayController::OnAssistantManagerCreated( assistant_client::AssistantManager* assistant_manager, assistant_client::AssistantManagerInternal* assistant_manager_internal) { + DCHECK(assistant_manager_internal); assistant_manager_internal->SetDisplayConnection(display_connection_.get()); + + assistant_manager_internal_ = assistant_manager_internal; +} + +void DisplayController::OnDestroyingAssistantManager( + assistant_client::AssistantManager* assistant_manager, + assistant_client::AssistantManagerInternal* assistant_manager_internal) { + assistant_manager_internal_ = nullptr; +} + +// Called from Libassistant thread. +void DisplayController::OnVerifyAndroidApp( + const std::vector<chromeos::assistant::AndroidAppInfo>& apps_info, + const chromeos::assistant::InteractionInfo& interaction) { + ENSURE_MOJOM_THREAD(&DisplayController::OnVerifyAndroidApp, apps_info, + interaction); + + std::vector<chromeos::assistant::AndroidAppInfo> result_apps_info; + for (auto& app_info : apps_info) { + chromeos::assistant::AndroidAppInfo result_app_info(app_info); + auto app_status = GetAndroidAppStatus(app_info.package_name); + result_app_info.status = app_status; + result_apps_info.emplace_back(result_app_info); + } + + std::string interaction_proto = CreateVerifyProviderResponseInteraction( + interaction.interaction_id, result_apps_info); + + assistant_client::VoicelessOptions options; + options.obfuscated_gaia_id = interaction.user_id; + // Set the request to be user initiated so that a new conversation will be + // created to handle the client OPs in the response of this request. + options.is_user_initiated = true; + + assistant_manager_internal_->SendVoicelessInteraction( + interaction_proto, /*description=*/"verify_provider_response", options, + [](auto) {}); +} + +chromeos::assistant::AppStatus DisplayController::GetAndroidAppStatus( + const std::string& package_name) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + + for (auto& app_info : display_connection_->GetCachedAndroidAppList()) { + if (app_info.package_name == package_name) { + DVLOG(1) << "Assistant: App is available on the device."; + return assistant::AppStatus::kAvailable; + } + } + + DVLOG(1) << "Assistant: App is unavailable on the device"; + return assistant::AppStatus::kUnavailable; } } // namespace libassistant
diff --git a/chromeos/services/libassistant/display_controller.h b/chromeos/services/libassistant/display_controller.h index e796183..581105e2 100644 --- a/chromeos/services/libassistant/display_controller.h +++ b/chromeos/services/libassistant/display_controller.h
@@ -5,8 +5,11 @@ #ifndef CHROMEOS_SERVICES_LIBASSISTANT_DISPLAY_CONTROLLER_H_ #define CHROMEOS_SERVICES_LIBASSISTANT_DISPLAY_CONTROLLER_H_ +#include "base/sequenced_task_runner.h" +#include "chromeos/assistant/internal/action/assistant_action_observer.h" #include "chromeos/services/libassistant/assistant_manager_observer.h" #include "chromeos/services/libassistant/public/mojom/display_controller.mojom.h" +#include "libassistant/shared/internal_api/assistant_manager_internal.h" #include "mojo/public/cpp/bindings/receiver.h" #include "mojo/public/cpp/bindings/remote_set.h" @@ -23,8 +26,10 @@ class DisplayConnectionImpl; -class DisplayController : public mojom::DisplayController, - public AssistantManagerObserver { +class DisplayController + : public mojom::DisplayController, + public AssistantManagerObserver, + public chromeos::assistant::action::AssistantActionObserver { public: explicit DisplayController(mojo::RemoteSet<mojom::SpeechRecognitionObserver>* speech_recognition_observers); @@ -46,10 +51,24 @@ assistant_client::AssistantManager* assistant_manager, assistant_client::AssistantManagerInternal* assistant_manager_internal) override; + void OnDestroyingAssistantManager( + assistant_client::AssistantManager* assistant_manager, + assistant_client::AssistantManagerInternal* assistant_manager_internal) + override; + + // chromeos::assistant::action::AssistantActionObserver: + void OnVerifyAndroidApp( + const std::vector<chromeos::assistant::AndroidAppInfo>& apps_info, + const chromeos::assistant::InteractionInfo& interaction) override; private: class EventObserver; + // Checks if the requested Android App with |package_name| is available on the + // device. + chromeos::assistant::AppStatus GetAndroidAppStatus( + const std::string& package_name); + mojo::Receiver<mojom::DisplayController> receiver_{this}; std::unique_ptr<EventObserver> event_observer_; std::unique_ptr<DisplayConnectionImpl> display_connection_; @@ -57,6 +76,17 @@ // Owned by |LibassistantService|. mojo::RemoteSet<mojom::SpeechRecognitionObserver>& speech_recognition_observers_; + + assistant_client::AssistantManagerInternal* assistant_manager_internal_ = + nullptr; + + // The callbacks from Libassistant are called on a different sequence, + // so this sequence checker ensures that no other methods are called on the + // libassistant sequence. + SEQUENCE_CHECKER(sequence_checker_); + + scoped_refptr<base::SequencedTaskRunner> mojom_task_runner_; + base::WeakPtrFactory<DisplayController> weak_factory_{this}; }; } // namespace libassistant
diff --git a/chromeos/services/libassistant/libassistant_service.cc b/chromeos/services/libassistant/libassistant_service.cc index d18852f..0a3a466f 100644 --- a/chromeos/services/libassistant/libassistant_service.cc +++ b/chromeos/services/libassistant/libassistant_service.cc
@@ -83,6 +83,7 @@ service_controller_.AddAndFireAssistantManagerObserver(&timer_controller_); conversation_controller_.AddActionObserver(&device_settings_controller_); + conversation_controller_.AddActionObserver(&display_controller_); platform_api_.SetAudioInputProvider( &audio_input_controller_.audio_input_provider()); }
diff --git a/components/browser_ui/strings/android/browser_ui_strings.grd b/components/browser_ui/strings/android/browser_ui_strings.grd index 14b768f..ac88a5de 100644 --- a/components/browser_ui/strings/android/browser_ui_strings.grd +++ b/components/browser_ui/strings/android/browser_ui_strings.grd
@@ -300,9 +300,6 @@ <message name="IDS_MENU_ITEM_MOVE_TO_TOP" desc="Option in item menu. User can click the 'Move to top' option to move the item up to the top of its list. [CHAR-LIMIT=24]"> Move to top </message> - <message name="IDS_ALL" desc="Generic label for a button to show all items or options, for example a button to view all bookmarks. [CHAR-LIMIT=20]"> - All - </message> <message name="IDS_JUST_ONCE" desc="Generic label for a user to select an option for just once"> Just once </message>
diff --git a/components/browser_ui/strings/android/browser_ui_strings_grd/IDS_ALL.png.sha1 b/components/browser_ui/strings/android/browser_ui_strings_grd/IDS_ALL.png.sha1 deleted file mode 100644 index dbf500d..0000000 --- a/components/browser_ui/strings/android/browser_ui_strings_grd/IDS_ALL.png.sha1 +++ /dev/null
@@ -1 +0,0 @@ -6c3e2edf3a560e4fa08cdffdd9bbc0567b4197de \ No newline at end of file
diff --git a/components/component_updater/android/BUILD.gn b/components/component_updater/android/BUILD.gn index de9747bc..15ff397 100644 --- a/components/component_updater/android/BUILD.gn +++ b/components/component_updater/android/BUILD.gn
@@ -70,6 +70,7 @@ sources = [ "component_loader_policy.cc", "component_loader_policy.h", + "component_loader_policy_forward.h", ] deps = [ ":embedded_component_loader_jni_headers",
diff --git a/components/component_updater/android/component_loader_policy.cc b/components/component_updater/android/component_loader_policy.cc index e647649..b4b557c 100644 --- a/components/component_updater/android/component_loader_policy.cc +++ b/components/component_updater/android/component_loader_policy.cc
@@ -33,6 +33,7 @@ #include "base/task/thread_pool.h" #include "base/values.h" #include "base/version.h" +#include "components/component_updater/android/component_loader_policy_forward.h" #include "components/component_updater/android/embedded_component_loader_jni_headers/ComponentLoaderPolicyBridge_jni.h" #include "components/update_client/utils.h"
diff --git a/components/component_updater/android/component_loader_policy.h b/components/component_updater/android/component_loader_policy.h index f897f50..e032a35 100644 --- a/components/component_updater/android/component_loader_policy.h +++ b/components/component_updater/android/component_loader_policy.h
@@ -16,6 +16,7 @@ #include "base/containers/flat_map.h" #include "base/memory/scoped_refptr.h" #include "base/sequence_checker.h" +#include "components/component_updater/android/component_loader_policy_forward.h" namespace base { class Version; @@ -74,9 +75,6 @@ virtual void GetHash(std::vector<uint8_t>* hash) const = 0; }; -using ComponentLoaderPolicyVector = - std::vector<std::unique_ptr<ComponentLoaderPolicy>>; - // Provides a bridge from Java to native to receive callbacks from the Java // loader and pass it to the wrapped ComponentLoaderPolicy instance. //
diff --git a/components/component_updater/android/component_loader_policy_forward.h b/components/component_updater/android/component_loader_policy_forward.h new file mode 100644 index 0000000..f6f8753 --- /dev/null +++ b/components/component_updater/android/component_loader_policy_forward.h
@@ -0,0 +1,19 @@ +// Copyright 2021 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 COMPONENTS_COMPONENT_UPDATER_ANDROID_COMPONENT_LOADER_POLICY_FORWARD_H_ +#define COMPONENTS_COMPONENT_UPDATER_ANDROID_COMPONENT_LOADER_POLICY_FORWARD_H_ + +#include <memory> +#include <vector> + +namespace component_updater { +class ComponentLoaderPolicy; + +using ComponentLoaderPolicyVector = + std::vector<std::unique_ptr<ComponentLoaderPolicy>>; + +} // namespace component_updater + +#endif // COMPONENTS_COMPONENT_UPDATER_ANDROID_COMPONENT_LOADER_POLICY_FORWARD_H_
diff --git a/components/component_updater/installer_policies/origin_trials_component_installer.cc b/components/component_updater/installer_policies/origin_trials_component_installer.cc index 713c2e3..6122093 100644 --- a/components/component_updater/installer_policies/origin_trials_component_installer.cc +++ b/components/component_updater/installer_policies/origin_trials_component_installer.cc
@@ -4,7 +4,9 @@ #include "components/component_updater/installer_policies/origin_trials_component_installer.h" +#include <iterator> #include <utility> +#include <vector> #include "base/bind.h" #include "base/callback.h" @@ -48,6 +50,20 @@ } // namespace +// static +void OriginTrialsComponentInstallerPolicy::GetComponentHash( + std::vector<uint8_t>* hash) { + if (!hash) + return; + hash->assign(std::begin(kOriginTrialSha2Hash), + std::end(kOriginTrialSha2Hash)); +} + +void OriginTrialsComponentInstallerPolicy::GetHash( + std::vector<uint8_t>* hash) const { + GetComponentHash(hash); +} + bool OriginTrialsComponentInstallerPolicy::VerifyInstallation( const base::DictionaryValue& manifest, const base::FilePath& install_dir) const { @@ -83,14 +99,6 @@ return base::FilePath(FILE_PATH_LITERAL("OriginTrials")); } -void OriginTrialsComponentInstallerPolicy::GetHash( - std::vector<uint8_t>* hash) const { - if (!hash) - return; - hash->assign(kOriginTrialSha2Hash, - kOriginTrialSha2Hash + base::size(kOriginTrialSha2Hash)); -} - std::string OriginTrialsComponentInstallerPolicy::GetName() const { return "Origin Trials"; }
diff --git a/components/component_updater/installer_policies/origin_trials_component_installer.h b/components/component_updater/installer_policies/origin_trials_component_installer.h index 9e81f42..1d682b8 100644 --- a/components/component_updater/installer_policies/origin_trials_component_installer.h +++ b/components/component_updater/installer_policies/origin_trials_component_installer.h
@@ -21,6 +21,7 @@ class OriginTrialsComponentInstallerPolicy : public ComponentInstallerPolicy { public: + static void GetComponentHash(std::vector<uint8_t>* hash); OriginTrialsComponentInstallerPolicy() = default; ~OriginTrialsComponentInstallerPolicy() override = default; OriginTrialsComponentInstallerPolicy(
diff --git a/components/embedder_support/BUILD.gn b/components/embedder_support/BUILD.gn index d98e6eb..c78282a 100644 --- a/components/embedder_support/BUILD.gn +++ b/components/embedder_support/BUILD.gn
@@ -32,6 +32,7 @@ source_set("unit_tests") { testonly = true sources = [ + "origin_trials/component_updater_utils_unittest.cc", "origin_trials/origin_trial_policy_impl_unittest.cc", "user_agent_utils_unittest.cc", ] @@ -40,8 +41,11 @@ ":browser_util", "//base", "//base/test:test_support", + "//components/component_updater/installer_policies", "//components/embedder_support", "//components/embedder_support/origin_trials", + "//components/prefs", + "//components/prefs:test_support", "//components/version_info:version_info", "//content/public/common", "//mojo/core/embedder:embedder",
diff --git a/components/embedder_support/origin_trials/BUILD.gn b/components/embedder_support/origin_trials/BUILD.gn index 37c2d578..eac5ee1 100644 --- a/components/embedder_support/origin_trials/BUILD.gn +++ b/components/embedder_support/origin_trials/BUILD.gn
@@ -4,16 +4,21 @@ source_set("origin_trials") { sources = [ + "component_updater_utils.cc", + "component_updater_utils.h", "features.cc", "features.h", "origin_trial_policy_impl.cc", "origin_trial_policy_impl.h", + "origin_trial_prefs.cc", + "origin_trial_prefs.h", "pref_names.cc", "pref_names.h", ] deps = [ "//base", "//components/embedder_support", + "//components/prefs", "//content/public/common", "//third_party/blink/public/common", ]
diff --git a/components/embedder_support/origin_trials/DEPS b/components/embedder_support/origin_trials/DEPS index 1235202..f453b7d3 100644 --- a/components/embedder_support/origin_trials/DEPS +++ b/components/embedder_support/origin_trials/DEPS
@@ -1,4 +1,6 @@ include_rules = [ + "+components/component_updater/installer_policies", + "+components/prefs", "+content/public/common", "+third_party/blink/public/common", "+services/network/public/cpp/is_potentially_trustworthy.h",
diff --git a/components/embedder_support/origin_trials/component_updater_utils.cc b/components/embedder_support/origin_trials/component_updater_utils.cc new file mode 100644 index 0000000..5ef281e --- /dev/null +++ b/components/embedder_support/origin_trials/component_updater_utils.cc
@@ -0,0 +1,61 @@ +// Copyright 2021 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 "components/embedder_support/origin_trials/component_updater_utils.h" + +#include <memory> +#include <string> + +#include "base/check.h" +#include "base/values.h" +#include "components/embedder_support/origin_trials/pref_names.h" +#include "components/prefs/pref_service.h" +#include "components/prefs/scoped_user_pref_update.h" + +namespace { + +constexpr char kManifestPublicKeyPath[] = "origin-trials.public-key"; +constexpr char kManifestDisabledFeaturesPath[] = + "origin-trials.disabled-features"; +constexpr char kManifestDisabledTokenSignaturesPath[] = + "origin-trials.disabled-tokens.signatures"; + +} // namespace + +namespace embedder_support { + +void ReadOriginTrialsConfigAndPopulateLocalState( + PrefService* local_state, + std::unique_ptr<base::DictionaryValue> manifest) { + DCHECK(local_state); + + std::string override_public_key; + if (manifest->GetString(kManifestPublicKeyPath, &override_public_key)) { + local_state->Set(prefs::kOriginTrialPublicKey, + base::Value(override_public_key)); + } else { + local_state->ClearPref(prefs::kOriginTrialPublicKey); + } + base::ListValue* override_disabled_feature_list = nullptr; + const bool manifest_has_disabled_features = manifest->GetList( + kManifestDisabledFeaturesPath, &override_disabled_feature_list); + if (manifest_has_disabled_features && + !override_disabled_feature_list->empty()) { + ListPrefUpdate update(local_state, prefs::kOriginTrialDisabledFeatures); + update->Swap(override_disabled_feature_list); + } else { + local_state->ClearPref(prefs::kOriginTrialDisabledFeatures); + } + base::ListValue* disabled_tokens_list = nullptr; + const bool manifest_has_disabled_tokens = manifest->GetList( + kManifestDisabledTokenSignaturesPath, &disabled_tokens_list); + if (manifest_has_disabled_tokens && !disabled_tokens_list->empty()) { + ListPrefUpdate update(local_state, prefs::kOriginTrialDisabledTokens); + update->Swap(disabled_tokens_list); + } else { + local_state->ClearPref(prefs::kOriginTrialDisabledTokens); + } +} + +} // namespace embedder_support
diff --git a/components/embedder_support/origin_trials/component_updater_utils.h b/components/embedder_support/origin_trials/component_updater_utils.h new file mode 100644 index 0000000..957fc77 --- /dev/null +++ b/components/embedder_support/origin_trials/component_updater_utils.h
@@ -0,0 +1,25 @@ +// Copyright 2021 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 COMPONENTS_EMBEDDER_SUPPORT_ORIGIN_TRIALS_COMPONENT_UPDATER_UTILS_H_ +#define COMPONENTS_EMBEDDER_SUPPORT_ORIGIN_TRIALS_COMPONENT_UPDATER_UTILS_H_ + +#include <memory> + +#include "base/values.h" + +class PrefService; + +namespace embedder_support { + +// Read the configuration from |manifest| and set values in |local_state|. +// If an individual configuration value is missing, reset values in +// local_state|. +void ReadOriginTrialsConfigAndPopulateLocalState( + PrefService* local_state, + std::unique_ptr<base::DictionaryValue> manifest); + +} // namespace embedder_support + +#endif // COMPONENTS_EMBEDDER_SUPPORT_ORIGIN_TRIALS_COMPONENT_UPDATER_UTILS_H_
diff --git a/components/embedder_support/origin_trials/component_updater_utils_unittest.cc b/components/embedder_support/origin_trials/component_updater_utils_unittest.cc new file mode 100644 index 0000000..e18e6a8 --- /dev/null +++ b/components/embedder_support/origin_trials/component_updater_utils_unittest.cc
@@ -0,0 +1,323 @@ +// Copyright 2021 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 "components/embedder_support/origin_trials/component_updater_utils.h" + +#include <string> +#include <utility> + +#include "base/files/scoped_temp_dir.h" +#include "base/values.h" +#include "base/version.h" +#include "components/component_updater/installer_policies/origin_trials_component_installer.h" +#include "components/embedder_support/origin_trials/origin_trial_prefs.h" +#include "components/embedder_support/origin_trials/pref_names.h" +#include "components/prefs/scoped_user_pref_update.h" +#include "components/prefs/testing_pref_service.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "testing/platform_test.h" + +namespace { + +// Mirror the constants used in the component installer. Do not share the +// constants, as want to catch inadvertent changes in the tests. The keys will +// will be generated server-side, so any changes need to be intentional and +// coordinated. +static const char kManifestOriginTrialsKey[] = "origin-trials"; +static const char kManifestPublicKeyPath[] = "origin-trials.public-key"; +static const char kManifestDisabledFeaturesPath[] = + "origin-trials.disabled-features"; +static const char kManifestDisabledTokensPath[] = + "origin-trials.disabled-tokens"; +static const char kManifestDisabledTokenSignaturesPath[] = + "origin-trials.disabled-tokens.signatures"; + +static const char kExistingPublicKey[] = "existing public key"; +static const char kNewPublicKey[] = "new public key"; +static const char kExistingDisabledFeature[] = "already disabled"; +static const std::vector<std::string> kExistingDisabledFeatures = { + kExistingDisabledFeature}; +static const char kNewDisabledFeature1[] = "newly disabled 1"; +static const char kNewDisabledFeature2[] = "newly disabled 2"; +static const std::vector<std::string> kNewDisabledFeatures = { + kNewDisabledFeature1, kNewDisabledFeature2}; +static const char kExistingDisabledToken[] = "already disabled token"; +static const std::vector<std::string> kExistingDisabledTokens = { + kExistingDisabledToken}; +static const char kNewDisabledToken1[] = "newly disabled token 1"; +static const char kNewDisabledToken2[] = "newly disabled token 2"; +static const std::vector<std::string> kNewDisabledTokens = {kNewDisabledToken1, + kNewDisabledToken2}; + +} // namespace + +namespace component_updater { + +class OriginTrialsComponentInstallerTest : public PlatformTest { + public: + OriginTrialsComponentInstallerTest() = default; + + void SetUp() override { + PlatformTest::SetUp(); + + ASSERT_TRUE(temp_dir_.CreateUniqueTempDir()); + embedder_support::OriginTrialPrefs::RegisterPrefs(local_state_.registry()); + policy_ = std::make_unique<OriginTrialsComponentInstallerPolicy>(); + } + + void LoadUpdates(std::unique_ptr<base::DictionaryValue> manifest) { + if (!manifest) { + manifest = std::make_unique<base::DictionaryValue>(); + manifest->Set(kManifestOriginTrialsKey, std::make_unique<base::Value>()); + } + ASSERT_TRUE(policy_->VerifyInstallation(*manifest, temp_dir_.GetPath())); + embedder_support::ReadOriginTrialsConfigAndPopulateLocalState( + local_state(), std::move(manifest)); + } + + void AddDisabledFeaturesToPrefs(const std::vector<std::string>& features) { + base::ListValue disabled_feature_list; + disabled_feature_list.AppendStrings(features); + ListPrefUpdate update( + local_state(), embedder_support::prefs::kOriginTrialDisabledFeatures); + update->Swap(&disabled_feature_list); + } + + void CheckDisabledFeaturesPrefs(const std::vector<std::string>& features) { + ASSERT_FALSE(features.empty()); + + ASSERT_TRUE(local_state()->HasPrefPath( + embedder_support::prefs::kOriginTrialDisabledFeatures)); + + const base::ListValue* disabled_feature_list = local_state()->GetList( + embedder_support::prefs::kOriginTrialDisabledFeatures); + ASSERT_TRUE(disabled_feature_list); + + ASSERT_EQ(features.size(), disabled_feature_list->GetSize()); + + std::string disabled_feature; + for (size_t i = 0; i < features.size(); ++i) { + const bool found = disabled_feature_list->GetString(i, &disabled_feature); + EXPECT_TRUE(found) << "Entry not found or not a string at index " << i; + if (!found) { + continue; + } + EXPECT_EQ(features[i], disabled_feature) + << "Feature lists differ at index " << i; + } + } + + void AddDisabledTokensToPrefs(const std::vector<std::string>& tokens) { + base::ListValue disabled_token_list; + disabled_token_list.AppendStrings(tokens); + ListPrefUpdate update(local_state(), + embedder_support::prefs::kOriginTrialDisabledTokens); + update->Swap(&disabled_token_list); + } + + void CheckDisabledTokensPrefs(const std::vector<std::string>& tokens) { + ASSERT_FALSE(tokens.empty()); + + ASSERT_TRUE(local_state()->HasPrefPath( + embedder_support::prefs::kOriginTrialDisabledTokens)); + + const base::ListValue* disabled_token_list = local_state()->GetList( + embedder_support::prefs::kOriginTrialDisabledTokens); + ASSERT_TRUE(disabled_token_list); + + ASSERT_EQ(tokens.size(), disabled_token_list->GetSize()); + + std::string disabled_token; + for (size_t i = 0; i < tokens.size(); ++i) { + const bool found = disabled_token_list->GetString(i, &disabled_token); + EXPECT_TRUE(found) << "Entry not found or not a string at index " << i; + if (!found) { + continue; + } + EXPECT_EQ(tokens[i], disabled_token) + << "Token lists differ at index " << i; + } + } + + PrefService* local_state() { return &local_state_; } + + protected: + base::ScopedTempDir temp_dir_; + TestingPrefServiceSimple local_state_; + std::unique_ptr<ComponentInstallerPolicy> policy_; + + private: + DISALLOW_COPY_AND_ASSIGN(OriginTrialsComponentInstallerTest); +}; + +TEST_F(OriginTrialsComponentInstallerTest, + PublicKeyResetToDefaultWhenOverrideMissing) { + local_state()->SetString(embedder_support::prefs::kOriginTrialPublicKey, + kExistingPublicKey); + ASSERT_EQ( + kExistingPublicKey, + local_state()->GetString(embedder_support::prefs::kOriginTrialPublicKey)); + + // Load with empty section in manifest + LoadUpdates(nullptr); + + EXPECT_FALSE(local_state()->HasPrefPath( + embedder_support::prefs::kOriginTrialPublicKey)); +} + +TEST_F(OriginTrialsComponentInstallerTest, PublicKeySetWhenOverrideExists) { + ASSERT_FALSE(local_state()->HasPrefPath( + embedder_support::prefs::kOriginTrialPublicKey)); + + auto manifest = std::make_unique<base::DictionaryValue>(); + manifest->SetString(kManifestPublicKeyPath, kNewPublicKey); + LoadUpdates(std::move(manifest)); + + EXPECT_EQ(kNewPublicKey, local_state()->GetString( + embedder_support::prefs::kOriginTrialPublicKey)); +} + +TEST_F(OriginTrialsComponentInstallerTest, + DisabledFeaturesResetToDefaultWhenListMissing) { + AddDisabledFeaturesToPrefs(kExistingDisabledFeatures); + ASSERT_TRUE(local_state()->HasPrefPath( + embedder_support::prefs::kOriginTrialDisabledFeatures)); + + // Load with empty section in manifest + LoadUpdates(nullptr); + + EXPECT_FALSE(local_state()->HasPrefPath( + embedder_support::prefs::kOriginTrialDisabledFeatures)); +} + +TEST_F(OriginTrialsComponentInstallerTest, + DisabledFeaturesResetToDefaultWhenListEmpty) { + AddDisabledFeaturesToPrefs(kExistingDisabledFeatures); + ASSERT_TRUE(local_state()->HasPrefPath( + embedder_support::prefs::kOriginTrialDisabledFeatures)); + + auto manifest = std::make_unique<base::DictionaryValue>(); + auto disabled_feature_list = std::make_unique<base::ListValue>(); + manifest->Set(kManifestDisabledFeaturesPath, + std::move(disabled_feature_list)); + + LoadUpdates(std::move(manifest)); + + EXPECT_FALSE(local_state()->HasPrefPath( + embedder_support::prefs::kOriginTrialDisabledFeatures)); +} + +TEST_F(OriginTrialsComponentInstallerTest, DisabledFeaturesSetWhenListExists) { + ASSERT_FALSE(local_state()->HasPrefPath( + embedder_support::prefs::kOriginTrialDisabledFeatures)); + + auto manifest = std::make_unique<base::DictionaryValue>(); + auto disabled_feature_list = std::make_unique<base::ListValue>(); + disabled_feature_list->AppendString(kNewDisabledFeature1); + manifest->Set(kManifestDisabledFeaturesPath, + std::move(disabled_feature_list)); + + LoadUpdates(std::move(manifest)); + + std::vector<std::string> features = {kNewDisabledFeature1}; + CheckDisabledFeaturesPrefs(features); +} + +TEST_F(OriginTrialsComponentInstallerTest, + DisabledFeaturesReplacedWhenListExists) { + AddDisabledFeaturesToPrefs(kExistingDisabledFeatures); + ASSERT_TRUE(local_state()->HasPrefPath( + embedder_support::prefs::kOriginTrialDisabledFeatures)); + + auto manifest = std::make_unique<base::DictionaryValue>(); + auto disabled_feature_list = std::make_unique<base::ListValue>(); + disabled_feature_list->AppendStrings(kNewDisabledFeatures); + manifest->Set(kManifestDisabledFeaturesPath, + std::move(disabled_feature_list)); + + LoadUpdates(std::move(manifest)); + + CheckDisabledFeaturesPrefs(kNewDisabledFeatures); +} + +TEST_F(OriginTrialsComponentInstallerTest, + DisabledTokensResetToDefaultWhenListMissing) { + AddDisabledTokensToPrefs(kExistingDisabledTokens); + ASSERT_TRUE(local_state()->HasPrefPath( + embedder_support::prefs::kOriginTrialDisabledTokens)); + + // Load with empty section in manifest + LoadUpdates(nullptr); + + EXPECT_FALSE(local_state()->HasPrefPath( + embedder_support::prefs::kOriginTrialDisabledTokens)); +} + +TEST_F(OriginTrialsComponentInstallerTest, + DisabledTokensResetToDefaultWhenKeyExistsAndListMissing) { + AddDisabledTokensToPrefs(kExistingDisabledTokens); + ASSERT_TRUE(local_state()->HasPrefPath( + embedder_support::prefs::kOriginTrialDisabledTokens)); + + // Load with disabled tokens key in manifest, but no list values + auto manifest = std::make_unique<base::DictionaryValue>(); + manifest->Set(kManifestDisabledTokensPath, std::make_unique<base::Value>()); + + LoadUpdates(std::move(manifest)); + + EXPECT_FALSE(local_state()->HasPrefPath( + embedder_support::prefs::kOriginTrialDisabledTokens)); +} + +TEST_F(OriginTrialsComponentInstallerTest, + DisabledTokensResetToDefaultWhenListEmpty) { + AddDisabledTokensToPrefs(kExistingDisabledTokens); + ASSERT_TRUE(local_state()->HasPrefPath( + embedder_support::prefs::kOriginTrialDisabledTokens)); + + auto manifest = std::make_unique<base::DictionaryValue>(); + auto disabled_token_list = std::make_unique<base::ListValue>(); + manifest->Set(kManifestDisabledTokenSignaturesPath, + std::move(disabled_token_list)); + + LoadUpdates(std::move(manifest)); + + EXPECT_FALSE(local_state()->HasPrefPath( + embedder_support::prefs::kOriginTrialDisabledTokens)); +} + +TEST_F(OriginTrialsComponentInstallerTest, DisabledTokensSetWhenListExists) { + ASSERT_FALSE(local_state()->HasPrefPath( + embedder_support::prefs::kOriginTrialDisabledTokens)); + + auto manifest = std::make_unique<base::DictionaryValue>(); + auto disabled_token_list = std::make_unique<base::ListValue>(); + disabled_token_list->AppendString(kNewDisabledToken1); + manifest->Set(kManifestDisabledTokenSignaturesPath, + std::move(disabled_token_list)); + + LoadUpdates(std::move(manifest)); + + std::vector<std::string> tokens = {kNewDisabledToken1}; + CheckDisabledTokensPrefs(tokens); +} + +TEST_F(OriginTrialsComponentInstallerTest, + DisabledTokensReplacedWhenListExists) { + AddDisabledTokensToPrefs(kExistingDisabledTokens); + ASSERT_TRUE(local_state()->HasPrefPath( + embedder_support::prefs::kOriginTrialDisabledTokens)); + + auto manifest = std::make_unique<base::DictionaryValue>(); + auto disabled_token_list = std::make_unique<base::ListValue>(); + disabled_token_list->AppendStrings(kNewDisabledTokens); + manifest->Set(kManifestDisabledTokenSignaturesPath, + std::move(disabled_token_list)); + + LoadUpdates(std::move(manifest)); + + CheckDisabledTokensPrefs(kNewDisabledTokens); +} + +} // namespace component_updater
diff --git a/chrome/browser/prefs/origin_trial_prefs.cc b/components/embedder_support/origin_trials/origin_trial_prefs.cc similarity index 82% rename from chrome/browser/prefs/origin_trial_prefs.cc rename to components/embedder_support/origin_trials/origin_trial_prefs.cc index e71f126..719a74a 100644 --- a/chrome/browser/prefs/origin_trial_prefs.cc +++ b/components/embedder_support/origin_trials/origin_trial_prefs.cc
@@ -2,11 +2,13 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "chrome/browser/prefs/origin_trial_prefs.h" +#include "components/embedder_support/origin_trials/origin_trial_prefs.h" #include "components/embedder_support/origin_trials/pref_names.h" #include "components/prefs/pref_registry_simple.h" +namespace embedder_support { + // static void OriginTrialPrefs::RegisterPrefs(PrefRegistrySimple* registry) { registry->RegisterStringPref(embedder_support::prefs::kOriginTrialPublicKey, @@ -16,3 +18,5 @@ registry->RegisterListPref( embedder_support::prefs::kOriginTrialDisabledTokens); } + +} // namespace embedder_support
diff --git a/components/embedder_support/origin_trials/origin_trial_prefs.h b/components/embedder_support/origin_trials/origin_trial_prefs.h new file mode 100644 index 0000000..da723445 --- /dev/null +++ b/components/embedder_support/origin_trials/origin_trial_prefs.h
@@ -0,0 +1,19 @@ +// 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. + +#ifndef COMPONENTS_EMBEDDER_SUPPORT_ORIGIN_TRIALS_ORIGIN_TRIAL_PREFS_H_ +#define COMPONENTS_EMBEDDER_SUPPORT_ORIGIN_TRIALS_ORIGIN_TRIAL_PREFS_H_ + +class PrefRegistrySimple; + +namespace embedder_support { + +class OriginTrialPrefs { + public: + static void RegisterPrefs(PrefRegistrySimple* registry); +}; + +} // namespace embedder_support + +#endif // COMPONENTS_EMBEDDER_SUPPORT_ORIGIN_TRIALS_ORIGIN_TRIAL_PREFS_H_
diff --git a/components/feed/core/proto/v2/store.proto b/components/feed/core/proto/v2/store.proto index cfb97a5..67a0b71 100644 --- a/components/feed/core/proto/v2/store.proto +++ b/components/feed/core/proto/v2/store.proto
@@ -71,6 +71,15 @@ int64 expiry_time_ms = 2; } + // Metadata about a specific stream. + message StreamMetadata { + string stream_id = 1; + // If this stream has been viewed before, this timestamp matches the + // 'StreamData.last_added_time_millis' value of that stream. This is used to + // determine whether the user has already seen a stored stream's data. + int64 view_time_millis = 2; + } + // Token used to read or write to the same storage. bytes consistency_token = 1; // ID for the next pending action. @@ -83,6 +92,7 @@ // "shared_state" // records. int32 stream_schema_version = 4; + repeated StreamMetadata stream_metadata = 5; } // A set of StreamStructures that should be applied to a stream.
diff --git a/components/feed/core/v2/BUILD.gn b/components/feed/core/v2/BUILD.gn index df5ca25c..bc300a0 100644 --- a/components/feed/core/v2/BUILD.gn +++ b/components/feed/core/v2/BUILD.gn
@@ -19,6 +19,8 @@ "feed_store.h", "feed_stream.cc", "feed_stream.h", + "feedstore_util.cc", + "feedstore_util.h", "image_fetcher.cc", "image_fetcher.h", "metrics_reporter.cc", @@ -42,6 +44,8 @@ "public/web_feed_subscriptions.h", "request_throttler.cc", "request_throttler.h", + "stream/unread_content_notifier.cc", + "stream/unread_content_notifier.h", "stream_model.cc", "stream_model.h", "stream_model/ephemeral_change.cc", @@ -68,6 +72,8 @@ "tasks/wait_for_store_initialize_task.h", "web_feed_index.cc", "web_feed_index.h", + "wire_response_translator.cc", + "wire_response_translator.h", ] deps = [ ":common", @@ -141,6 +147,7 @@ "feed_network_impl_unittest.cc", "feed_store_unittest.cc", "feed_stream_unittest.cc", + "feedstore_util_unittest.cc", "image_fetcher_unittest.cc", "metrics_reporter_unittest.cc", "persistent_key_value_store_impl_unittest.cc", @@ -156,6 +163,7 @@ "test/proto_printer.h", "test/stream_builder.cc", "test/stream_builder.h", + "test/test_util.h", "web_feed_index_unittest.cc", ]
diff --git a/components/feed/core/v2/feed_store.cc b/components/feed/core/v2/feed_store.cc index 841070e..4c0bd9f 100644 --- a/components/feed/core/v2/feed_store.cc +++ b/components/feed/core/v2/feed_store.cc
@@ -18,6 +18,7 @@ #include "base/strings/string_util.h" #include "base/task/post_task.h" #include "base/task/thread_pool.h" +#include "components/feed/core/v2/feedstore_util.h" #include "components/feed/core/v2/protocol_translator.h" #include "components/leveldb_proto/public/proto_database_provider.h" @@ -39,15 +40,6 @@ constexpr char kMetadataKey[] = "m"; constexpr char kSubscribedFeedsKey[] = "subs"; constexpr char kRecommendedIndexKey[] = "recommendedIndex"; -constexpr base::StringPiece kForYouStreamId{"i"}; -constexpr base::StringPiece kFollowStreamId{"w"}; - -base::StringPiece StreamId(const StreamType& stream_type) { - if (stream_type.IsForYou()) - return kForYouStreamId; - DCHECK(stream_type.IsWebFeed()); - return kFollowStreamId; -} leveldb::ReadOptions CreateReadOptions() { leveldb::ReadOptions opts; @@ -64,7 +56,7 @@ return base::StrCat({"S/", stream_id}); } std::string StreamDataKey(const StreamType& stream_type) { - return StreamDataKey(StreamId(stream_type)); + return StreamDataKey(feedstore::StreamId(stream_type)); } std::string ContentKey(const base::StringPiece stream_type, const feedwire::ContentId& content_id) { @@ -73,7 +65,7 @@ } std::string ContentKey(const StreamType& stream_type, const feedwire::ContentId& content_id) { - return ContentKey(StreamId(stream_type), content_id); + return ContentKey(feedstore::StreamId(stream_type), content_id); } std::string SharedStateKey(const base::StringPiece stream_type, const feedwire::ContentId& content_id) { @@ -82,7 +74,7 @@ } std::string SharedStateKey(const StreamType& stream_type, const feedwire::ContentId& content_id) { - return SharedStateKey(StreamId(stream_type), content_id); + return SharedStateKey(feedstore::StreamId(stream_type), content_id); } std::string LocalActionKey(int64_t id) { return kLocalActionPrefix + base::NumberToString(id); @@ -103,7 +95,7 @@ class StreamKeyMatcher { public: explicit StreamKeyMatcher(const StreamType& stream_type) { - stream_id_ = StreamId(stream_type).as_string(); + stream_id_ = feedstore::StreamId(stream_type).as_string(); stream_id_plus_slash_ = stream_id_ + '/'; } @@ -240,7 +232,7 @@ int32_t structure_set_sequence_number, const StreamType& stream_type, std::unique_ptr<StreamModelUpdateRequest> update_request) { - base::StringPiece stream_id = StreamId(stream_type); + base::StringPiece stream_id = feedstore::StreamId(stream_type); auto updates = std::make_unique< std::vector<std::pair<std::string, feedstore::Record>>>(); update_request->stream_data.set_stream_id(stream_id.as_string()); @@ -371,8 +363,9 @@ }; database_->LoadEntriesWithFilter( - base::BindRepeating(filter, StreamDataKey(stream_type), - base::StrCat({"T/", StreamId(stream_type), "/"})), + base::BindRepeating( + filter, StreamDataKey(stream_type), + base::StrCat({"T/", feedstore::StreamId(stream_type), "/"})), CreateReadOptions(), /*target_prefix=*/"", base::BindOnce(&FeedStore::OnLoadStreamFinished, GetWeakPtr(), @@ -393,13 +386,13 @@ switch (record.data_case()) { case feedstore::Record::kStreamData: result.stream_data = std::move(*record.mutable_stream_data()); - DLOG_IF(ERROR, - result.stream_data.stream_id() != StreamId(stream_type)) + DLOG_IF(ERROR, result.stream_data.stream_id() != + feedstore::StreamId(stream_type)) << "Read a record with the wrong stream_id"; break; case feedstore::Record::kStreamStructures: DLOG_IF(ERROR, record.stream_structures().stream_id() != - StreamId(stream_type)) + feedstore::StreamId(stream_type)) << "Read a record with the wrong stream_id"; result.stream_structures.push_back( std::move(*record.mutable_stream_structures())); @@ -488,7 +481,7 @@ const StreamType& stream_type, int32_t sequence_number, std::vector<feedstore::DataOperation> operations) { - base::StringPiece stream_id = StreamId(stream_type); + base::StringPiece stream_id = feedstore::StreamId(stream_type); std::vector<feedstore::Record> records; feedstore::Record structures_record; feedstore::StreamStructureSet& structure_set = @@ -502,7 +495,7 @@ records.push_back(std::move(record)); } } - structure_set.set_stream_id(StreamId(stream_type).as_string()); + structure_set.set_stream_id(feedstore::StreamId(stream_type).as_string()); structure_set.set_sequence_number(sequence_number); records.push_back(std::move(structures_record));
diff --git a/components/feed/core/v2/feed_store_unittest.cc b/components/feed/core/v2/feed_store_unittest.cc index aca417e..f5fb1ff 100644 --- a/components/feed/core/v2/feed_store_unittest.cc +++ b/components/feed/core/v2/feed_store_unittest.cc
@@ -19,6 +19,7 @@ #include "components/feed/core/v2/test/callback_receiver.h" #include "components/feed/core/v2/test/proto_printer.h" #include "components/feed/core/v2/test/stream_builder.h" +#include "components/feed/core/v2/test/test_util.h" #include "components/feed/feed_feature_list.h" #include "components/leveldb_proto/testing/fake_db.h" #include "testing/gtest/include/gtest/gtest.h" @@ -26,15 +27,6 @@ namespace feed { namespace { -// This is EXPECT_EQ, but also dumps the string values for ease of reading. -#define EXPECT_STRINGS_EQUAL(WANT, GOT) \ - { \ - std::string want_param_ = (WANT), got_param_ = (GOT); \ - EXPECT_EQ(want_param_, got_param_) << "Wanted:\n" \ - << (want_param_) << "\nBut got:\n" \ - << (got_param_); \ - } - using LoadStreamResult = FeedStore::LoadStreamResult; std::string KeyForContentId(base::StringPiece prefix,
diff --git a/components/feed/core/v2/feed_stream.cc b/components/feed/core/v2/feed_stream.cc index c415f239..a92f48b 100644 --- a/components/feed/core/v2/feed_stream.cc +++ b/components/feed/core/v2/feed_stream.cc
@@ -12,6 +12,7 @@ #include "base/feature_list.h" #include "base/metrics/histogram_macros.h" #include "base/notreached.h" +#include "base/stl_util.h" #include "base/threading/thread_task_runner_handle.h" #include "base/time/time.h" #include "components/feed/core/common/pref_names.h" @@ -23,6 +24,7 @@ #include "components/feed/core/v2/enums.h" #include "components/feed/core/v2/feed_network.h" #include "components/feed/core/v2/feed_store.h" +#include "components/feed/core/v2/feedstore_util.h" #include "components/feed/core/v2/image_fetcher.h" #include "components/feed/core/v2/metrics_reporter.h" #include "components/feed/core/v2/offline_page_spy.h" @@ -32,6 +34,7 @@ #include "components/feed/core/v2/public/refresh_task_scheduler.h" #include "components/feed/core/v2/public/types.h" #include "components/feed/core/v2/scheduling.h" +#include "components/feed/core/v2/stream/unread_content_notifier.h" #include "components/feed/core/v2/stream_model.h" #include "components/feed/core/v2/surface_updater.h" #include "components/feed/core/v2/tasks/clear_all_task.h" @@ -104,70 +107,6 @@ FeedStream* stream_; }; -RefreshResponseData FeedStream::WireResponseTranslator::TranslateWireResponse( - feedwire::Response response, - StreamModelUpdateRequest::Source source, - bool was_signed_in_request, - base::Time current_time) const { - return ::feed::TranslateWireResponse(std::move(response), source, - was_signed_in_request, current_time); -} - -FeedStream::Metadata::Metadata(FeedStore* store) : store_(store) {} -FeedStream::Metadata::~Metadata() = default; - -void FeedStream::Metadata::Populate(feedstore::Metadata metadata) { - metadata_ = std::move(metadata); -} - -const std::string& FeedStream::Metadata::GetConsistencyToken() const { - return metadata_.consistency_token(); -} - -void FeedStream::Metadata::SetConsistencyToken(std::string consistency_token) { - metadata_.set_consistency_token(std::move(consistency_token)); - store_->WriteMetadata(metadata_, base::DoNothing()); -} - -const std::string& FeedStream::Metadata::GetSessionIdToken() const { - return metadata_.session_id().token(); -} - -base::Time FeedStream::Metadata::GetSessionIdExpiryTime() const { - return base::Time::FromDeltaSinceWindowsEpoch( - base::TimeDelta::FromMilliseconds( - metadata_.session_id().expiry_time_ms())); -} - -void FeedStream::Metadata::SetSessionId(std::string token, - base::Time expiry_time) { - feedstore::Metadata::SessionID* session_id = metadata_.mutable_session_id(); - session_id->set_token(std::move(token)); - session_id->set_expiry_time_ms( - expiry_time.ToDeltaSinceWindowsEpoch().InMilliseconds()); - store_->WriteMetadata(metadata_, base::DoNothing()); -} - -void FeedStream::Metadata::MaybeUpdateSessionId( - base::Optional<std::string> token) { - if (token && metadata_.session_id().token() != *token) { - base::Time expiry_time = - token->empty() ? base::Time() - : base::Time::Now() + GetFeedConfig().session_id_max_age; - SetSessionId(*token, expiry_time); - } -} - -LocalActionId FeedStream::Metadata::GetNextActionId() { - uint32_t id = metadata_.next_action_id(); - // Never use 0, as that's an invalid LocalActionId. - if (id == 0) - ++id; - metadata_.set_next_action_id(id + 1); - store_->WriteMetadata(metadata_, base::DoNothing()); - return LocalActionId(id); -} - FeedStream::Stream::Stream() = default; FeedStream::Stream::~Stream() = default; @@ -194,7 +133,6 @@ chrome_info_(chrome_info), task_queue_(this), request_throttler_(profile_prefs), - metadata_(feed_store), notice_card_tracker_(profile_prefs) { static WireResponseTranslator default_translator; wire_response_translator_ = &default_translator; @@ -270,7 +208,7 @@ } void FeedStream::InitializeComplete(WaitForStoreInitializeTask::Result result) { - metadata_.Populate(result.metadata); + metadata_ = std::move(result.metadata); // TODO(crbug/1152592): Test that the index is populated once there's an API // to access the data. web_feed_index_.Populate(result.web_feed_startup_data); @@ -284,7 +222,7 @@ result.loaded_new_content_from_network, result.stored_content_age, std::move(result.latencies)); UpdateIsActivityLoggingEnabled(result.stream_type); - + MaybeNotifyHasUnreadContent(result.stream_type); stream.model_loading_in_progress = false; stream.surface_updater->LoadStreamComplete(stream.model != nullptr, result.final_status); @@ -320,7 +258,18 @@ } std::string FeedStream::GetSessionId() const { - return GetMetadata()->GetSessionIdToken(); + return metadata_.session_id().token(); +} +void FeedStream::SetMetadata(feedstore::Metadata metadata) { + metadata_ = std::move(metadata); + store_->WriteMetadata(metadata_, base::DoNothing()); +} +bool FeedStream::SetMetadata(base::Optional<feedstore::Metadata> metadata) { + if (metadata) { + SetMetadata(std::move(*metadata)); + return true; + } + return false; } void FeedStream::PrefetchImage(const GURL& url) { @@ -358,6 +307,23 @@ ScheduleModelUnloadIfNoSurfacesAttached(surface->GetStreamType()); } +void FeedStream::AddUnreadContentObserver(const StreamType& stream_type, + UnreadContentObserver* observer) { + GetStream(stream_type) + .unread_content_notifiers.emplace_back(observer->GetWeakPtr()); + MaybeNotifyHasUnreadContent(stream_type); +} + +void FeedStream::RemoveUnreadContentObserver(const StreamType& stream_type, + UnreadContentObserver* observer) { + Stream& stream = GetStream(stream_type); + auto predicate = [&](const UnreadContentNotifier& notifier) { + UnreadContentObserver* ptr = notifier.observer().get(); + return ptr == nullptr || observer == ptr; + }; + base::EraseIf(stream.unread_content_notifiers, predicate); +} + void FeedStream::ScheduleModelUnloadIfNoSurfacesAttached( const StreamType& stream_type) { Stream& stream = GetStream(stream_type); @@ -423,10 +389,10 @@ metrics_reporter_->OnLoadMoreBegin(surface.GetSurfaceId()); stream.surface_updater->SetLoadingMore(true); - // Have at most one in-flight LoadMore() request. Send the result to all - // requestors. - load_more_complete_callbacks_.push_back(std::move(callback)); - if (load_more_complete_callbacks_.size() == 1) { + // Have at most one in-flight LoadMore() request per stream. Send the result + // to all requestors. + stream.load_more_complete_callbacks.push_back(std::move(callback)); + if (stream.load_more_complete_callbacks.size() == 1) { task_queue_.AddTask(std::make_unique<LoadMoreTask>( surface.GetStreamType(), this, base::BindOnce(&FeedStream::LoadMoreComplete, base::Unretained(this)))); @@ -439,7 +405,7 @@ metrics_reporter_->OnLoadMore(result.final_status); stream.surface_updater->SetLoadingMore(false); std::vector<base::OnceCallback<void(bool)>> moved_callbacks = - std::move(load_more_complete_callbacks_); + std::move(stream.load_more_complete_callbacks); bool success = result.final_status == LoadStreamStatus::kLoadedFromNetwork; for (auto& callback : moved_callbacks) { std::move(callback).Run(success); @@ -736,7 +702,7 @@ if (stream->model->signed_in()) { result.client_instance_id = GetClientInstanceId(); } else { - result.session_id = GetMetadata()->GetSessionIdToken(); + result.session_id = GetSessionId(); } } else { // The request is for the first page of the feed. Use client_instance_id @@ -745,9 +711,9 @@ if (delegate_->IsSignedIn() && !ShouldForceSignedOutFeedQueryRequest(stream_type)) { result.client_instance_id = GetClientInstanceId(); - } else if (!GetMetadata()->GetSessionIdToken().empty() && - GetMetadata()->GetSessionIdExpiryTime() > base::Time::Now()) { - result.session_id = GetMetadata()->GetSessionIdToken(); + } else if (!GetSessionId().empty() && feedstore::GetSessionIdExpiryTime( + metadata_) > base::Time::Now()) { + result.session_id = GetSessionId(); } } @@ -827,6 +793,8 @@ } void FeedStream::BackgroundRefreshComplete(LoadStreamTask::Result result) { + if (!result.last_added_time.is_null()) + GetStream(result.stream_type).last_updated_time = result.last_added_time; metrics_reporter_->OnBackgroundRefresh(result.final_status); if (result.loaded_new_content_from_network) { if (result.stream_type.IsForYou()) @@ -834,6 +802,8 @@ } MaybeReportNewSuggestionsAvailable(result); + MaybeNotifyHasUnreadContent(result.stream_type); + // Add prefetch images to task queue without waiting to finish // since we treat them as best-effort. if (result.stream_type.IsForYou()) @@ -863,7 +833,6 @@ void FeedStream::ClearAll() { metrics_reporter_->OnClearAll(base::Time::Now() - GetLastFetchTime()); - task_queue_.AddTask(std::make_unique<ClearAllTask>(this)); } @@ -871,7 +840,8 @@ // Clear any experiments stored. feed::prefs::SetExperiments({}, *profile_prefs_); feed::prefs::ClearClientInstanceId(*profile_prefs_); - metadata_.Populate(feedstore::Metadata()); + SetMetadata(feedstore::MakeMetadata()); + delegate_->ClearAll(); for (auto& item : streams_) { @@ -915,11 +885,13 @@ stream.model = std::move(model); stream.model->SetStreamType(stream_type); stream.model->SetStoreObserver(this); + stream.last_updated_time = stream.model->GetLastAddedTime(); stream.surface_updater->SetModel(stream.model.get()); if (stream.type.IsForYou()) { offline_page_spy_->SetModel(stream.model.get()); } ScheduleModelUnloadIfNoSurfacesAttached(stream_type); + MaybeNotifyHasUnreadContent(stream_type); } void FeedStream::SetRequestSchedule(const StreamType& stream_type, @@ -994,14 +966,22 @@ const std::string& slice_id) { Stream& stream = GetStream(stream_type); int index = stream.surface_updater->GetSliceIndexFromSliceId(slice_id); - if (index >= 0) { - if (stream_type.IsForYou()) { - UpdateShownSlicesUploadCondition(index); - notice_card_tracker_.OnSliceViewed(index); + if (index < 0) + return; + + if (stream.model) { + if (SetMetadata(SetStreamViewTime(metadata_, stream_type, + stream.model->GetLastAddedTime()))) { + MaybeNotifyHasUnreadContent(stream_type); } metrics_reporter_->ContentSliceViewed(stream_type, index); } + if (stream_type.IsForYou()) { + UpdateShownSlicesUploadCondition(index); + notice_card_tracker_.OnSliceViewed(index); + } } + // TODO(crbug/1147237): Rename this method and related members? bool FeedStream::CanUploadActions() const { // TODO(crbug/1152592): Determine notice card behavior with web feeds. @@ -1064,6 +1044,27 @@ can_upload_actions_with_notice_card_ = HasReachedConditionsToUploadActionsWithNoticeCard(); } + +// Notifies observers if 'HasUnreadContent' has changed for `stream_type`. +// Stream content has been seen if StreamData::last_added_time_millis == +// Metadata::StreamMetadata::view_time_millis. This should be called: when the +// model is loaded, when a refresh is attempted, and when content is viewed. +void FeedStream::MaybeNotifyHasUnreadContent(const StreamType& stream_type) { + Stream& stream = GetStream(stream_type); + // Don't notify if we don't know the update time. + if (stream.last_updated_time.is_null()) + return; + + const bool has_new_content = + feedstore::GetStreamViewTime(metadata_, stream_type) != + stream.last_updated_time && + !stream.last_updated_time.is_null(); + + for (auto& o : stream.unread_content_notifiers) { + o.NotifyIfValueChanged(has_new_content); + } +} + void FeedStream::ReportFeedViewed(SurfaceId surface_id) { metrics_reporter_->FeedViewed(surface_id); }
diff --git a/components/feed/core/v2/feed_stream.h b/components/feed/core/v2/feed_stream.h index ad2eadda..6198ffe 100644 --- a/components/feed/core/v2/feed_stream.h +++ b/components/feed/core/v2/feed_stream.h
@@ -28,6 +28,7 @@ #include "components/feed/core/v2/tasks/load_stream_task.h" #include "components/feed/core/v2/tasks/wait_for_store_initialize_task.h" #include "components/feed/core/v2/web_feed_index.h" +#include "components/feed/core/v2/wire_response_translator.h" #include "components/offline_pages/core/prefetch/suggestions_provider.h" #include "components/offline_pages/task/task_queue.h" @@ -39,6 +40,9 @@ } // namespace offline_pages namespace feed { +namespace feed_stream { +class UnreadContentNotifier; +} class FeedNetwork; class FeedStore; class ImageFetcher; @@ -48,7 +52,6 @@ class PersistentKeyValueStoreImpl; class StreamModel; class SurfaceUpdater; -struct StreamModelUpdateRequest; // Implements FeedApi. |FeedStream| additionally exposes functionality // needed by other classes within the Feed component. @@ -71,45 +74,6 @@ virtual void RegisterExperiments(const Experiments& experiments) = 0; }; - // Forwards to |feed::TranslateWireResponse()| by default. Can be overridden - // for testing. - class WireResponseTranslator { - public: - WireResponseTranslator() = default; - ~WireResponseTranslator() = default; - virtual RefreshResponseData TranslateWireResponse( - feedwire::Response response, - StreamModelUpdateRequest::Source source, - bool was_signed_in_request, - base::Time current_time) const; - }; - - class Metadata { - public: - explicit Metadata(FeedStore* store); - ~Metadata(); - - void Populate(feedstore::Metadata metadata); - - const std::string& GetConsistencyToken() const; - void SetConsistencyToken(std::string consistency_token); - - const std::string& GetSessionIdToken() const; - base::Time GetSessionIdExpiryTime() const; - void SetSessionId(std::string token, base::Time expiry_time); - void MaybeUpdateSessionId(base::Optional<std::string> token); - - LocalActionId GetNextActionId(); - - const feedstore::Metadata& GetMetadataProtoForTesting() const { - return metadata_; - } - - private: - FeedStore* store_; - feedstore::Metadata metadata_; - }; - FeedStream(RefreshTaskScheduler* refresh_task_scheduler, MetricsReporter* metrics_reporter, Delegate* delegate, @@ -132,6 +96,10 @@ std::string GetSessionId() const override; void AttachSurface(FeedStreamSurface*) override; void DetachSurface(FeedStreamSurface*) override; + void AddUnreadContentObserver(const StreamType& stream_type, + UnreadContentObserver* observer) override; + void RemoveUnreadContentObserver(const StreamType& stream_type, + UnreadContentObserver* observer) override; bool IsArticlesListVisible() override; std::string GetClientInstanceId() const override; void ExecuteRefreshTask(RefreshTaskId task_id) override; @@ -223,8 +191,10 @@ FeedNetwork* GetNetwork() { return feed_network_; } FeedStore* GetStore() { return store_; } RequestThrottler* GetRequestThrottler() { return &request_throttler_; } - Metadata* GetMetadata() { return &metadata_; } - const Metadata* GetMetadata() const { return &metadata_; } + const feedstore::Metadata& GetMetadata() const { return metadata_; } + void SetMetadata(feedstore::Metadata metadata); + bool SetMetadata(base::Optional<feedstore::Metadata> metadata); + MetricsReporter* GetMetricsReporter() const { return metrics_reporter_; } void PrefetchImage(const GURL& url); @@ -295,8 +265,13 @@ bool CanUploadActions() const; void SetLastStreamLoadHadNoticeCard(bool value); + base::WeakPtr<FeedStream> GetWeakPtr() { + return weak_ptr_factory_.GetWeakPtr(); + } + private: class OfflineSuggestionsProvider; + using UnreadContentNotifier = feed_stream::UnreadContentNotifier; struct Stream { Stream(); @@ -313,12 +288,13 @@ // |UnloadModel()|. std::unique_ptr<StreamModel> model; int unload_on_detach_sequence_number = 0; + // When new content was last added to this stream. Populated when we attempt + // to load the model or background refresh. + base::Time last_updated_time; + std::vector<UnreadContentNotifier> unread_content_notifiers; + std::vector<base::OnceCallback<void(bool)>> load_more_complete_callbacks; }; - base::WeakPtr<FeedStream> GetWeakPtr() { - return weak_ptr_factory_.GetWeakPtr(); - } - void InitializeComplete(WaitForStoreInitializeTask::Result result); // Re-evaluate whether or not activity logging should currently be enabled. @@ -356,6 +332,7 @@ bool CanLogViews() const; void UpdateCanUploadActionsWithNoticeCard(); + void MaybeNotifyHasUnreadContent(const StreamType& stream_type); Stream& GetStream(const StreamType& type); Stream* FindStream(const StreamType& type); @@ -387,10 +364,9 @@ // Mutable state. RequestThrottler request_throttler_; base::TimeTicks signed_out_for_you_refreshes_until_; - std::vector<base::OnceCallback<void(bool)>> load_more_complete_callbacks_; // State loaded at startup: - Metadata metadata_; + feedstore::Metadata metadata_; WebFeedIndex web_feed_index_; bool is_activity_logging_enabled_ = false; @@ -398,6 +374,8 @@ // feed. bool can_upload_actions_with_notice_card_ = false; + base::ObserverList<UnreadContentObserver> unread_content_observers_; + // To allow tests to wait on task queue idle. base::RepeatingClosure idle_callback_;
diff --git a/components/feed/core/v2/feed_stream_unittest.cc b/components/feed/core/v2/feed_stream_unittest.cc index cafc2189..f4874df 100644 --- a/components/feed/core/v2/feed_stream_unittest.cc +++ b/components/feed/core/v2/feed_stream_unittest.cc
@@ -38,6 +38,7 @@ #include "components/feed/core/shared_prefs/pref_names.h" #include "components/feed/core/v2/config.h" #include "components/feed/core/v2/feed_network.h" +#include "components/feed/core/v2/feedstore_util.h" #include "components/feed/core/v2/image_fetcher.h" #include "components/feed/core/v2/metrics_reporter.h" #include "components/feed/core/v2/persistent_key_value_store_impl.h" @@ -52,6 +53,8 @@ #include "components/feed/core/v2/test/callback_receiver.h" #include "components/feed/core/v2/test/proto_printer.h" #include "components/feed/core/v2/test/stream_builder.h" +#include "components/feed/core/v2/test/test_util.h" +#include "components/feed/core/v2/wire_response_translator.h" #include "components/feed/feed_feature_list.h" #include "components/leveldb_proto/public/proto_database_provider.h" #include "components/offline_pages/core/client_namespace_constants.h" @@ -154,24 +157,14 @@ return msg; } -// This is EXPECT_EQ, but also dumps the string values for ease of reading. -#define EXPECT_STRINGS_EQUAL(WANT, GOT) \ - { \ - std::string want = (WANT), got = (GOT); \ - EXPECT_EQ(want, got) << "Wanted:\n" << (want) << "\nBut got:\n" << (got); \ +class TestUnreadContentObserver : public FeedApi::UnreadContentObserver { + public: + void HasUnreadContentChanged(bool has_unread_content) override { + calls.push_back(has_unread_content); } -// Although time is mocked through TaskEnvironment, it does drift by small -// amounts. -const base::TimeDelta kEpsilon = base::TimeDelta::FromMilliseconds(5); -#define EXPECT_TIME_EQ(WANT, GOT) \ - { \ - base::Time want = (WANT), got = (GOT); \ - if (got != want) { \ - EXPECT_LT(want - kEpsilon, got); \ - EXPECT_GT(want + kEpsilon, got); \ - } \ - } + std::vector<bool> calls; +}; class TestSurfaceBase : public FeedStreamSurface { public: @@ -191,7 +184,7 @@ void Attach(FeedStream* stream) { EXPECT_FALSE(stream_); - stream_ = stream; + stream_ = stream->GetWeakPtr(); stream_->AttachSurface(this); } @@ -290,7 +283,7 @@ } // The stream if it was attached using the constructor. - FeedStream* stream_ = nullptr; + base::WeakPtr<FeedStream> stream_; std::vector<std::string> described_updates_; std::map<std::string, std::string> data_store_entries_; }; @@ -469,9 +462,9 @@ base::Optional<feedwire::Response> injected_response_; }; -// Forwards to |FeedStream::WireResponseTranslator| unless a response is +// Forwards to |WireResponseTranslator| unless a response is // injected. -class TestWireResponseTranslator : public FeedStream::WireResponseTranslator { +class TestWireResponseTranslator : public WireResponseTranslator { public: RefreshResponseData TranslateWireResponse( feedwire::Response response, @@ -485,7 +478,7 @@ injected_responses_.erase(injected_responses_.begin()); return result; } - return FeedStream::WireResponseTranslator::TranslateWireResponse( + return WireResponseTranslator::TranslateWireResponse( std::move(response), source, was_signed_in_request, current_time); } void InjectResponse(std::unique_ptr<StreamModelUpdateRequest> response, @@ -1096,7 +1089,11 @@ } TEST_P(FeedStreamTestForAllStreamTypes, LoadFromNetwork) { - stream_->GetMetadata()->SetConsistencyToken("token"); + { + auto metadata = stream_->GetMetadata(); + metadata.set_consistency_token("token"); + stream_->SetMetadata(metadata); + } // Store is empty, so we should fallback to a network request. response_translator_.InjectResponse(MakeTypicalInitialModelState()); @@ -1231,7 +1228,6 @@ GetFeedConfig().stale_content_threshold - base::TimeDelta::FromMinutes(1)), base::DoNothing()); - stream_->GetMetadata()->SetConsistencyToken("token-1"); // Store is stale, so we should fallback to a network request. response_translator_.InjectResponse(MakeTypicalInitialModelState()); @@ -1239,10 +1235,6 @@ WaitForIdleTaskQueue(); ASSERT_TRUE(network_.query_request_sent); - // The stored continutation token should be sent. - EXPECT_EQ( - "token-1", - network_.query_request_sent->feed_request().consistency_token().token()); EXPECT_TRUE(response_translator_.InjectedResponseConsumed()); ASSERT_TRUE(surface.initial_state); } @@ -1258,7 +1250,6 @@ MakeTypicalInitialModelState( /*first_cluster_id=*/0, kTestTimeEpoch - kContentAge), base::DoNothing()); - stream_->GetMetadata()->SetConsistencyToken("token-1"); // Store is stale, so we should fallback to a network request. response_translator_.InjectResponse(MakeTypicalInitialModelState()); @@ -1266,10 +1257,6 @@ WaitForIdleTaskQueue(); ASSERT_TRUE(network_.query_request_sent); - // The stored continutation token should be sent. - EXPECT_EQ( - "token-1", - network_.query_request_sent->feed_request().consistency_token().token()); EXPECT_TRUE(response_translator_.InjectedResponseConsumed()); ASSERT_TRUE(surface.initial_state); EXPECT_EQ(LoadStreamStatus::kDataInStoreIsExpired, @@ -1289,7 +1276,6 @@ MakeTypicalInitialModelState( /*first_cluster_id=*/0, kTestTimeEpoch - kContentAge), base::DoNothing()); - stream_->GetMetadata()->SetConsistencyToken("token-1"); // Store is stale, so we should fallback to a network request. Since we didn't // inject a network response, the network update will fail. @@ -1421,7 +1407,7 @@ // Validate the downstream consumption of the response. EXPECT_EQ("loading -> 2 slices", surface.DescribeUpdates()); - EXPECT_EQ(kSessionId, stream_->GetMetadata()->GetSessionIdToken()); + EXPECT_EQ(kSessionId, stream_->GetMetadata().session_id().token()); EXPECT_FALSE(stream_->GetModel(surface.GetStreamType())->signed_in()); // Advance the clock beyond the forced signed out period. @@ -1438,7 +1424,7 @@ // contained the session id. ASSERT_EQ(2, network_.send_query_call_count); EXPECT_TRUE(network_.forced_signed_out_request); - EXPECT_EQ(kSessionId, stream_->GetMetadata()->GetSessionIdToken()); + EXPECT_EQ(kSessionId, stream_->GetMetadata().session_id().token()); EXPECT_EQ(network_.query_request_sent->feed_request() .client_info() .chrome_client_info() @@ -1462,7 +1448,7 @@ // The model should now be in the signed-in state. EXPECT_TRUE(stream_->GetModel(kForYouStream)->signed_in()); - EXPECT_TRUE(stream_->GetMetadata()->GetSessionIdToken().empty()); + EXPECT_TRUE(stream_->GetMetadata().session_id().token().empty()); } TEST_F(FeedStreamTest, WebFeedUsesSignedInRequestAfterHistoryIsDeleted) { @@ -1486,7 +1472,7 @@ EXPECT_EQ("loading -> 2 slices", surface.DescribeUpdates()); EXPECT_FALSE(network_.forced_signed_out_request); - EXPECT_TRUE(stream_->GetMetadata()->GetSessionIdToken().empty()); + EXPECT_TRUE(stream_->GetMetadata().session_id().token().empty()); } TEST_F(FeedStreamTest, ShouldMakeFeedQueryRequestConsumesQuota) { @@ -1629,6 +1615,79 @@ "ContentSuggestions.Feed.CardAction.OpenInNewTab")); } +TEST_F(FeedStreamTest, HasUnreadContentAfterLoadFromNetwork) { + response_translator_.InjectResponse(MakeTypicalInitialModelState()); + TestUnreadContentObserver observer; + stream_->AddUnreadContentObserver(kForYouStream, &observer); + TestForYouSurface surface(stream_.get()); + + WaitForIdleTaskQueue(); + + EXPECT_EQ(std::vector<bool>({true}), observer.calls); +} + +TEST_F(FeedStreamTest, RemovedUnreadContentObserverDoesNotReceiveCalls) { + response_translator_.InjectResponse(MakeTypicalInitialModelState()); + TestUnreadContentObserver observer; + stream_->AddUnreadContentObserver(kForYouStream, &observer); + stream_->RemoveUnreadContentObserver(kForYouStream, &observer); + TestForYouSurface surface(stream_.get()); + + WaitForIdleTaskQueue(); + + EXPECT_EQ(std::vector<bool>(), observer.calls); +} + +TEST_F(FeedStreamTest, DeletedUnreadContentObserverDoesNotCrash) { + response_translator_.InjectResponse(MakeTypicalInitialModelState()); + { + TestUnreadContentObserver observer; + stream_->AddUnreadContentObserver(kForYouStream, &observer); + } + TestForYouSurface surface(stream_.get()); + + WaitForIdleTaskQueue(); +} + +TEST_F(FeedStreamTest, HasUnreadContentAfterLoadFromStore) { + store_->OverwriteStream(kForYouStream, MakeTypicalInitialModelState(), + base::DoNothing()); + + TestUnreadContentObserver observer; + stream_->AddUnreadContentObserver(kForYouStream, &observer); + TestForYouSurface surface(stream_.get()); + + WaitForIdleTaskQueue(); + + EXPECT_EQ(std::vector<bool>({true}), observer.calls); +} + +TEST_F(FeedStreamTest, ReportSliceViewedUpdatesObservers) { + response_translator_.InjectResponse(MakeTypicalInitialModelState()); + TestUnreadContentObserver observer; + stream_->AddUnreadContentObserver(kForYouStream, &observer); + TestForYouSurface surface(stream_.get()); + + WaitForIdleTaskQueue(); + + stream_->ReportSliceViewed( + surface.GetSurfaceId(), surface.GetStreamType(), + surface.initial_state->updated_slices(1).slice().slice_id()); + task_environment_.RunUntilIdle(); + + EXPECT_EQ(std::vector<bool>({true, false}), observer.calls); + + // Verify that the fact the stream was viewed persists. + CreateStream(); + + TestUnreadContentObserver observer2; + stream_->AddUnreadContentObserver(kForYouStream, &observer2); + TestForYouSurface surface2(stream_.get()); + WaitForIdleTaskQueue(); + + EXPECT_EQ(std::vector<bool>({false}), observer2.calls); +} + TEST_P(FeedStreamTestForAllStreamTypes, LoadMoreAppendsContent) { response_translator_.InjectResponse(MakeTypicalInitialModelState()); TestSurface surface(stream_.get()); @@ -1715,12 +1774,17 @@ } TEST_F(FeedStreamTest, LoadMoreSendsTokens) { + { + auto metadata = stream_->GetMetadata(); + metadata.set_consistency_token("token"); + stream_->SetMetadata(metadata); + } + response_translator_.InjectResponse(MakeTypicalInitialModelState()); TestForYouSurface surface(stream_.get()); WaitForIdleTaskQueue(); ASSERT_EQ("loading -> 2 slices", surface.DescribeUpdates()); - stream_->GetMetadata()->SetConsistencyToken("token-1"); response_translator_.InjectResponse(MakeTypicalNextPageState(2)); CallbackReceiver<bool> callback; stream_->LoadMore(surface, callback.Bind()); @@ -1729,7 +1793,7 @@ ASSERT_EQ("2 slices +spinner -> 4 slices", surface.DescribeUpdates()); EXPECT_EQ( - "token-1", + "token", network_.query_request_sent->feed_request().consistency_token().token()); EXPECT_EQ("page-2", network_.query_request_sent->feed_request() .feed_query() @@ -1737,7 +1801,6 @@ .next_page_token() .next_page_token()); - stream_->GetMetadata()->SetConsistencyToken("token-2"); response_translator_.InjectResponse(MakeTypicalNextPageState(3)); stream_->LoadMore(surface, callback.Bind()); @@ -1745,7 +1808,7 @@ ASSERT_EQ("4 slices +spinner -> 6 slices", surface.DescribeUpdates()); EXPECT_EQ( - "token-2", + "token", network_.query_request_sent->feed_request().consistency_token().token()); EXPECT_EQ("page-3", network_.query_request_sent->feed_request() .feed_query() @@ -1930,8 +1993,8 @@ ASSERT_EQ("loading -> 2 slices -> loading -> cant-refresh", surface.DescribeUpdates()); - EXPECT_EQ("", DumpStoreState()); - EXPECT_EQ("", stream_->GetMetadata()->GetConsistencyToken()); + EXPECT_EQ("{\n}\n\n", DumpStoreState()); + EXPECT_EQ("", stream_->GetMetadata().consistency_token()); EXPECT_FALSE(stream_->IsActivityLoggingEnabled()); } @@ -2299,7 +2362,7 @@ WaitForIdleTaskQueue(); EXPECT_EQ(1, network_.GetActionRequestSent()->feed_actions_size()); - EXPECT_EQ("token-12", stream_->GetMetadata()->GetConsistencyToken()); + EXPECT_EQ("token-12", stream_->GetMetadata().consistency_token()); // Uploaded action should have been erased from the store. network_.ClearTestData(); @@ -2499,26 +2562,25 @@ } TEST_F(FeedStreamTest, MetadataLoadedWhenDatabaseInitialized) { - ASSERT_TRUE(stream_->GetMetadata()); - // Verify the schema has been updated to the current version. - EXPECT_EQ((int)FeedStore::kCurrentStreamSchemaVersion, - stream_->GetMetadata() - ->GetMetadataProtoForTesting() - .stream_schema_version()); - const auto kExpiry = kTestTimeEpoch + base::TimeDelta::FromDays(1234); - stream_->GetMetadata()->SetSessionId("session-id", kExpiry); - stream_->GetMetadata()->SetConsistencyToken("token"); - EXPECT_EQ(1, stream_->GetMetadata()->GetNextActionId().GetUnsafeValue()); + { + // Write some metadata so it can be loaded when FeedStream starts up. + feedstore::Metadata initial_metadata; + feedstore::SetSessionId(initial_metadata, "session-id", kExpiry); + initial_metadata.set_consistency_token("token"); + store_->WriteMetadata(initial_metadata, base::DoNothing()); + } // Creating a stream should load metadata. CreateStream(); - ASSERT_TRUE(stream_->GetMetadata()); - EXPECT_EQ("session-id", stream_->GetMetadata()->GetSessionIdToken()); - EXPECT_TIME_EQ(kExpiry, stream_->GetMetadata()->GetSessionIdExpiryTime()); - EXPECT_EQ("token", stream_->GetMetadata()->GetConsistencyToken()); - EXPECT_EQ(2, stream_->GetMetadata()->GetNextActionId().GetUnsafeValue()); + EXPECT_EQ("session-id", stream_->GetMetadata().session_id().token()); + EXPECT_TIME_EQ(kExpiry, + feedstore::GetSessionIdExpiryTime(stream_->GetMetadata())); + EXPECT_EQ("token", stream_->GetMetadata().consistency_token()); + // Verify the schema has been updated to the current version. + EXPECT_EQ((int)FeedStore::kCurrentStreamSchemaVersion, + stream_->GetMetadata().stream_schema_version()); } TEST_F(FeedStreamTest, ModelUnloadsAfterTimeout) { @@ -2775,8 +2837,6 @@ } TEST_F(FeedStreamTest, SendsClientInstanceId) { - stream_->GetMetadata()->SetConsistencyToken("token"); - // Store is empty, so we should fallback to a network request. response_translator_.InjectResponse(MakeTypicalInitialModelState()); TestForYouSurface surface(stream_.get()); @@ -2864,50 +2924,6 @@ .notice_card_acknowledged()); } -TEST_F(FeedStreamTest, GetSetAndUpdateSessionId) { - const std::string kToken1 = "token1"; - const std::string kToken2 = "token2"; - const base::Time kExpiryTime1 = - kTestTimeEpoch + base::TimeDelta::FromHours(2); - const base::Time kExpiryTime2 = - kTestTimeEpoch + GetFeedConfig().session_id_max_age; - ASSERT_NE(kExpiryTime1, kExpiryTime2); - - // The stream metadata is initialized with an empty token and expiry time. - EXPECT_TRUE(stream_->GetMetadata()->GetSessionIdToken().empty()); - EXPECT_TRUE(stream_->GetMetadata()->GetSessionIdExpiryTime().is_null()); - - // Verify that directly calling SetSessionId works as expected. - stream_->GetMetadata()->SetSessionId(kToken1, kExpiryTime1); - EXPECT_EQ(kToken1, stream_->GetMetadata()->GetSessionIdToken()); - EXPECT_TIME_EQ(kExpiryTime1, - stream_->GetMetadata()->GetSessionIdExpiryTime()); - - // Updating the token with nullopt is a NOP. - stream_->GetMetadata()->MaybeUpdateSessionId(base::nullopt); - EXPECT_EQ(kToken1, stream_->GetMetadata()->GetSessionIdToken()); - EXPECT_TIME_EQ(kExpiryTime1, - stream_->GetMetadata()->GetSessionIdExpiryTime()); - - // Updating the token with the same value is a NOP. - stream_->GetMetadata()->MaybeUpdateSessionId(kToken1); - EXPECT_EQ(kToken1, stream_->GetMetadata()->GetSessionIdToken()); - EXPECT_TIME_EQ(kExpiryTime1, - stream_->GetMetadata()->GetSessionIdExpiryTime()); - - // Updating the token with a different value resets the token and assigns a - // new expiry time. - stream_->GetMetadata()->MaybeUpdateSessionId(kToken2); - EXPECT_EQ(kToken2, stream_->GetMetadata()->GetSessionIdToken()); - EXPECT_TIME_EQ(kExpiryTime2, - stream_->GetMetadata()->GetSessionIdExpiryTime()); - - // Updating the token with the empty string clears its value. - stream_->GetMetadata()->MaybeUpdateSessionId(""); - EXPECT_TRUE(stream_->GetMetadata()->GetSessionIdToken().empty()); - EXPECT_TRUE(stream_->GetMetadata()->GetSessionIdExpiryTime().is_null()); -} - TEST_F(FeedStreamTest, SignedOutSessionIdConsistency) { const std::string kSessionToken1("session-token-1"); const std::string kSessionToken2("session-token-2"); @@ -2934,9 +2950,9 @@ EXPECT_FALSE(network_.query_request_sent->feed_request() .client_info() .has_chrome_client_info()); - EXPECT_EQ(kSessionToken1, stream_->GetMetadata()->GetSessionIdToken()); + EXPECT_EQ(kSessionToken1, stream_->GetMetadata().session_id().token()); const base::Time kSessionToken1ExpiryTime = - stream_->GetMetadata()->GetSessionIdExpiryTime(); + feedstore::GetSessionIdExpiryTime(stream_->GetMetadata()); // (2) LoadMore: the server returns the same session-id token // - this should trigger a network request @@ -2958,9 +2974,9 @@ .client_info() .chrome_client_info() .session_id()); - EXPECT_EQ(kSessionToken1, stream_->GetMetadata()->GetSessionIdToken()); + EXPECT_EQ(kSessionToken1, stream_->GetMetadata().session_id().token()); EXPECT_TIME_EQ(kSessionToken1ExpiryTime, - stream_->GetMetadata()->GetSessionIdExpiryTime()); + feedstore::GetSessionIdExpiryTime(stream_->GetMetadata())); // (3) LoadMore: the server omits returning a session-id token // - this should trigger a network request @@ -2981,9 +2997,9 @@ .client_info() .chrome_client_info() .session_id()); - EXPECT_EQ(kSessionToken1, stream_->GetMetadata()->GetSessionIdToken()); + EXPECT_EQ(kSessionToken1, stream_->GetMetadata().session_id().token()); EXPECT_TIME_EQ(kSessionToken1ExpiryTime, - stream_->GetMetadata()->GetSessionIdExpiryTime()); + feedstore::GetSessionIdExpiryTime(stream_->GetMetadata())); // (4) LoadMore: the server returns new session id. // - this should trigger a network request @@ -3005,26 +3021,27 @@ .client_info() .chrome_client_info() .session_id()); - EXPECT_EQ(kSessionToken2, stream_->GetMetadata()->GetSessionIdToken()); + EXPECT_EQ(kSessionToken2, stream_->GetMetadata().session_id().token()); EXPECT_TIME_EQ(kSessionToken1ExpiryTime + base::TimeDelta::FromSeconds(3), - stream_->GetMetadata()->GetSessionIdExpiryTime()); + feedstore::GetSessionIdExpiryTime(stream_->GetMetadata())); } TEST_F(FeedStreamTest, ClearAllResetsSessionId) { is_signed_in_ = false; // Initialize a session id. - stream_->GetMetadata()->MaybeUpdateSessionId("session-id"); - ASSERT_FALSE(stream_->GetMetadata()->GetSessionIdToken().empty()); - ASSERT_FALSE(stream_->GetMetadata()->GetSessionIdExpiryTime().is_null()); + feedstore::Metadata metadata = stream_->GetMetadata(); + metadata = *feedstore::MaybeUpdateSessionId(metadata, "session-id"); + stream_->SetMetadata(metadata); // Trigger a ClearAll. stream_->OnCacheDataCleared(); WaitForIdleTaskQueue(); // Session-ID should be wiped. - EXPECT_TRUE(stream_->GetMetadata()->GetSessionIdToken().empty()); - EXPECT_TRUE(stream_->GetMetadata()->GetSessionIdExpiryTime().is_null()); + EXPECT_TRUE(stream_->GetMetadata().session_id().token().empty()); + EXPECT_TRUE( + feedstore::GetSessionIdExpiryTime(stream_->GetMetadata()).is_null()); } TEST_F(FeedStreamTest, SignedOutSessionIdExpiry) { @@ -3048,7 +3065,7 @@ EXPECT_FALSE(network_.query_request_sent->feed_request() .client_info() .has_chrome_client_info()); - EXPECT_EQ(kSessionToken1, stream_->GetMetadata()->GetSessionIdToken()); + EXPECT_EQ(kSessionToken1, stream_->GetMetadata().session_id().token()); // (2) Reload the stream from the network: // - Detach the surface, advance the clock beyond the stale content @@ -3067,7 +3084,7 @@ .client_info() .chrome_client_info() .session_id()); - EXPECT_EQ(kSessionToken1, stream_->GetMetadata()->GetSessionIdToken()); + EXPECT_EQ(kSessionToken1, stream_->GetMetadata().session_id().token()); // (3) Reload the stream from the network: // - Detach the surface, advance the clock beyond the session id max age @@ -3078,7 +3095,7 @@ surface.Detach(); task_environment_.FastForwardBy(GetFeedConfig().session_id_max_age - GetFeedConfig().stale_content_threshold); - ASSERT_LT(stream_->GetMetadata()->GetSessionIdExpiryTime(), + ASSERT_LT(feedstore::GetSessionIdExpiryTime(stream_->GetMetadata()), base::Time::Now()); response_translator_.InjectResponse(model_generator.MakeFirstPage(), kSessionToken2); @@ -3088,7 +3105,7 @@ EXPECT_FALSE(network_.query_request_sent->feed_request() .client_info() .has_chrome_client_info()); - EXPECT_EQ(kSessionToken2, stream_->GetMetadata()->GetSessionIdToken()); + EXPECT_EQ(kSessionToken2, stream_->GetMetadata().session_id().token()); } TEST_F(FeedStreamTest, SessionIdPersistsAcrossStreamLoads) { @@ -3116,8 +3133,9 @@ CreateStream(); WaitForIdleTaskQueue(); ASSERT_EQ(1, network_.send_query_call_count); - EXPECT_EQ(kSessionToken, stream_->GetMetadata()->GetSessionIdToken()); - EXPECT_TIME_EQ(kExpiryTime, stream_->GetMetadata()->GetSessionIdExpiryTime()); + EXPECT_EQ(kSessionToken, stream_->GetMetadata().session_id().token()); + EXPECT_TIME_EQ(kExpiryTime, + feedstore::GetSessionIdExpiryTime(stream_->GetMetadata())); } TEST_F(FeedStreamTest, PersistentKeyValueStoreIsClearedOnClearAll) {
diff --git a/components/feed/core/v2/feedstore_util.cc b/components/feed/core/v2/feedstore_util.cc new file mode 100644 index 0000000..7ae7aea --- /dev/null +++ b/components/feed/core/v2/feedstore_util.cc
@@ -0,0 +1,133 @@ +// Copyright 2021 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 "components/feed/core/v2/feedstore_util.h" + +#include "components/feed/core/v2/config.h" +#include "components/feed/core/v2/feed_store.h" + +namespace feedstore { +using feed::LocalActionId; +using feed::StreamType; + +namespace { +int64_t ToTimestampMillis(base::Time t) { + return (t - base::Time::UnixEpoch()).InMilliseconds(); +} +base::Time FromTimestampMillis(int64_t millis) { + return base::Time::UnixEpoch() + base::TimeDelta::FromMilliseconds(millis); +} +} // namespace + +base::StringPiece StreamId(const StreamType& stream_type) { + if (stream_type.IsForYou()) + return kForYouStreamId; + DCHECK(stream_type.IsWebFeed()); + return kFollowStreamId; +} + +void SetLastAddedTime(base::Time t, feedstore::StreamData& data) { + data.set_last_added_time_millis(ToTimestampMillis(t)); +} + +base::Time GetLastAddedTime(const feedstore::StreamData& data) { + return FromTimestampMillis(data.last_added_time_millis()); +} + +base::Time GetSessionIdExpiryTime(const Metadata& metadata) { + return base::Time::FromDeltaSinceWindowsEpoch( + base::TimeDelta::FromMilliseconds( + metadata.session_id().expiry_time_ms())); +} + +void SetSessionId(Metadata& metadata, + std::string token, + base::Time expiry_time) { + Metadata::SessionID* session_id = metadata.mutable_session_id(); + session_id->set_token(std::move(token)); + session_id->set_expiry_time_ms( + expiry_time.ToDeltaSinceWindowsEpoch().InMilliseconds()); +} + +base::Optional<Metadata> MaybeUpdateSessionId( + const Metadata& metadata, + base::Optional<std::string> token) { + if (token && metadata.session_id().token() != *token) { + base::Time expiry_time = + token->empty() + ? base::Time() + : base::Time::Now() + feed::GetFeedConfig().session_id_max_age; + auto new_metadata = metadata; + SetSessionId(new_metadata, *token, expiry_time); + return new_metadata; + } + return base::nullopt; +} + +LocalActionId GetNextActionId(Metadata& metadata) { + uint32_t id = metadata.next_action_id(); + // Never use 0, as that's an invalid LocalActionId. + if (id == 0) + ++id; + metadata.set_next_action_id(id + 1); + return LocalActionId(id); +} + +const Metadata::StreamMetadata* FindMetadataForStream( + const Metadata& metadata, + const StreamType& stream_type) { + base::StringPiece id = StreamId(stream_type); + for (const auto& sm : metadata.stream_metadata()) { + if (sm.stream_id() == id) + return &sm; + } + return nullptr; +} + +Metadata::StreamMetadata& MetadataForStream(Metadata& metadata, + const StreamType& stream_type) { + const Metadata::StreamMetadata* existing = + FindMetadataForStream(metadata, stream_type); + if (existing) + return *const_cast<Metadata::StreamMetadata*>(existing); + Metadata::StreamMetadata* sm = metadata.add_stream_metadata(); + sm->set_stream_id(StreamId(stream_type).as_string()); + return *sm; +} + +void SetStreamViewTime(Metadata& metadata, + const StreamType& stream_type, + base::Time stream_last_added_time) { + Metadata::StreamMetadata& sm = MetadataForStream(metadata, stream_type); + sm.set_view_time_millis(ToTimestampMillis(stream_last_added_time)); +} + +base::Time GetStreamViewTime(const Metadata& metadata, + const StreamType& stream_type) { + base::Time result; + const Metadata::StreamMetadata* sm = + FindMetadataForStream(metadata, stream_type); + if (sm) + result = FromTimestampMillis(sm->view_time_millis()); + return result; +} + +feedstore::Metadata MakeMetadata() { + feedstore::Metadata md; + md.set_stream_schema_version(feed::FeedStore::kCurrentStreamSchemaVersion); + return md; +} + +base::Optional<Metadata> SetStreamViewTime(const Metadata& metadata, + const StreamType& stream_type, + base::Time stream_last_added_time) { + base::Optional<Metadata> result; + if (GetStreamViewTime(metadata, stream_type) != stream_last_added_time) { + result = metadata; + SetStreamViewTime(*result, stream_type, stream_last_added_time); + } + return result; +} + +} // namespace feedstore
diff --git a/components/feed/core/v2/feedstore_util.h b/components/feed/core/v2/feedstore_util.h new file mode 100644 index 0000000..aa1b00b9 --- /dev/null +++ b/components/feed/core/v2/feedstore_util.h
@@ -0,0 +1,51 @@ +// Copyright 2021 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 COMPONENTS_FEED_CORE_V2_FEEDSTORE_UTIL_H_ +#define COMPONENTS_FEED_CORE_V2_FEEDSTORE_UTIL_H_ + +#include <string> +#include "base/optional.h" +#include "base/time/time.h" +#include "components/feed/core/proto/v2/store.pb.h" +#include "components/feed/core/v2/public/feed_api.h" +#include "components/feed/core/v2/types.h" + +namespace feedstore { +class Metadata; + +constexpr base::StringPiece kForYouStreamId{"i"}; +constexpr base::StringPiece kFollowStreamId{"w"}; + +base::StringPiece StreamId(const feed::StreamType& stream_type); + +/////////////////////////////////////////////////// +// Functions that operate on feedstore proto types. + +void SetLastAddedTime(base::Time t, feedstore::StreamData& data); + +base::Time GetLastAddedTime(const feedstore::StreamData& data); +base::Time GetSessionIdExpiryTime(const feedstore::Metadata& metadata); +base::Time GetStreamViewTime(const Metadata& metadata, + const feed::StreamType& stream_type); +feedstore::Metadata MakeMetadata(); + +// Mutations of Metadata. Metadata will need stored again after being changed, +// call `FeedStream::SetMetadata()`. +void SetSessionId(feedstore::Metadata& metadata, + std::string token, + base::Time expiry_time); +base::Optional<Metadata> MaybeUpdateSessionId( + const feedstore::Metadata& metadata, + base::Optional<std::string> token); +feed::LocalActionId GetNextActionId(feedstore::Metadata& metadata); +const feedstore::Metadata::StreamMetadata* FindMetadataForStream( + const feed::StreamType& stream_type); +base::Optional<Metadata> SetStreamViewTime(const Metadata& metadata, + const feed::StreamType& stream_type, + base::Time stream_last_added_time); + +} // namespace feedstore + +#endif // COMPONENTS_FEED_CORE_V2_FEEDSTORE_UTIL_H_
diff --git a/components/feed/core/v2/feedstore_util_unittest.cc b/components/feed/core/v2/feedstore_util_unittest.cc new file mode 100644 index 0000000..2dbe7f3 --- /dev/null +++ b/components/feed/core/v2/feedstore_util_unittest.cc
@@ -0,0 +1,70 @@ +// Copyright 2021 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 "components/feed/core/v2/feedstore_util.h" + +#include <string> +#include "base/test/task_environment.h" +#include "components/feed/core/v2/config.h" +#include "components/feed/core/v2/test/test_util.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace feedstore { +namespace { +base::Time kTestTimeEpoch = base::Time::UnixEpoch(); +const base::Time kExpiryTime1 = kTestTimeEpoch + base::TimeDelta::FromHours(2); + +const std::string Token1() { + return "token1"; +} +const std::string Token2() { + return "token2"; +} + +TEST(feedstore_util_test, SetSessionId) { + Metadata metadata; + + // Verify that directly calling SetSessionId works as expected. + SetSessionId(metadata, Token1(), kExpiryTime1); + + EXPECT_EQ(Token1(), metadata.session_id().token()); + EXPECT_TIME_EQ(kExpiryTime1, GetSessionIdExpiryTime(metadata)); +} + +TEST(feedstore_util_test, MaybeUpdateSessionId) { + base::test::TaskEnvironment task_environment{ + base::test::TaskEnvironment::TimeSource::MOCK_TIME}; + + feedstore::Metadata metadata; + SetSessionId(metadata, Token1(), kExpiryTime1); + + // Updating the token with nullopt is a NOP. + EXPECT_FALSE(MaybeUpdateSessionId(metadata, base::nullopt)); + + // Updating the token with the same value is a NOP. + EXPECT_FALSE(MaybeUpdateSessionId(metadata, Token1())); + + // Updating the token with a different value resets the token and assigns a + // new expiry time. + base::Optional<Metadata> metadata2 = MaybeUpdateSessionId(metadata, Token2()); + ASSERT_TRUE(metadata2); + EXPECT_EQ(Token2(), metadata2->session_id().token()); + EXPECT_TIME_EQ(base::Time::Now() + feed::GetFeedConfig().session_id_max_age, + GetSessionIdExpiryTime(*metadata2)); + + // Updating the token with the empty string clears its value. + base::Optional<Metadata> metadata3 = MaybeUpdateSessionId(*metadata2, ""); + EXPECT_TRUE(metadata3->session_id().token().empty()); + EXPECT_TRUE(GetSessionIdExpiryTime(*metadata3).is_null()); +} + +TEST(feedstore_util_test, GetNextActionId) { + Metadata metadata; + + EXPECT_EQ(feed::LocalActionId(1), GetNextActionId(metadata)); + EXPECT_EQ(feed::LocalActionId(2), GetNextActionId(metadata)); +} + +} // namespace +} // namespace feedstore
diff --git a/components/feed/core/v2/notice_card_tracker.cc b/components/feed/core/v2/notice_card_tracker.cc index 99d8a42..823b3a14 100644 --- a/components/feed/core/v2/notice_card_tracker.cc +++ b/components/feed/core/v2/notice_card_tracker.cc
@@ -26,6 +26,22 @@ NoticeCardTracker::NoticeCardTracker(PrefService* profile_prefs) : profile_prefs_(profile_prefs) { DCHECK(profile_prefs_); + views_count_ = prefs::GetNoticeCardViewsCount(*profile_prefs_); + clicks_count_ = prefs::GetNoticeCardClicksCount(*profile_prefs_); + + views_count_threshold_ = base::GetFieldTrialParamByFeatureAsInt( + feed::kInterestFeedNoticeCardAutoDismiss, + kNoticeCardViewsCountThresholdParamName, 3); + DCHECK(views_count_threshold_ >= 0); + + clicks_count_threshold_ = base::GetFieldTrialParamByFeatureAsInt( + feed::kInterestFeedNoticeCardAutoDismiss, + kNoticeCardClicksCountThresholdParamName, 1); + DCHECK(clicks_count_threshold_ >= 0); + + DCHECK(views_count_threshold_ > 0 || clicks_count_threshold_ > 0) + << "all notice card auto-dismiss thresholds are set to 0 when there " + "should be at least one threshold above 0"; } void NoticeCardTracker::OnSliceViewed(int index) { @@ -40,28 +56,13 @@ if (!base::FeatureList::IsEnabled(feed::kInterestFeedNoticeCardAutoDismiss)) return false; - int views_count_threshold = base::GetFieldTrialParamByFeatureAsInt( - feed::kInterestFeedNoticeCardAutoDismiss, - kNoticeCardViewsCountThresholdParamName, 3); - DCHECK(views_count_threshold >= 0); - int clicks_count_threshold = base::GetFieldTrialParamByFeatureAsInt( - feed::kInterestFeedNoticeCardAutoDismiss, - kNoticeCardClicksCountThresholdParamName, 1); - DCHECK(clicks_count_threshold >= 0); - - DCHECK(views_count_threshold > 0 || clicks_count_threshold > 0) - << "all notice card auto-dismiss thresholds are set to 0 when there " - "should be at least one threshold above 0"; - - if (views_count_threshold > 0 && - prefs::GetNoticeCardViewsCount(*profile_prefs_) >= - views_count_threshold) { + base::AutoLock auto_lock_views(views_count_lock_); + if (views_count_threshold_ > 0 && views_count_ >= views_count_threshold_) { return true; } - if (clicks_count_threshold > 0 && - prefs::GetNoticeCardClicksCount(*profile_prefs_) >= - clicks_count_threshold) { + base::AutoLock auto_lock_clicks(clicks_count_lock_); + if (clicks_count_threshold_ > 0 && clicks_count_ >= clicks_count_threshold_) { return true; } @@ -82,17 +83,23 @@ } return true; } + void NoticeCardTracker::MaybeUpdateNoticeCardViewsCount(int index) { if (!HasNoticeCardActionsCountPrerequisites(index)) return; prefs::IncrementNoticeCardViewsCount(*profile_prefs_); + base::AutoLock auto_lock(views_count_lock_); + views_count_++; } + void NoticeCardTracker::MaybeUpdateNoticeCardClicksCount(int index) { if (!HasNoticeCardActionsCountPrerequisites(index)) return; prefs::IncrementNoticeCardClicksCount(*profile_prefs_); + base::AutoLock auto_lock(clicks_count_lock_); + clicks_count_++; } } // namespace feed
diff --git a/components/feed/core/v2/notice_card_tracker.h b/components/feed/core/v2/notice_card_tracker.h index db9f7181..ab604c1 100644 --- a/components/feed/core/v2/notice_card_tracker.h +++ b/components/feed/core/v2/notice_card_tracker.h
@@ -5,6 +5,9 @@ #ifndef COMPONENTS_FEED_CORE_V2_NOTICE_CARD_TRACKER_H_ #define COMPONENTS_FEED_CORE_V2_NOTICE_CARD_TRACKER_H_ +#include "base/synchronization/lock.h" +#include "base/thread_annotations.h" + class PrefService; namespace feed { @@ -31,7 +34,8 @@ // Get signals based on the actions. // Indicates whether there were enough views or clicks done on the notice - // card to consider it as acknowledged by the user. + // card to consider it as acknowledged by the user. This is safe to call in a + // background thread. bool HasAcknowledgedNoticeCard() const; private: @@ -40,8 +44,24 @@ void MaybeUpdateNoticeCardClicksCount(int index); PrefService* profile_prefs_; + + // The number of views of the notice card. + mutable base::Lock views_count_lock_; + int views_count_ GUARDED_BY(views_count_lock_); + + // The number of clicks/taps of the notice card. + mutable base::Lock clicks_count_lock_; + int clicks_count_ GUARDED_BY(clicks_count_lock_); + + // The number of views of the notice card to consider it acknowledged by the + // user. + int views_count_threshold_; + + // The number of clicks/taps of the notice card to consider it acknowledged by + // the user. + int clicks_count_threshold_; }; } // namespace feed -#endif // COMPONENTS_FEED_CORE_V2_NOTICE_CARD_TRACKER_H_ \ No newline at end of file +#endif // COMPONENTS_FEED_CORE_V2_NOTICE_CARD_TRACKER_H_
diff --git a/components/feed/core/v2/proto_util.cc b/components/feed/core/v2/proto_util.cc index 1b72f0b..e8424cbc 100644 --- a/components/feed/core/v2/proto_util.cc +++ b/components/feed/core/v2/proto_util.cc
@@ -259,15 +259,3 @@ } } // namespace feed - -namespace feedstore { -void SetLastAddedTime(base::Time t, feedstore::StreamData& data) { - data.set_last_added_time_millis( - (t - base::Time::UnixEpoch()).InMilliseconds()); -} - -base::Time GetLastAddedTime(const feedstore::StreamData& data) { - return base::Time::UnixEpoch() + - base::TimeDelta::FromMilliseconds(data.last_added_time_millis()); -} -} // namespace feedstore
diff --git a/components/feed/core/v2/proto_util.h b/components/feed/core/v2/proto_util.h index 95fd50d..8314df7 100644 --- a/components/feed/core/v2/proto_util.h +++ b/components/feed/core/v2/proto_util.h
@@ -18,7 +18,6 @@ } // namespace feedwire namespace feedstore { class Content; -class StreamData; } // namespace feedstore // Helper functions/classes for dealing with feed proto messages. @@ -62,11 +61,4 @@ } // namespace feed -namespace feedstore { - -void SetLastAddedTime(base::Time t, feedstore::StreamData& data); -base::Time GetLastAddedTime(const feedstore::StreamData& data); - -} // namespace feedstore - #endif // COMPONENTS_FEED_CORE_V2_PROTO_UTIL_H_
diff --git a/components/feed/core/v2/protocol_translator.cc b/components/feed/core/v2/protocol_translator.cc index ea51901..b796f62 100644 --- a/components/feed/core/v2/protocol_translator.cc +++ b/components/feed/core/v2/protocol_translator.cc
@@ -19,6 +19,7 @@ #include "components/feed/core/proto/v2/wire/request_schedule.pb.h" #include "components/feed/core/proto/v2/wire/stream_structure.pb.h" #include "components/feed/core/proto/v2/wire/token.pb.h" +#include "components/feed/core/v2/feedstore_util.h" #include "components/feed/core/v2/metrics_reporter.h" #include "components/feed/core/v2/proto_util.h"
diff --git a/components/feed/core/v2/public/feed_api.cc b/components/feed/core/v2/public/feed_api.cc index 7fda6010..3ff23a9 100644 --- a/components/feed/core/v2/public/feed_api.cc +++ b/components/feed/core/v2/public/feed_api.cc
@@ -42,6 +42,9 @@ FeedApi::FeedApi() = default; FeedApi::~FeedApi() = default; +FeedApi::UnreadContentObserver::UnreadContentObserver() = default; +FeedApi::UnreadContentObserver::~UnreadContentObserver() = default; + FeedStreamSurface::FeedStreamSurface(StreamType stream_type) : stream_type_(stream_type) { static SurfaceId::Generator id_generator;
diff --git a/components/feed/core/v2/public/feed_api.h b/components/feed/core/v2/public/feed_api.h index 678c142..170b8ea 100644 --- a/components/feed/core/v2/public/feed_api.h +++ b/components/feed/core/v2/public/feed_api.h
@@ -116,12 +116,38 @@ // TODO(crbug/1152592): Implement subscriptions(). // WebFeedSubscriptions& subscriptions() = 0; + // Observes whether there is unread content for a specific stream type. + // In some cases, this information will not be known until after stream + // data is loaded from the database. This observer will not be notified until + // the information is available. + class UnreadContentObserver { + public: + UnreadContentObserver(); + virtual ~UnreadContentObserver(); + virtual void HasUnreadContentChanged(bool has_unread_content) = 0; + UnreadContentObserver(const UnreadContentObserver&) = delete; + UnreadContentObserver& operator=(const UnreadContentObserver&) = delete; + + base::WeakPtr<UnreadContentObserver> GetWeakPtr() { + return weak_ptr_factory_.GetWeakPtr(); + } + + private: + base::WeakPtrFactory<UnreadContentObserver> weak_ptr_factory_{this}; + }; // Attach/detach a surface. Surfaces should be attached when content is // required for display, and detached when content is no longer shown. virtual void AttachSurface(FeedStreamSurface*) = 0; virtual void DetachSurface(FeedStreamSurface*) = 0; + // Begin/stop observing a stream type. An observer instance should not be + // added twice without first being removed. + virtual void AddUnreadContentObserver(const StreamType& stream_type, + UnreadContentObserver* observer) = 0; + virtual void RemoveUnreadContentObserver(const StreamType& stream_type, + UnreadContentObserver* observer) = 0; + virtual bool IsArticlesListVisible() = 0; // Returns true if activity logging is enabled. The returned value is
diff --git a/components/feed/core/v2/stream/README.md b/components/feed/core/v2/stream/README.md new file mode 100644 index 0000000..1820f65 --- /dev/null +++ b/components/feed/core/v2/stream/README.md
@@ -0,0 +1 @@ +This directory contains implementation details for FeedStream.
diff --git a/components/feed/core/v2/stream/unread_content_notifier.cc b/components/feed/core/v2/stream/unread_content_notifier.cc new file mode 100644 index 0000000..b17f959 --- /dev/null +++ b/components/feed/core/v2/stream/unread_content_notifier.cc
@@ -0,0 +1,35 @@ +// Copyright 2021 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 "components/feed/core/v2/stream/unread_content_notifier.h" + +#include "base/bind.h" +#include "base/threading/sequenced_task_runner_handle.h" + +namespace feed { +namespace feed_stream { + +UnreadContentNotifier::~UnreadContentNotifier() = default; +UnreadContentNotifier::UnreadContentNotifier(UnreadContentNotifier&&) = default; +UnreadContentNotifier& UnreadContentNotifier::operator=( + UnreadContentNotifier&&) = default; + +UnreadContentNotifier::UnreadContentNotifier( + base::WeakPtr<UnreadContentObserver> observer) + : observer_(observer) {} + +void UnreadContentNotifier::NotifyIfValueChanged(bool has_unread_content) { + bool changed = !is_initialized_ || has_unread_content_ != has_unread_content; + has_unread_content_ = has_unread_content; + is_initialized_ = true; + if (changed) { + base::SequencedTaskRunnerHandle::Get()->PostTask( + FROM_HERE, + base::BindOnce(&UnreadContentObserver::HasUnreadContentChanged, + observer_, has_unread_content)); + } +} + +} // namespace feed_stream +} // namespace feed
diff --git a/components/feed/core/v2/stream/unread_content_notifier.h b/components/feed/core/v2/stream/unread_content_notifier.h new file mode 100644 index 0000000..8168d032d --- /dev/null +++ b/components/feed/core/v2/stream/unread_content_notifier.h
@@ -0,0 +1,38 @@ +// Copyright 2021 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 COMPONENTS_FEED_CORE_V2_STREAM_UNREAD_CONTENT_NOTIFIER_H_ +#define COMPONENTS_FEED_CORE_V2_STREAM_UNREAD_CONTENT_NOTIFIER_H_ + +#include "components/feed/core/v2/public/feed_api.h" + +namespace feed { +namespace feed_stream { + +// Wraps and notifies a `UnreadContentObserver`. +class UnreadContentNotifier { + public: + using UnreadContentObserver = FeedApi::UnreadContentObserver; + explicit UnreadContentNotifier(base::WeakPtr<UnreadContentObserver> observer); + ~UnreadContentNotifier(); + UnreadContentNotifier(UnreadContentNotifier&&); + UnreadContentNotifier& operator=(UnreadContentNotifier&&); + UnreadContentNotifier(const UnreadContentNotifier&) = delete; + UnreadContentNotifier& operator=(const UnreadContentNotifier&) = delete; + + // Notify the observer if it `has_unread_content` has changed. + void NotifyIfValueChanged(bool has_unread_content); + + base::WeakPtr<UnreadContentObserver> observer() const { return observer_; } + + private: + base::WeakPtr<UnreadContentObserver> observer_; + bool is_initialized_ = false; + bool has_unread_content_ = false; +}; + +} // namespace feed_stream +} // namespace feed + +#endif // COMPONENTS_FEED_CORE_V2_STREAM_UNREAD_CONTENT_NOTIFIER_H_
diff --git a/components/feed/core/v2/stream_model.cc b/components/feed/core/v2/stream_model.cc index 6448dce6..5d3f1ea 100644 --- a/components/feed/core/v2/stream_model.cc +++ b/components/feed/core/v2/stream_model.cc
@@ -15,6 +15,8 @@ #include "base/strings/string_number_conversions.h" #include "components/feed/core/proto/v2/store.pb.h" #include "components/feed/core/proto/v2/wire/content_id.pb.h" +#include "components/feed/core/v2/feedstore_util.h" +#include "components/feed/core/v2/proto_util.h" #include "components/feed/core/v2/protocol_translator.h" namespace feed { @@ -247,6 +249,10 @@ return stream_data_.next_page_token(); } +base::Time StreamModel::GetLastAddedTime() const { + return feedstore::GetLastAddedTime(stream_data_); +} + std::string StreamModel::DumpStateForTesting() { std::stringstream ss; ss << "StreamModel{\n";
diff --git a/components/feed/core/v2/stream_model.h b/components/feed/core/v2/stream_model.h index 81bd01e05..f72d859 100644 --- a/components/feed/core/v2/stream_model.h +++ b/components/feed/core/v2/stream_model.h
@@ -131,6 +131,9 @@ bool RejectEphemeralChange(EphemeralChangeId id); const std::string& GetNextPageToken() const; + // Time the client received this stream data. 'NextPage' requests do not + // change this time. + base::Time GetLastAddedTime() const; // Outputs a string representing the model state for debugging or testing. std::string DumpStateForTesting();
diff --git a/components/feed/core/v2/tasks/load_more_task.cc b/components/feed/core/v2/tasks/load_more_task.cc index 3873062..b5562536 100644 --- a/components/feed/core/v2/tasks/load_more_task.cc +++ b/components/feed/core/v2/tasks/load_more_task.cc
@@ -10,15 +10,18 @@ #include "base/callback_helpers.h" #include "base/check.h" #include "base/time/time.h" +#include "components/feed/core/proto/v2/store.pb.h" #include "components/feed/core/proto/v2/wire/client_info.pb.h" #include "components/feed/core/proto/v2/wire/feed_request.pb.h" #include "components/feed/core/proto/v2/wire/request.pb.h" #include "components/feed/core/v2/feed_network.h" #include "components/feed/core/v2/feed_stream.h" +#include "components/feed/core/v2/feedstore_util.h" #include "components/feed/core/v2/proto_util.h" #include "components/feed/core/v2/protocol_translator.h" #include "components/feed/core/v2/stream_model.h" #include "components/feed/core/v2/tasks/upload_actions_task.h" +#include "components/feed/core/v2/wire_response_translator.h" namespace feed { @@ -74,7 +77,7 @@ NetworkRequestType::kNextPage, CreateFeedQueryLoadMoreRequest( stream_->GetRequestMetadata(stream_type_, /*is_for_next_page=*/true), - stream_->GetMetadata()->GetConsistencyToken(), + stream_->GetMetadata().consistency_token(), stream_->GetModel(stream_type_)->GetNextPageToken()), force_signed_out_request, base::BindOnce(&LoadMoreTask::QueryRequestComplete, GetWeakPtr())); @@ -100,7 +103,12 @@ loaded_new_content_from_network_ = !translated_response.model_update_request->stream_structures.empty(); - stream_->GetMetadata()->MaybeUpdateSessionId(translated_response.session_id); + base::Optional<feedstore::Metadata> updated_metadata = + feedstore::MaybeUpdateSessionId(stream_->GetMetadata(), + translated_response.session_id); + if (updated_metadata) { + stream_->SetMetadata(std::move(*updated_metadata)); + } model->Update(std::move(translated_response.model_update_request));
diff --git a/components/feed/core/v2/tasks/load_stream_from_store_task.cc b/components/feed/core/v2/tasks/load_stream_from_store_task.cc index 1a63087..24793ff 100644 --- a/components/feed/core/v2/tasks/load_stream_from_store_task.cc +++ b/components/feed/core/v2/tasks/load_stream_from_store_task.cc
@@ -12,6 +12,7 @@ #include "components/feed/core/v2/config.h" #include "components/feed/core/v2/feed_store.h" #include "components/feed/core/v2/feed_stream.h" +#include "components/feed/core/v2/feedstore_util.h" #include "components/feed/core/v2/proto_util.h" #include "components/feed/core/v2/protocol_translator.h" #include "components/feed/core/v2/public/feed_api.h" @@ -65,8 +66,8 @@ return; } if (!ignore_staleness_) { - content_age_ = - base::Time::Now() - feedstore::GetLastAddedTime(result.stream_data); + last_added_time_ = feedstore::GetLastAddedTime(result.stream_data); + content_age_ = base::Time::Now() - last_added_time_; if (content_age_ > GetFeedConfig().content_expiration_threshold) { Complete(LoadStreamStatus::kDataInStoreIsExpired); return; @@ -145,6 +146,7 @@ task_result.status = status; } task_result.content_age = content_age_; + task_result.last_added_time = last_added_time_; std::move(result_callback_).Run(std::move(task_result)); TaskComplete(); }
diff --git a/components/feed/core/v2/tasks/load_stream_from_store_task.h b/components/feed/core/v2/tasks/load_stream_from_store_task.h index 29a69f9..96e35e0 100644 --- a/components/feed/core/v2/tasks/load_stream_from_store_task.h +++ b/components/feed/core/v2/tasks/load_stream_from_store_task.h
@@ -38,6 +38,8 @@ // How long since the loaded content was fetched from the server. // May be zero if content is not loaded. base::TimeDelta content_age; + // Last time the stream was fetched from the network. + base::Time last_added_time; }; enum class LoadType { @@ -80,6 +82,7 @@ std::unique_ptr<StreamModelUpdateRequest> update_request_; std::vector<feedstore::StoredAction> pending_actions_; base::TimeDelta content_age_; + base::Time last_added_time_; base::WeakPtrFactory<LoadStreamFromStoreTask> weak_ptr_factory_{this}; };
diff --git a/components/feed/core/v2/tasks/load_stream_task.cc b/components/feed/core/v2/tasks/load_stream_task.cc index 906c960..ae72afe 100644 --- a/components/feed/core/v2/tasks/load_stream_task.cc +++ b/components/feed/core/v2/tasks/load_stream_task.cc
@@ -16,6 +16,7 @@ #include "components/feed/core/proto/v2/wire/request.pb.h" #include "components/feed/core/v2/feed_network.h" #include "components/feed/core/v2/feed_stream.h" +#include "components/feed/core/v2/feedstore_util.h" #include "components/feed/core/v2/metrics_reporter.h" #include "components/feed/core/v2/proto_util.h" #include "components/feed/core/v2/protocol_translator.h" @@ -92,6 +93,7 @@ load_from_store_status_ = result.status; latencies_->StepComplete(LoadLatencyTimes::kLoadFromStore); stored_content_age_ = result.content_age; + last_added_time_ = result.last_added_time; // Phase 2. // - If loading from store works, update the model. @@ -142,7 +144,7 @@ CreateFeedQueryRefreshRequest( GetRequestReason(load_type_), stream_->GetRequestMetadata(stream_type_, /*is_for_next_page=*/false), - stream_->GetMetadata()->GetConsistencyToken()), + stream_->GetMetadata().consistency_token()), force_signed_out_request, base::BindOnce(&LoadStreamTask::QueryRequestComplete, GetWeakPtr())); } @@ -174,6 +176,8 @@ return Done(LoadStreamStatus::kProtoTranslationFailed); loaded_new_content_from_network_ = true; + last_added_time_ = feedstore::GetLastAddedTime( + response_data.model_update_request->stream_data); stream_->GetStore()->OverwriteStream( stream_type_, @@ -186,7 +190,12 @@ stream_->SetLastStreamLoadHadNoticeCard(isNoticeCardFulfilled); MetricsReporter::NoticeCardFulfilled(isNoticeCardFulfilled); - stream_->GetMetadata()->MaybeUpdateSessionId(response_data.session_id); + base::Optional<feedstore::Metadata> updated_metadata = + feedstore::MaybeUpdateSessionId(stream_->GetMetadata(), + response_data.session_id); + if (updated_metadata) { + stream_->SetMetadata(std::move(*updated_metadata)); + } if (response_data.experiments) experiments_ = *response_data.experiments; @@ -215,6 +224,7 @@ result.stream_type = stream_type_; result.load_from_store_status = load_from_store_status_; result.stored_content_age = stored_content_age_; + result.last_added_time = last_added_time_; result.final_status = status; result.load_type = load_type_; result.network_response_info = network_response_info_;
diff --git a/components/feed/core/v2/tasks/load_stream_task.h b/components/feed/core/v2/tasks/load_stream_task.h index 8bcb5f6..864bdde 100644 --- a/components/feed/core/v2/tasks/load_stream_task.h +++ b/components/feed/core/v2/tasks/load_stream_task.h
@@ -50,6 +50,9 @@ LoadStreamStatus load_from_store_status = LoadStreamStatus::kNoStatus; // Age of content loaded from local storage. Zero if none was loaded. base::TimeDelta stored_content_age; + // Last time the stream was fetched from the network. This may be either + // a previous fetch time, or the one triggered by `LoadStreamTask`. + base::Time last_added_time; LoadType load_type; // Information about the network request, if one was made. @@ -94,6 +97,7 @@ base::Optional<NetworkResponseInfo> network_response_info_; bool loaded_new_content_from_network_ = false; base::TimeDelta stored_content_age_; + base::Time last_added_time_; Experiments experiments_; std::unique_ptr<LoadLatencyTimes> latencies_;
diff --git a/components/feed/core/v2/tasks/upload_actions_task.cc b/components/feed/core/v2/tasks/upload_actions_task.cc index 476227b..af20692 100644 --- a/components/feed/core/v2/tasks/upload_actions_task.cc +++ b/components/feed/core/v2/tasks/upload_actions_task.cc
@@ -15,6 +15,7 @@ #include "components/feed/core/v2/feed_network.h" #include "components/feed/core/v2/feed_store.h" #include "components/feed/core/v2/feed_stream.h" +#include "components/feed/core/v2/feedstore_util.h" #include "components/feed/core/v2/metrics_reporter.h" #include "components/feed/core/v2/request_throttler.h" @@ -137,14 +138,15 @@ UploadActionsTask::~UploadActionsTask() = default; void UploadActionsTask::Run() { - consistency_token_ = stream_->GetMetadata()->GetConsistencyToken(); + consistency_token_ = stream_->GetMetadata().consistency_token(); // From constructor 1: If there is an action to store, store it and maybe try // to upload all pending actions. if (wire_action_) { StoredAction action; - int32_t action_id = - stream_->GetMetadata()->GetNextActionId().GetUnsafeValue(); + feedstore::Metadata metadata = stream_->GetMetadata(); + int32_t action_id = feedstore::GetNextActionId(metadata).GetUnsafeValue(); + stream_->SetMetadata(std::move(metadata)); action.set_id(action_id); wire_action_->mutable_client_data()->set_sequence_number(action_id); *action.mutable_action() = std::move(*wire_action_); @@ -301,8 +303,9 @@ void UploadActionsTask::UpdateTokenAndFinish() { if (consistency_token_.empty()) return Done(UploadActionsStatus::kFinishedWithoutUpdatingConsistencyToken); - - stream_->GetMetadata()->SetConsistencyToken(consistency_token_); + feedstore::Metadata metadata = stream_->GetMetadata(); + metadata.set_consistency_token(consistency_token_); + stream_->SetMetadata(metadata); Done(UploadActionsStatus::kUpdatedConsistencyToken); }
diff --git a/components/feed/core/v2/test/stream_builder.cc b/components/feed/core/v2/test/stream_builder.cc index 4d37c83..4d81922 100644 --- a/components/feed/core/v2/test/stream_builder.cc +++ b/components/feed/core/v2/test/stream_builder.cc
@@ -8,6 +8,7 @@ #include "base/strings/strcat.h" #include "base/strings/string_number_conversions.h" +#include "components/feed/core/v2/feedstore_util.h" #include "components/feed/core/v2/proto_util.h" #include "components/feed/core/v2/protocol_translator.h" @@ -187,7 +188,7 @@ initial_update->stream_data.set_logging_enabled(logging_enabled); initial_update->stream_data.set_privacy_notice_fulfilled( privacy_notice_fulfilled); - SetLastAddedTime(last_added_time, initial_update->stream_data); + feedstore::SetLastAddedTime(last_added_time, initial_update->stream_data); return initial_update; } @@ -216,7 +217,7 @@ initial_update->stream_data.set_logging_enabled(logging_enabled); initial_update->stream_data.set_privacy_notice_fulfilled( privacy_notice_fulfilled); - SetLastAddedTime(last_added_time, initial_update->stream_data); + feedstore::SetLastAddedTime(last_added_time, initial_update->stream_data); return initial_update; }
diff --git a/components/feed/core/v2/test/test_util.h b/components/feed/core/v2/test/test_util.h new file mode 100644 index 0000000..6cd4857 --- /dev/null +++ b/components/feed/core/v2/test/test_util.h
@@ -0,0 +1,36 @@ +// Copyright 2021 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 COMPONENTS_FEED_CORE_V2_TEST_TEST_UTIL_H_ +#define COMPONENTS_FEED_CORE_V2_TEST_TEST_UTIL_H_ + +#include "base/time/time.h" + +// Some functionality shared among feed tests. +namespace feed { + +// Although time is mocked through TaskEnvironment, it does drift by small +// amounts. +const base::TimeDelta kEpsilon = base::TimeDelta::FromMilliseconds(5); +#define EXPECT_TIME_EQ(WANT, GOT) \ + { \ + base::Time want___ = (WANT), got___ = (GOT); \ + if (got___ != want___) { \ + EXPECT_LT(want___ - ::feed::kEpsilon, got___); \ + EXPECT_GT(want___ + ::feed::kEpsilon, got___); \ + } \ + } + +// This is EXPECT_EQ, but also dumps the string values for ease of reading. +#define EXPECT_STRINGS_EQUAL(WANT, GOT) \ + { \ + std::string want___ = (WANT), got___ = (GOT); \ + EXPECT_EQ(want___, got___) << "Wanted:\n" \ + << want___ << "\nBut got:\n" \ + << got___; \ + } + +} // namespace feed + +#endif // COMPONENTS_FEED_CORE_V2_TEST_TEST_UTIL_H_
diff --git a/components/feed/core/v2/wire_response_translator.cc b/components/feed/core/v2/wire_response_translator.cc new file mode 100644 index 0000000..daefe46 --- /dev/null +++ b/components/feed/core/v2/wire_response_translator.cc
@@ -0,0 +1,18 @@ +// Copyright 2021 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 "components/feed/core/v2/wire_response_translator.h" + +namespace feed { + +RefreshResponseData WireResponseTranslator::TranslateWireResponse( + feedwire::Response response, + StreamModelUpdateRequest::Source source, + bool was_signed_in_request, + base::Time current_time) const { + return ::feed::TranslateWireResponse(std::move(response), source, + was_signed_in_request, current_time); +} + +} // namespace feed
diff --git a/components/feed/core/v2/wire_response_translator.h b/components/feed/core/v2/wire_response_translator.h new file mode 100644 index 0000000..c4b8b99 --- /dev/null +++ b/components/feed/core/v2/wire_response_translator.h
@@ -0,0 +1,27 @@ +// Copyright 2021 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 COMPONENTS_FEED_CORE_V2_WIRE_RESPONSE_TRANSLATOR_H_ +#define COMPONENTS_FEED_CORE_V2_WIRE_RESPONSE_TRANSLATOR_H_ + +#include "components/feed/core/v2/protocol_translator.h" + +namespace feed { + +// Forwards to |feed::TranslateWireResponse()| by default. Can be overridden +// for testing. +class WireResponseTranslator { + public: + WireResponseTranslator() = default; + ~WireResponseTranslator() = default; + virtual RefreshResponseData TranslateWireResponse( + feedwire::Response response, + StreamModelUpdateRequest::Source source, + bool was_signed_in_request, + base::Time current_time) const; +}; + +} // namespace feed + +#endif // COMPONENTS_FEED_CORE_V2_WIRE_RESPONSE_TRANSLATOR_H_
diff --git a/components/management_strings.grdp b/components/management_strings.grdp index 52cdefe..5df9a3d7 100644 --- a/components/management_strings.grdp +++ b/components/management_strings.grdp
@@ -113,6 +113,9 @@ <message name="IDS_MANAGEMENT_REPORT_PRINTING" desc="Message stating that administrators can see names of printed files."> Names of files that you print </message> + <message name="IDS_MANAGEMENT_REPORT_PRINT_JOBS" desc="Message stating that administrators can see printing history associated with user, device, and printer."> + Printing history + </message> <message name="IDS_MANAGEMENT_CROSTINI" desc="Message stating that administrators can see Crostini usage"> Linux apps installed and when they were last used </message>
diff --git a/components/management_strings_grdp/IDS_MANAGEMENT_REPORT_PRINT_JOBS.png.sha1 b/components/management_strings_grdp/IDS_MANAGEMENT_REPORT_PRINT_JOBS.png.sha1 new file mode 100644 index 0000000..c82f077 --- /dev/null +++ b/components/management_strings_grdp/IDS_MANAGEMENT_REPORT_PRINT_JOBS.png.sha1
@@ -0,0 +1 @@ +31438fb5bab0db2e341f1c427db0057ef142baba \ No newline at end of file
diff --git a/components/messages/android/BUILD.gn b/components/messages/android/BUILD.gn index f5e8df4..ad4d3794 100644 --- a/components/messages/android/BUILD.gn +++ b/components/messages/android/BUILD.gn
@@ -9,19 +9,13 @@ # probably move MessageBanner* classes to internal/. android_library("java") { sources = [ - "java/src/org/chromium/components/messages/MessageAutoDismissTimer.java", - "java/src/org/chromium/components/messages/MessageBannerCoordinator.java", - "java/src/org/chromium/components/messages/MessageBannerMediator.java", "java/src/org/chromium/components/messages/MessageBannerProperties.java", - "java/src/org/chromium/components/messages/MessageBannerView.java", - "java/src/org/chromium/components/messages/MessageBannerViewBinder.java", "java/src/org/chromium/components/messages/MessageContainer.java", "java/src/org/chromium/components/messages/MessageDispatcher.java", "java/src/org/chromium/components/messages/MessageDispatcherBridge.java", "java/src/org/chromium/components/messages/MessageDispatcherProvider.java", "java/src/org/chromium/components/messages/MessageStateHandler.java", "java/src/org/chromium/components/messages/MessageWrapper.java", - "java/src/org/chromium/components/messages/SingleActionMessage.java", ] resources_package = "org.chromium.components.messages" annotation_processor_deps = [ "//base/android/jni_generator:jni_processor" ] @@ -42,11 +36,7 @@ } android_resources("java_resources") { - sources = [ - "java/res/drawable/message_bg_tinted.xml", - "java/res/layout/message_banner_view.xml", - "java/res/values/dimens.xml", - ] + sources = [ "java/res/values/dimens.xml" ] deps = [ "//components/browser_ui/strings/android:browser_ui_strings_grd", "//components/browser_ui/styles/android:java_resources", @@ -123,44 +113,12 @@ deps = [ "//base" ] } -android_library("javatests") { - testonly = true - sources = [ - "java/src/org/chromium/components/messages/MessageBannerRenderTest.java", - "java/src/org/chromium/components/messages/MessageBannerViewTest.java", - "java/src/org/chromium/components/messages/SingleActionMessageTest.java", - ] - resources_package = "org.chromium.components.messages" - deps = [ - ":java", - ":java_resources", - "//base:base_java", - "//base:base_java_test_support", - "//content/public/test/android:content_java_test_support", - "//third_party/android_deps:espresso_java", - "//third_party/android_support_test_runner:rules_java", - "//third_party/android_support_test_runner:runner_java", - "//third_party/androidx:androidx_annotation_annotation_java", - "//third_party/androidx:androidx_appcompat_appcompat_java", - "//third_party/androidx:androidx_appcompat_appcompat_resources_java", - "//third_party/androidx:androidx_core_core_java", - "//third_party/androidx:androidx_test_runner_java", - "//third_party/junit", - "//third_party/mockito:mockito_java", - "//ui/android:ui_java", - "//ui/android:ui_java_test_support", - ] -} - java_library("junit") { # Skip platform checks since Robolectric depends on requires_android targets. bypass_platform_checks = true testonly = true - sources = [ - "java/src/org/chromium/components/messages/MessageAutoDismissTimerTest.java", - "java/src/org/chromium/components/messages/MessageBannerMediatorUnitTest.java", - "java/src/org/chromium/components/messages/MessageWrapperTest.java", - ] + sources = + [ "java/src/org/chromium/components/messages/MessageWrapperTest.java" ] deps = [ ":java", "//base:base_java",
diff --git a/components/messages/android/internal/BUILD.gn b/components/messages/android/internal/BUILD.gn index ae777d2..169afced 100644 --- a/components/messages/android/internal/BUILD.gn +++ b/components/messages/android/internal/BUILD.gn
@@ -6,19 +6,32 @@ android_library("java") { sources = [ + "java/src/org/chromium/components/messages/MessageAutoDismissTimer.java", + "java/src/org/chromium/components/messages/MessageBannerCoordinator.java", + "java/src/org/chromium/components/messages/MessageBannerMediator.java", + "java/src/org/chromium/components/messages/MessageBannerView.java", + "java/src/org/chromium/components/messages/MessageBannerViewBinder.java", "java/src/org/chromium/components/messages/MessageDispatcherImpl.java", "java/src/org/chromium/components/messages/MessageQueueManager.java", "java/src/org/chromium/components/messages/MessageScopeChange.java", "java/src/org/chromium/components/messages/MessagesFactory.java", "java/src/org/chromium/components/messages/ScopeChangeController.java", + "java/src/org/chromium/components/messages/SingleActionMessage.java", ] + resources_package = "org.chromium.components.messages" + deps = [ + ":java_resources", "..:java", + "..:java_resources", "..:manager_java", "//base:base_java", + "//components/browser_ui/widget/android:java", "//content/public/android:content_java", "//third_party/androidx:androidx_annotation_annotation_java", + "//third_party/androidx:androidx_appcompat_appcompat_resources_java", + "//third_party/androidx:androidx_core_core_java", "//ui/android:ui_full_java", ] } @@ -28,6 +41,8 @@ bypass_platform_checks = true testonly = true sources = [ + "java/src/org/chromium/components/messages/MessageAutoDismissTimerTest.java", + "java/src/org/chromium/components/messages/MessageBannerMediatorUnitTest.java", "java/src/org/chromium/components/messages/MessageQueueManagerTest.java", "java/src/org/chromium/components/messages/ScopeChangeControllerTest.java", ] @@ -35,10 +50,51 @@ ":java", "..:java", "..:manager_java", + "//base:base_java", + "//base:base_java_test_support", "//base:base_junit_test_support", + "//components/browser_ui/widget/android:java", "//content/public/android:content_java", + "//third_party/android_deps:robolectric_all_java", "//third_party/androidx:androidx_test_runner_java", "//third_party/junit", "//third_party/mockito:mockito_java", + "//ui/android:ui_java", + ] +} + +android_library("javatests") { + testonly = true + sources = [ + "java/src/org/chromium/components/messages/MessageBannerRenderTest.java", + "java/src/org/chromium/components/messages/MessageBannerViewTest.java", + "java/src/org/chromium/components/messages/SingleActionMessageTest.java", + ] + deps = [ + ":java", + ":java_resources", + "..:java", + "//base:base_java", + "//base:base_java_test_support", + "//content/public/test/android:content_java_test_support", + "//third_party/android_deps:espresso_java", + "//third_party/androidx:androidx_test_runner_java", + "//third_party/junit", + "//third_party/mockito:mockito_java", + "//ui/android:ui_java", + "//ui/android:ui_java_test_support", + ] +} + +android_resources("java_resources") { + sources = [ + "java/res/drawable/message_bg_tinted.xml", + "java/res/layout/message_banner_view.xml", + "java/res/values/dimens.xml", + ] + deps = [ + "//components/browser_ui/strings/android:browser_ui_strings_grd", + "//components/browser_ui/styles/android:java_resources", + "//ui/android:ui_java_resources", ] }
diff --git a/components/messages/android/java/res/drawable/message_bg_tinted.xml b/components/messages/android/internal/java/res/drawable/message_bg_tinted.xml similarity index 100% rename from components/messages/android/java/res/drawable/message_bg_tinted.xml rename to components/messages/android/internal/java/res/drawable/message_bg_tinted.xml
diff --git a/components/messages/android/java/res/layout/message_banner_view.xml b/components/messages/android/internal/java/res/layout/message_banner_view.xml similarity index 96% rename from components/messages/android/java/res/layout/message_banner_view.xml rename to components/messages/android/internal/java/res/layout/message_banner_view.xml index e44aba6..bafa2d26 100644 --- a/components/messages/android/java/res/layout/message_banner_view.xml +++ b/components/messages/android/internal/java/res/layout/message_banner_view.xml
@@ -27,7 +27,7 @@ android:id="@+id/message_icon" android:paddingStart="@dimen/message_icon_padding" android:paddingEnd="@dimen/message_icon_padding" - android:tint="@color/default_icon_color_blue" + app:tint="@color/default_icon_color_blue" android:layout_weight="0" android:layout_width="wrap_content" android:layout_height="match_parent" @@ -56,7 +56,7 @@ <!-- Content description is set programmatically according to secondary button icon. --> <org.chromium.components.browser_ui.widget.listmenu.ListMenuButton android:id="@+id/message_secondary_button" - android:tint="@color/default_icon_color_secondary" + app:tint="@color/default_icon_color_secondary" android:layout_weight="0" android:paddingStart="@dimen/message_icon_padding" android:paddingEnd="@dimen/message_icon_padding"
diff --git a/components/messages/android/internal/java/res/values/dimens.xml b/components/messages/android/internal/java/res/values/dimens.xml new file mode 100644 index 0000000..b0394de --- /dev/null +++ b/components/messages/android/internal/java/res/values/dimens.xml
@@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright 2021 The Chromium Authors. All rights reserved. + Use of this source code is governed by a BSD-style license that can be + found in the LICENSE file. --> + +<resources> + + <dimen name="message_banner_height">64dp</dimen> + <dimen name="message_banner_radius">12dp</dimen> + <dimen name="message_banner_vertical_padding">12dp</dimen> + <dimen name="message_banner_elevation">6dp</dimen> + <!-- Min width to ensure the min touchable size. --> + <dimen name="message_banner_button_min_width">12dp</dimen> + <dimen name="message_icon_padding">12dp</dimen> + <dimen name="message_divider_margin">12dp</dimen> + <dimen name="message_shadow_lateral_margin">12dp</dimen> + <dimen name="message_shadow_bottom_margin">16dp</dimen> + <dimen name="message_vertical_hide_threshold">16dp</dimen> + <dimen name="message_horizontal_hide_threshold">24dp</dimen> + <dimen name="message_max_horizontal_translation">360dp</dimen> + <dimen name="message_max_width">380dp</dimen> + +</resources> \ No newline at end of file
diff --git a/components/messages/android/java/src/org/chromium/components/messages/MessageAutoDismissTimer.java b/components/messages/android/internal/java/src/org/chromium/components/messages/MessageAutoDismissTimer.java similarity index 100% rename from components/messages/android/java/src/org/chromium/components/messages/MessageAutoDismissTimer.java rename to components/messages/android/internal/java/src/org/chromium/components/messages/MessageAutoDismissTimer.java
diff --git a/components/messages/android/java/src/org/chromium/components/messages/MessageAutoDismissTimerTest.java b/components/messages/android/internal/java/src/org/chromium/components/messages/MessageAutoDismissTimerTest.java similarity index 100% rename from components/messages/android/java/src/org/chromium/components/messages/MessageAutoDismissTimerTest.java rename to components/messages/android/internal/java/src/org/chromium/components/messages/MessageAutoDismissTimerTest.java
diff --git a/components/messages/android/java/src/org/chromium/components/messages/MessageBannerCoordinator.java b/components/messages/android/internal/java/src/org/chromium/components/messages/MessageBannerCoordinator.java similarity index 100% rename from components/messages/android/java/src/org/chromium/components/messages/MessageBannerCoordinator.java rename to components/messages/android/internal/java/src/org/chromium/components/messages/MessageBannerCoordinator.java
diff --git a/components/messages/android/java/src/org/chromium/components/messages/MessageBannerMediator.java b/components/messages/android/internal/java/src/org/chromium/components/messages/MessageBannerMediator.java similarity index 100% rename from components/messages/android/java/src/org/chromium/components/messages/MessageBannerMediator.java rename to components/messages/android/internal/java/src/org/chromium/components/messages/MessageBannerMediator.java
diff --git a/components/messages/android/java/src/org/chromium/components/messages/MessageBannerMediatorUnitTest.java b/components/messages/android/internal/java/src/org/chromium/components/messages/MessageBannerMediatorUnitTest.java similarity index 100% rename from components/messages/android/java/src/org/chromium/components/messages/MessageBannerMediatorUnitTest.java rename to components/messages/android/internal/java/src/org/chromium/components/messages/MessageBannerMediatorUnitTest.java
diff --git a/components/messages/android/java/src/org/chromium/components/messages/MessageBannerRenderTest.java b/components/messages/android/internal/java/src/org/chromium/components/messages/MessageBannerRenderTest.java similarity index 100% rename from components/messages/android/java/src/org/chromium/components/messages/MessageBannerRenderTest.java rename to components/messages/android/internal/java/src/org/chromium/components/messages/MessageBannerRenderTest.java
diff --git a/components/messages/android/java/src/org/chromium/components/messages/MessageBannerView.java b/components/messages/android/internal/java/src/org/chromium/components/messages/MessageBannerView.java similarity index 100% rename from components/messages/android/java/src/org/chromium/components/messages/MessageBannerView.java rename to components/messages/android/internal/java/src/org/chromium/components/messages/MessageBannerView.java
diff --git a/components/messages/android/java/src/org/chromium/components/messages/MessageBannerViewBinder.java b/components/messages/android/internal/java/src/org/chromium/components/messages/MessageBannerViewBinder.java similarity index 100% rename from components/messages/android/java/src/org/chromium/components/messages/MessageBannerViewBinder.java rename to components/messages/android/internal/java/src/org/chromium/components/messages/MessageBannerViewBinder.java
diff --git a/components/messages/android/java/src/org/chromium/components/messages/MessageBannerViewTest.java b/components/messages/android/internal/java/src/org/chromium/components/messages/MessageBannerViewTest.java similarity index 100% rename from components/messages/android/java/src/org/chromium/components/messages/MessageBannerViewTest.java rename to components/messages/android/internal/java/src/org/chromium/components/messages/MessageBannerViewTest.java
diff --git a/components/messages/android/java/src/org/chromium/components/messages/SingleActionMessage.java b/components/messages/android/internal/java/src/org/chromium/components/messages/SingleActionMessage.java similarity index 100% rename from components/messages/android/java/src/org/chromium/components/messages/SingleActionMessage.java rename to components/messages/android/internal/java/src/org/chromium/components/messages/SingleActionMessage.java
diff --git a/components/messages/android/java/src/org/chromium/components/messages/SingleActionMessageTest.java b/components/messages/android/internal/java/src/org/chromium/components/messages/SingleActionMessageTest.java similarity index 78% rename from components/messages/android/java/src/org/chromium/components/messages/SingleActionMessageTest.java rename to components/messages/android/internal/java/src/org/chromium/components/messages/SingleActionMessageTest.java index 7e5c7ada..e579814 100644 --- a/components/messages/android/java/src/org/chromium/components/messages/SingleActionMessageTest.java +++ b/components/messages/android/internal/java/src/org/chromium/components/messages/SingleActionMessageTest.java
@@ -15,6 +15,9 @@ import androidx.test.filters.MediumTest; import org.junit.Assert; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.ClassRule; import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; @@ -26,16 +29,28 @@ import org.chromium.base.ApiCompatibilityUtils; import org.chromium.base.Callback; +import org.chromium.base.test.BaseActivityTestRule; import org.chromium.base.test.BaseJUnit4ClassRunner; import org.chromium.base.test.util.CallbackHelper; +import org.chromium.content_public.browser.test.util.TestThreadUtils; import org.chromium.ui.modelutil.PropertyModel; -import org.chromium.ui.test.util.DummyUiActivityTestCase; +import org.chromium.ui.test.util.DisableAnimationsTestRule; +import org.chromium.ui.test.util.DummyUiActivity; /** * Tests for {@link SingleActionMessage}. */ @RunWith(BaseJUnit4ClassRunner.class) -public class SingleActionMessageTest extends DummyUiActivityTestCase { +public class SingleActionMessageTest { + @ClassRule + public static DisableAnimationsTestRule sDisableAnimationsRule = + new DisableAnimationsTestRule(); + @ClassRule + public static BaseActivityTestRule<DummyUiActivity> sActivityTestRule = + new BaseActivityTestRule<>(DummyUiActivity.class); + + private static Activity sActivity; + @Rule public MockitoRule mMockitoRule = MockitoJUnit.rule(); @Mock @@ -45,23 +60,29 @@ private SingleActionMessage.DismissCallback mEmptyDismissCallback = (model, dismissReason) -> {}; - @Override - public void setUpTest() throws Exception { - super.setUpTest(); + @BeforeClass + public static void setupSuite() { + sActivityTestRule.launchActivity(null); + TestThreadUtils.runOnUiThreadBlocking( + () -> { sActivity = sActivityTestRule.getActivity(); }); + } + + @Before + public void setupTest() throws Exception { mDismissCallback = new CallbackHelper(); } @Test @MediumTest public void testAddAndRemoveSingleActionMessage() throws Exception { - MessageContainer container = new MessageContainer(getActivity(), null); + MessageContainer container = new MessageContainer(sActivity, null); PropertyModel model = createBasicSingleActionMessageModel(); SingleActionMessage message = new SingleActionMessage( container, model, mEmptyDismissCallback, () -> 0, () -> 0L, mAnimatorStartCallback); final MessageBannerCoordinator messageBanner = Mockito.mock(MessageBannerCoordinator.class); doNothing().when(messageBanner).show(any(Runnable.class)); doNothing().when(messageBanner).setOnTouchRunnable(any(Runnable.class)); - final MessageBannerView view = new MessageBannerView(getActivity(), null); + final MessageBannerView view = new MessageBannerView(sActivity, null); view.setId(R.id.message_banner); message.setMessageBannerForTesting(messageBanner); message.setViewForTesting(view); @@ -85,7 +106,7 @@ @Test @MediumTest public void testAutoDismissDuration() { - MessageContainer container = new MessageContainer(getActivity(), null); + MessageContainer container = new MessageContainer(sActivity, null); PropertyModel model = createBasicSingleActionMessageModel(); long duration = 42; SingleActionMessage message = new SingleActionMessage(container, model, @@ -97,7 +118,7 @@ @Test(expected = IllegalStateException.class) @MediumTest public void testAddMultipleSingleActionMessage() { - MessageContainer container = new MessageContainer(getActivity(), null); + MessageContainer container = new MessageContainer(sActivity, null); PropertyModel m1 = createBasicSingleActionMessageModel(); PropertyModel m2 = createBasicSingleActionMessageModel(); SingleActionMessage message1 = new SingleActionMessage( @@ -105,7 +126,7 @@ final MessageBannerCoordinator messageBanner1 = Mockito.mock(MessageBannerCoordinator.class); doNothing().when(messageBanner1).show(any(Runnable.class)); - final MessageBannerView view1 = new MessageBannerView(getActivity(), null); + final MessageBannerView view1 = new MessageBannerView(sActivity, null); view1.setId(R.id.message_banner); message1.setMessageBannerForTesting(messageBanner1); message1.setViewForTesting(view1); @@ -114,7 +135,7 @@ final MessageBannerCoordinator messageBanner2 = Mockito.mock(MessageBannerCoordinator.class); doNothing().when(messageBanner2).show(any(Runnable.class)); - final MessageBannerView view2 = new MessageBannerView(getActivity(), null); + final MessageBannerView view2 = new MessageBannerView(sActivity, null); view2.setId(R.id.message_banner); message2.setMessageBannerForTesting(messageBanner2); message2.setViewForTesting(view2); @@ -123,13 +144,12 @@ } private PropertyModel createBasicSingleActionMessageModel() { - Activity activity = getActivity(); return new PropertyModel.Builder(MessageBannerProperties.SINGLE_ACTION_MESSAGE_KEYS) .with(MessageBannerProperties.TITLE, "test") .with(MessageBannerProperties.DESCRIPTION, "Description") .with(MessageBannerProperties.ICON, ApiCompatibilityUtils.getDrawable( - activity.getResources(), android.R.drawable.ic_menu_add)) + sActivity.getResources(), android.R.drawable.ic_menu_add)) .with(MessageBannerProperties.ON_PRIMARY_ACTION, () -> {}) .with(MessageBannerProperties.ON_TOUCH_RUNNABLE, () -> {}) .with(MessageBannerProperties.ON_DISMISSED,
diff --git a/components/messages/android/java/res/values/dimens.xml b/components/messages/android/java/res/values/dimens.xml index 44a6549..256349c 100644 --- a/components/messages/android/java/res/values/dimens.xml +++ b/components/messages/android/java/res/values/dimens.xml
@@ -5,21 +5,7 @@ <resources> - <dimen name="message_banner_height">64dp</dimen> - <dimen name="message_banner_radius">12dp</dimen> - <dimen name="message_banner_vertical_padding">12dp</dimen> - <dimen name="message_banner_elevation">6dp</dimen> - <!-- Min width to ensure the min touchable size. --> - <dimen name="message_banner_button_min_width">12dp</dimen> - <dimen name="message_icon_padding">12dp</dimen> - <dimen name="message_divider_margin">12dp</dimen> <dimen name="message_shadow_top_margin">8dp</dimen> - <dimen name="message_shadow_lateral_margin">12dp</dimen> - <dimen name="message_shadow_bottom_margin">16dp</dimen> <dimen name="message_bubble_inset">8dp</dimen> - <dimen name="message_vertical_hide_threshold">16dp</dimen> - <dimen name="message_horizontal_hide_threshold">24dp</dimen> - <dimen name="message_max_horizontal_translation">360dp</dimen> - <dimen name="message_max_width">380dp</dimen> </resources> \ No newline at end of file
diff --git a/components/messages/android/java/src/org/chromium/components/messages/MessageContainer.java b/components/messages/android/java/src/org/chromium/components/messages/MessageContainer.java index b85bc85..e7ebee5 100644 --- a/components/messages/android/java/src/org/chromium/components/messages/MessageContainer.java +++ b/components/messages/android/java/src/org/chromium/components/messages/MessageContainer.java
@@ -18,6 +18,8 @@ * Container holding messages. */ public class MessageContainer extends FrameLayout { + private View mMessageBannerView; + public MessageContainer(@NonNull Context context, @Nullable AttributeSet attrs) { super(context, attrs); } @@ -28,10 +30,11 @@ * @param view The message view to display on the screen. */ void addMessage(View view) { - if (getChildCount() != 0) { + if (mMessageBannerView != null) { throw new IllegalStateException( "Should not contain any view when adding a new message."); } + mMessageBannerView = view; addView(view); // TODO(sinansahin): clipChildren should be set to false only when the message is in motion. ViewUtils.setAncestorsShouldClipChildren(this, false); @@ -42,11 +45,21 @@ * @param view The message which should be removed. */ void removeMessage(View view) { - if (indexOfChild(view) < 0) { + if (mMessageBannerView != view) { throw new IllegalStateException("The given view is not being shown."); } ViewUtils.setAncestorsShouldClipChildren(this, true); removeAllViews(); + mMessageBannerView = null; + } + + public int getMessageBannerHeight() { + assert mMessageBannerView != null; + return mMessageBannerView.getHeight(); + } + + public int getMessageShadowTopMargin() { + return getResources().getDimensionPixelOffset(R.dimen.message_shadow_top_margin); } /** @@ -55,14 +68,13 @@ * @param runnable The {@link Runnable}. */ void runAfterInitialMessageLayout(Runnable runnable) { - final View message = findViewById(R.id.message_banner); - assert message != null; - if (message.getHeight() > 0) { + assert mMessageBannerView != null; + if (mMessageBannerView.getHeight() > 0) { runnable.run(); return; } - message.addOnLayoutChangeListener(new OnLayoutChangeListener() { + mMessageBannerView.addOnLayoutChangeListener(new OnLayoutChangeListener() { @Override public void onLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom) {
diff --git a/components/optimization_guide/core/hints_processing_util.cc b/components/optimization_guide/core/hints_processing_util.cc index 4a482da..7b7fee5 100644 --- a/components/optimization_guide/core/hints_processing_util.cc +++ b/components/optimization_guide/core/hints_processing_util.cc
@@ -55,6 +55,8 @@ return "LinkPerformance"; case proto::OptimizationType::SHOPPING_PAGE_PREDICTOR: return "ShoppingPagePredictor"; + case proto::OptimizationType::LOGIN_DETECTION: + return "LoginDetection"; } NOTREACHED(); return std::string();
diff --git a/components/optimization_guide/proto/hints.proto b/components/optimization_guide/proto/hints.proto index 149f2f9..ad67a0d 100644 --- a/components/optimization_guide/proto/hints.proto +++ b/components/optimization_guide/proto/hints.proto
@@ -138,6 +138,9 @@ // increased to reduce the number of non-shopping page predictions for // shopping pages. SHOPPING_PAGE_PREDICTOR = 15; + // This optimization provides information about hosts that are identified as + // commonly logged-in. + LOGIN_DETECTION = 16; } // Presents semantics for how page load URLs should be matched.
diff --git a/components/proxy_config/proxy_prefs.h b/components/proxy_config/proxy_prefs.h index 8af504f..e955c90 100644 --- a/components/proxy_config/proxy_prefs.h +++ b/components/proxy_config/proxy_prefs.h
@@ -19,8 +19,12 @@ // Direct connection to the network, other proxy preferences are ignored. MODE_DIRECT = 0, - // Try to retrieve a PAC script from http://wpad/wpad.dat or fall back to - // direct connection. + // Try to auto-detect the PAC script location. + // On Windows and Chrome OS, DHCP is tried first (DHCP Option 252), and DNS + // (resolving http://wpad/wpad.dat) is tried second. + // On other platforms, only DNS is tried. + // If no PAC script can be found by this method, fall back to direct + // connection. MODE_AUTO_DETECT = 1, // Try to retrieve a PAC script from kProxyPacURL or fall back to direct
diff --git a/components/services/storage/dom_storage/legacy_dom_storage_database.cc b/components/services/storage/dom_storage/legacy_dom_storage_database.cc index 2f15263..716f171 100644 --- a/components/services/storage/dom_storage/legacy_dom_storage_database.cc +++ b/components/services/storage/dom_storage/legacy_dom_storage_database.cc
@@ -56,7 +56,7 @@ std::u16string key = statement.ColumnString16(0); std::u16string value; statement.ColumnBlobAsString16(1, &value); - (*result)[key] = base::NullableString16(value, false); + (*result)[key] = value; } known_to_be_empty_ = result->empty(); @@ -90,9 +90,9 @@ auto it = changes.begin(); for (; it != changes.end(); ++it) { sql::Statement statement; - std::u16string key = it->first; - base::NullableString16 value = it->second; - if (value.is_null()) { + const std::u16string& key = it->first; + const base::Optional<std::u16string>& value = it->second; + if (!value.has_value()) { statement.Assign(db_->GetCachedStatement( SQL_FROM_HERE, "DELETE FROM ItemTable WHERE key=?")); statement.BindString16(0, key); @@ -101,8 +101,8 @@ statement.Assign(db_->GetCachedStatement( SQL_FROM_HERE, "INSERT INTO ItemTable VALUES (?,?)")); statement.BindString16(0, key); - statement.BindBlob(1, value.string().data(), - value.string().length() * sizeof(char16_t)); + statement.BindBlob(1, value.value().data(), + value.value().length() * sizeof(char16_t)); known_to_be_empty_ = false; did_insert = true; }
diff --git a/components/services/storage/dom_storage/legacy_dom_storage_database.h b/components/services/storage/dom_storage/legacy_dom_storage_database.h index 055c7fc..5227ba9 100644 --- a/components/services/storage/dom_storage/legacy_dom_storage_database.h +++ b/components/services/storage/dom_storage/legacy_dom_storage_database.h
@@ -11,7 +11,6 @@ #include "base/files/file_path.h" #include "base/gtest_prod_util.h" -#include "base/strings/nullable_string16.h" #include "sql/database.h" namespace base { @@ -25,7 +24,7 @@ class FilesystemProxy; using LegacyDomStorageValuesMap = - std::map<std::u16string, base::NullableString16>; + std::map<std::u16string, base::Optional<std::u16string>>; // Represents a SQLite based backing for DOM storage data. This // class is designed to be used on a single thread. @@ -35,17 +34,16 @@ std::unique_ptr<FilesystemProxy> filesystem_proxy); virtual ~LegacyDomStorageDatabase(); // virtual for unit testing - // Reads all the key, value pairs stored in the database and returns - // them. |result| is assumed to be empty and any duplicate keys will - // be overwritten. If the database exists on disk then it will be - // opened. If it does not exist then it will not be created and - // |result| will be unmodified. + // Reads all the key, value pairs stored in the database and returns them. + // |result| is assumed to be empty and any duplicate keys will be overwritten. + // If the database exists on disk then it will be opened. If it does not exist + // then it will not be created and |result| will be unmodified. void ReadAllValues(LegacyDomStorageValuesMap* result); - // Updates the backing database. Will remove all keys before updating - // the database if |clear_all_first| is set. Then all entries in - // |changes| will be examined - keys mapped to a null NullableString16 - // will be removed and all others will be inserted/updated as appropriate. + // Updates the backing database. Will remove all keys before updating the + // database if |clear_all_first| is set. Then all entries in |changes| will be + // examined - keys mapped to a nullopt value will be removed and all others + // will be inserted/updated as appropriate. bool CommitChanges(bool clear_all_first, const LegacyDomStorageValuesMap& changes);
diff --git a/components/services/storage/dom_storage/legacy_dom_storage_database_unittest.cc b/components/services/storage/dom_storage/legacy_dom_storage_database_unittest.cc index ae0dc1e2..d4cf288 100644 --- a/components/services/storage/dom_storage/legacy_dom_storage_database_unittest.cc +++ b/components/services/storage/dom_storage/legacy_dom_storage_database_unittest.cc
@@ -8,6 +8,7 @@ #include "base/files/file_util.h" #include "base/files/scoped_temp_dir.h" #include "base/path_service.h" +#include "base/stl_util.h" #include "base/strings/utf_string_conversions.h" #include "components/services/storage/public/cpp/filesystem/filesystem_proxy.h" #include "sql/statement.h" @@ -54,23 +55,18 @@ LegacyDomStorageValuesMap::const_iterator it = values_read.begin(); for (; it != values_read.end(); ++it) { - std::u16string key = it->first; - base::NullableString16 value = it->second; - base::NullableString16 expected_value = expected.find(key)->second; - EXPECT_EQ(expected_value.string(), value.string()); - EXPECT_EQ(expected_value.is_null(), value.is_null()); + const std::u16string& key = it->first; + const base::Optional<std::u16string>& value = it->second; + const base::Optional<std::u16string>& expected_value = + expected.find(key)->second; + EXPECT_EQ(expected_value, value); } } void CreateMapWithValues(LegacyDomStorageValuesMap* values) { - std::u16string kCannedKeys[] = {ASCIIToUTF16("test"), ASCIIToUTF16("company"), - ASCIIToUTF16("date"), ASCIIToUTF16("empty")}; - base::NullableString16 kCannedValues[] = { - base::NullableString16(ASCIIToUTF16("123"), false), - base::NullableString16(ASCIIToUTF16("Google"), false), - base::NullableString16(ASCIIToUTF16("18-01-2012"), false), - base::NullableString16(std::u16string(), false)}; - for (unsigned i = 0; i < sizeof(kCannedKeys) / sizeof(kCannedKeys[0]); i++) + std::u16string kCannedKeys[] = {u"test", u"company", u"date", u"empty"}; + std::u16string kCannedValues[] = {u"123", u"Google", u"18-01-2012", u""}; + for (unsigned i = 0; i < base::size(kCannedKeys); i++) (*values)[kCannedKeys[i]] = kCannedValues[i]; } @@ -139,7 +135,7 @@ ASSERT_TRUE(db.CommitChanges(false, storage)); auto it = storage.begin(); for (; it != storage.end(); ++it) - it->second = base::NullableString16(); + it->second = base::nullopt; ASSERT_TRUE(db.CommitChanges(false, storage)); } EXPECT_FALSE(base::PathExists(file_name)); @@ -161,8 +157,7 @@ // Reading an empty db should not open the database. EXPECT_FALSE(db.IsOpen()); - values[ASCIIToUTF16("key")] = - base::NullableString16(ASCIIToUTF16("value"), false); + values[u"key"] = u"value"; db.CommitChanges(false, values); // Writing content should open the database. EXPECT_TRUE(db.IsOpen()); @@ -208,8 +203,7 @@ // Insert some values, clearing the database first. storage.clear(); - storage[ASCIIToUTF16("another_key")] = - base::NullableString16(ASCIIToUTF16("test"), false); + storage[u"another_key"] = u"test"; ASSERT_TRUE(db.CommitChanges(true, storage)); CheckValuesMatch(&db, storage); @@ -223,8 +217,8 @@ LegacyDomStorageDatabase db(MakeFilesystemProxy()); ASSERT_TRUE(db.LazyOpen(true)); - const std::u16string kCannedKey = ASCIIToUTF16("test"); - const base::NullableString16 kCannedValue(ASCIIToUTF16("data"), false); + const std::u16string kCannedKey = u"test"; + const std::u16string kCannedValue = u"data"; LegacyDomStorageValuesMap expected; expected[kCannedKey] = kCannedValue; @@ -235,7 +229,7 @@ LegacyDomStorageValuesMap values; // A null string in the map should mean that that key gets // removed. - values[kCannedKey] = base::NullableString16(); + values[kCannedKey] = base::nullopt; EXPECT_TRUE(db.CommitChanges(false, values)); expected.clear(); @@ -267,16 +261,15 @@ EXPECT_TRUE(db.IsOpen()); EXPECT_EQ(2u, values.size()); - LegacyDomStorageValuesMap::const_iterator it = - values.find(ASCIIToUTF16("value")); + LegacyDomStorageValuesMap::const_iterator it = values.find(u"value"); EXPECT_TRUE(it != values.end()); - EXPECT_EQ(ASCIIToUTF16("I am in local storage!"), it->second.string()); + EXPECT_EQ(u"I am in local storage!", it->second.value()); - it = values.find(ASCIIToUTF16("timestamp")); + it = values.find(u"timestamp"); EXPECT_TRUE(it != values.end()); - EXPECT_EQ(ASCIIToUTF16("1326738338841"), it->second.string()); + EXPECT_EQ(u"1326738338841", it->second.value()); - it = values.find(ASCIIToUTF16("not_there")); + it = values.find(u"not_there"); EXPECT_TRUE(it == values.end()); }
diff --git a/components/services/storage/dom_storage/local_storage_impl.cc b/components/services/storage/dom_storage/local_storage_impl.cc index 724475d..33d8bdbb 100644 --- a/components/services/storage/dom_storage/local_storage_impl.cc +++ b/components/services/storage/dom_storage/local_storage_impl.cc
@@ -128,7 +128,7 @@ auto values = std::make_unique<StorageAreaImpl::ValueMap>(); for (const auto& it : map) { (*values)[LocalStorageImpl::MigrateString(it.first)] = - LocalStorageImpl::MigrateString(it.second.string()); + LocalStorageImpl::MigrateString(it.second.value()); } reply_task_runner->PostTask( FROM_HERE, base::BindOnce(std::move(callback), std::move(values)));
diff --git a/components/services/storage/dom_storage/local_storage_impl_unittest.cc b/components/services/storage/dom_storage/local_storage_impl_unittest.cc index 0d6741f..08dbed9c 100644 --- a/components/services/storage/dom_storage/local_storage_impl_unittest.cc +++ b/components/services/storage/dom_storage/local_storage_impl_unittest.cc
@@ -696,8 +696,8 @@ old_db_path, std::make_unique<FilesystemProxy>( FilesystemProxy::UNRESTRICTED, local_storage_path)); LegacyDomStorageValuesMap data; - data[key] = base::NullableString16(value, false); - data[key2] = base::NullableString16(value, false); + data[key] = value; + data[key2] = value; db.CommitChanges(false, data); } EXPECT_TRUE(base::PathExists(old_db_path));
diff --git a/components/services/storage/dom_storage/session_storage_impl_unittest.cc b/components/services/storage/dom_storage/session_storage_impl_unittest.cc index dcc4224..49ad460 100644 --- a/components/services/storage/dom_storage/session_storage_impl_unittest.cc +++ b/components/services/storage/dom_storage/session_storage_impl_unittest.cc
@@ -183,8 +183,8 @@ auto db = base::MakeRefCounted<TestingLegacySessionStorageDatabase>( old_db_path, base::ThreadTaskRunnerHandle::Get().get()); LegacyDomStorageValuesMap data; - data[key] = base::NullableString16(value, false); - data[key2] = base::NullableString16(value, false); + data[key] = value; + data[key2] = value; EXPECT_TRUE(db->CommitAreaChanges(namespace_id1, origin1, false, data)); EXPECT_TRUE(db->CloneNamespace(namespace_id1, namespace_id2)); }
diff --git a/components/services/storage/dom_storage/session_storage_metadata_unittest.cc b/components/services/storage/dom_storage/session_storage_metadata_unittest.cc index d45b704..7ebf1ffe 100644 --- a/components/services/storage/dom_storage/session_storage_metadata_unittest.cc +++ b/components/services/storage/dom_storage/session_storage_metadata_unittest.cc
@@ -485,8 +485,8 @@ key2.push_back(0xd83d); key2.push_back(0xde00); LegacyDomStorageValuesMap data; - data[key] = base::NullableString16(value, false); - data[key2] = base::NullableString16(value, false); + data[key] = value; + data[key2] = value; EXPECT_TRUE(old_ss_database_->CommitAreaChanges(test_namespace1_id_, test_origin1_, false, data)); EXPECT_TRUE(old_ss_database_->CloneNamespace(test_namespace1_id_,
diff --git a/components/services/storage/dom_storage/testing_legacy_session_storage_database.cc b/components/services/storage/dom_storage/testing_legacy_session_storage_database.cc index aee1163c..436cef3b 100644 --- a/components/services/storage/dom_storage/testing_legacy_session_storage_database.cc +++ b/components/services/storage/dom_storage/testing_legacy_session_storage_database.cc
@@ -719,15 +719,14 @@ std::u16string key16 = base::UTF8ToUTF16(key.substr(map_start_key.length())); if (only_keys) { - (*result)[key16] = base::NullableString16(); + (*result)[key16] = base::nullopt; } else { // Convert the raw data stored in std::string (it->value()) to raw data // stored in std::u16string. size_t len = it->value().size() / sizeof(char16_t); const char16_t* data_ptr = reinterpret_cast<const char16_t*>(it->value().data()); - (*result)[key16] = - base::NullableString16(std::u16string(data_ptr, len), false); + (*result)[key16] = std::u16string(data_ptr, len); } } return true; @@ -738,15 +737,15 @@ const LegacyDomStorageValuesMap& values, leveldb::WriteBatch* batch) { for (auto it = values.begin(); it != values.end(); ++it) { - base::NullableString16 value = it->second; + const base::Optional<std::u16string>& value = it->second; std::string key = MapKey(map_id, base::UTF16ToUTF8(it->first)); - if (value.is_null()) { + if (!value.has_value()) { batch->Delete(key); } else { // Convert the raw data stored in std::u16string to raw data stored in // std::string. - const char* data = reinterpret_cast<const char*>(value.string().data()); - size_t size = value.string().size() * 2; + const char* data = reinterpret_cast<const char*>(value.value().data()); + size_t size = value.value().size() * 2; batch->Put(key, leveldb::Slice(data, size)); } }
diff --git a/components/services/storage/dom_storage/testing_legacy_session_storage_database.h b/components/services/storage/dom_storage/testing_legacy_session_storage_database.h index 5ed8dd0..21dd47af 100644 --- a/components/services/storage/dom_storage/testing_legacy_session_storage_database.h +++ b/components/services/storage/dom_storage/testing_legacy_session_storage_database.h
@@ -76,8 +76,8 @@ // Updates the data for |namespace_id| and |origin|. Will remove all keys // before updating the database if |clear_all_first| is set. Then all entries - // in |changes| will be examined - keys mapped to a null NullableString16 will - // be removed and all others will be inserted/updated as appropriate. It is + // in |changes| will be examined - keys mapped to a nullopt value will be + // removed and all others will be inserted/updated as appropriate. It is // allowed to write data into a shallow copy created by CloneNamespace, and in // that case the copy will be made deep before writing the values. bool CommitAreaChanges(const std::string& namespace_id,
diff --git a/components/viz/common/quads/compositor_render_pass_unittest.cc b/components/viz/common/quads/compositor_render_pass_unittest.cc index f99301f..0b83d642 100644 --- a/components/viz/common/quads/compositor_render_pass_unittest.cc +++ b/components/viz/common/quads/compositor_render_pass_unittest.cc
@@ -301,8 +301,8 @@ auto* quad = pass->quad_list.AllocateAndConstruct<SolidColorDrawQuad>(); gfx::Rect quad_rect(1, 2, 3, 4); quad->SetNew(quad_state, quad_rect, quad_rect, SkColor(), false); - pass->ReplaceExistingQuadWithOpaqueTransparentSolidColor( - pass->quad_list.begin()); + pass->ReplaceExistingQuadWithSolidColor(pass->quad_list.begin(), SkColor(), + SkBlendMode::kSrcOver); EXPECT_EQ(pass->quad_list.begin()->rect, quad_rect); } @@ -312,10 +312,49 @@ auto* quad = pass->quad_list.AllocateAndConstruct<SolidColorDrawQuad>(); gfx::Rect quad_rect(1, 2, 3, 4); quad->SetNew(quad_state, quad_rect, quad_rect, SkColor(), false); - pass->ReplaceExistingQuadWithOpaqueTransparentSolidColor( - pass->quad_list.begin()); + pass->ReplaceExistingQuadWithSolidColor(pass->quad_list.begin(), SkColor(), + SkBlendMode::kSrcOver); EXPECT_FALSE(pass->quad_list.begin()->shared_quad_state->are_contents_opaque); } +TEST(CompositorRenderPassTest, ReplacedQuadsGetColor) { + auto pass = CompositorRenderPass::Create(); + const SharedQuadState* quad_state = pass->CreateAndAppendSharedQuadState(); + auto* quad = pass->quad_list.AllocateAndConstruct<SolidColorDrawQuad>(); + const gfx::Rect quad_rect(1, 2, 3, 4); + quad->SetNew(quad_state, quad_rect, quad_rect, SK_ColorRED, false); + pass->ReplaceExistingQuadWithSolidColor(pass->quad_list.begin(), + SK_ColorGREEN, SkBlendMode::kSrcOver); + EXPECT_EQ(SK_ColorGREEN, quad->color); +} + +TEST(CompositorRenderPassTest, ReplacedQuadsGetBlendMode) { + auto pass = CompositorRenderPass::Create(); + SharedQuadState* quad_state = pass->CreateAndAppendSharedQuadState(); + // Make |are_contents_opaque| already false, to test that the blend mode is + // recognized as a reason for needing a new |SharedQuadState|. + quad_state->are_contents_opaque = false; + auto* quad = pass->quad_list.AllocateAndConstruct<SolidColorDrawQuad>(); + const gfx::Rect quad_rect(1, 2, 3, 4); + quad->SetNew(quad_state, quad_rect, quad_rect, SkColor(), false); + pass->ReplaceExistingQuadWithSolidColor(pass->quad_list.begin(), SkColor(), + SkBlendMode::kDstOut); + EXPECT_EQ(SkBlendMode::kDstOut, quad->shared_quad_state->blend_mode); +} + +TEST(CompositorRenderPassTest, + ReplacedQuadsKeepOldSharedQuadStateWhenPossible) { + auto pass = CompositorRenderPass::Create(); + SharedQuadState* quad_state = pass->CreateAndAppendSharedQuadState(); + quad_state->are_contents_opaque = false; + quad_state->blend_mode = SkBlendMode::kSoftLight; + auto* quad = pass->quad_list.AllocateAndConstruct<SolidColorDrawQuad>(); + const gfx::Rect quad_rect(1, 2, 3, 4); + quad->SetNew(quad_state, quad_rect, quad_rect, SK_ColorRED, false); + pass->ReplaceExistingQuadWithSolidColor( + pass->quad_list.begin(), SK_ColorGREEN, SkBlendMode::kSoftLight); + EXPECT_EQ(quad_state, quad->shared_quad_state); +} + } // namespace } // namespace viz
diff --git a/components/viz/common/quads/draw_quad.h b/components/viz/common/quads/draw_quad.h index 4900fb6..d80735d 100644 --- a/components/viz/common/quads/draw_quad.h +++ b/components/viz/common/quads/draw_quad.h
@@ -76,9 +76,13 @@ bool IsDebugQuad() const { return material == Material::kDebugBorder; } - bool ShouldDrawWithBlending() const { + bool ShouldDrawWithBlendingForReasonOtherThanMaskFilter() const { return needs_blending || shared_quad_state->opacity < 1.0f || - shared_quad_state->blend_mode != SkBlendMode::kSrcOver || + shared_quad_state->blend_mode != SkBlendMode::kSrcOver; + } + + bool ShouldDrawWithBlending() const { + return ShouldDrawWithBlendingForReasonOtherThanMaskFilter() || !shared_quad_state->mask_filter_info.IsEmpty(); }
diff --git a/components/viz/common/quads/render_pass_internal.cc b/components/viz/common/quads/render_pass_internal.cc index 6c9ac6a..ba6383c 100644 --- a/components/viz/common/quads/render_pass_internal.cc +++ b/components/viz/common/quads/render_pass_internal.cc
@@ -35,24 +35,25 @@ return shared_quad_state_list.AllocateAndConstruct<SharedQuadState>(); } -void RenderPassInternal::ReplaceExistingQuadWithOpaqueTransparentSolidColor( - QuadList::Iterator at) { - // In order to fill the backbuffer with transparent black, the replacement - // solid color quad needs to set |needs_blending| to false, and - // ShouldDrawWithBlending() returns false so it is drawn without blending. - const gfx::Rect rect = at->rect; - bool needs_blending = false; +void RenderPassInternal::ReplaceExistingQuadWithSolidColor( + QuadList::Iterator at, + SkColor color, + SkBlendMode blend_mode) { const SharedQuadState* shared_quad_state = at->shared_quad_state; - if (shared_quad_state->are_contents_opaque) { + if (shared_quad_state->are_contents_opaque || + shared_quad_state->blend_mode != blend_mode) { auto* new_shared_quad_state = shared_quad_state_list.AllocateAndCopyFrom(shared_quad_state); new_shared_quad_state->are_contents_opaque = false; + new_shared_quad_state->blend_mode = blend_mode; shared_quad_state = new_shared_quad_state; } - auto* replacement = quad_list.ReplaceExistingElement<SolidColorDrawQuad>(at); - replacement->SetAll(shared_quad_state, rect, rect /* visible_rect */, - needs_blending, SK_ColorTRANSPARENT, true); + const gfx::Rect rect = at->rect; + quad_list.ReplaceExistingElement<SolidColorDrawQuad>(at)->SetAll( + shared_quad_state, rect, /*visible_rect=*/rect, + /*needs_blending=*/false, color, + /*force_anti_aliasing_off=*/true); } } // namespace viz
diff --git a/components/viz/common/quads/render_pass_internal.h b/components/viz/common/quads/render_pass_internal.h index a3a1bbf3..9165c5c48 100644 --- a/components/viz/common/quads/render_pass_internal.h +++ b/components/viz/common/quads/render_pass_internal.h
@@ -31,9 +31,10 @@ public: SharedQuadState* CreateAndAppendSharedQuadState(); - // Replaces a quad in |quad_list| with a transparent black SolidColorQuad. - void ReplaceExistingQuadWithOpaqueTransparentSolidColor( - QuadList::Iterator at); + // Replaces a quad in |quad_list| with a |SolidColorDrawQuad|. + void ReplaceExistingQuadWithSolidColor(QuadList::Iterator at, + SkColor color, + SkBlendMode blend_mode); // These are in the space of the render pass' physical pixels. gfx::Rect output_rect;
diff --git a/components/viz/service/display/ca_layer_overlay.cc b/components/viz/service/display/ca_layer_overlay.cc index 171b2e6..284d8f0 100644 --- a/components/viz/service/display/ca_layer_overlay.cc +++ b/components/viz/service/display/ca_layer_overlay.cc
@@ -363,7 +363,8 @@ break; } - render_pass->ReplaceExistingQuadWithOpaqueTransparentSolidColor(it); + render_pass->ReplaceExistingQuadWithSolidColor(it, SK_ColorTRANSPARENT, + SkBlendMode::kSrcOver); ca_layer_overlays->push_back(ca_layer); } }
diff --git a/components/viz/service/display/dc_layer_overlay.cc b/components/viz/service/display/dc_layer_overlay.cc index d51453f3..4ef9ac4 100644 --- a/components/viz/service/display/dc_layer_overlay.cc +++ b/components/viz/service/display/dc_layer_overlay.cc
@@ -890,7 +890,8 @@ // When the opacity == 1.0, drawing with transparent will be done without // blending and will have the proper effect of completely clearing the // layer. - render_pass->ReplaceExistingQuadWithOpaqueTransparentSolidColor(it); + render_pass->ReplaceExistingQuadWithSolidColor(it, SK_ColorTRANSPARENT, + SkBlendMode::kSrcOver); is_opaque = true; }
diff --git a/components/viz/service/display/overlay_candidate.cc b/components/viz/service/display/overlay_candidate.cc index fa02f20..49d1388 100644 --- a/components/viz/service/display/overlay_candidate.cc +++ b/components/viz/service/display/overlay_candidate.cc
@@ -145,9 +145,6 @@ if (sqs->opacity != 1.f) return false; - // We can't support overlays with mask filter. - if (!sqs->mask_filter_info.IsEmpty()) - return false; // We support only kSrc (no blending) and kSrcOver (blending with premul). if (!(sqs->blend_mode == SkBlendMode::kSrc || sqs->blend_mode == SkBlendMode::kSrcOver)) { @@ -315,7 +312,9 @@ candidate->clip_rect = sqs->clip_rect; candidate->is_clipped = sqs->is_clipped; - candidate->is_opaque = !quad->ShouldDrawWithBlending(); + candidate->is_opaque = + !quad->ShouldDrawWithBlendingForReasonOtherThanMaskFilter(); + candidate->has_mask_filter = !sqs->mask_filter_info.IsEmpty(); // For underlays the function 'EstimateVisibleDamage()' is called to update // |damage_area_estimate| to more accurately reflect the actual visible // damage. @@ -345,6 +344,10 @@ candidate->display_rect = gfx::RectF(quad->rect); transform.TransformRect(&candidate->display_rect); candidate->transform = overlay_transform; + candidate->is_opaque = + !quad->ShouldDrawWithBlendingForReasonOtherThanMaskFilter(); + candidate->has_mask_filter = + !quad->shared_quad_state->mask_filter_info.IsEmpty(); // For underlays the function 'EstimateVisibleDamage()' is called to update // |damage_area_estimate| to more accurately reflect the actual visible // damage.
diff --git a/components/viz/service/display/overlay_candidate.h b/components/viz/service/display/overlay_candidate.h index 0d7ee6be..c04e71bd 100644 --- a/components/viz/service/display/overlay_candidate.h +++ b/components/viz/service/display/overlay_candidate.h
@@ -101,6 +101,8 @@ bool is_clipped = false; // If the quad doesn't require blending. bool is_opaque = false; + // If the quad has a mask filter. + bool has_mask_filter = false; // Texture resource to present in an overlay. ResourceId resource_id = kInvalidResourceId; // Mailbox from resource_id. It is used by SkiaRenderer.
diff --git a/components/viz/service/display/overlay_processor_using_strategy.cc b/components/viz/service/display/overlay_processor_using_strategy.cc index 3c41bbe..a6488c6 100644 --- a/components/viz/service/display/overlay_processor_using_strategy.cc +++ b/components/viz/service/display/overlay_processor_using_strategy.cc
@@ -240,12 +240,14 @@ DCHECK_LE(candidates->size(), 1U); gfx::Rect this_frame_overlay_rect; + bool has_mask_filter = false; bool is_opaque_overlay = false; bool is_underlay = false; uint32_t exclude_overlay_index = OverlayCandidate::kInvalidDamageIndex; for (const OverlayCandidate& overlay : *candidates) { this_frame_overlay_rect = GetOverlayDamageRectForOutputSurface(overlay); + has_mask_filter = overlay.has_mask_filter; if (overlay.plane_z_order >= 0) { // If an overlay candidate comes from output surface, its z-order should // be 0. @@ -272,13 +274,11 @@ exclude_overlay_index, surface_damage_rect_list, *damage_rect, this_frame_overlay_rect, is_opaque_overlay && !is_underlay); - // Track the overlay_rect from frame to frame. If it is the same and nothing - // is on top of it then that rect doesn't need to be damaged because the - // drawing is occurring on a different plane. If it is different then that - // indicates that a different overlay has been chosen and the previous - // overlay rect should be damaged because it has changed planes from the - // overlay plane to the main plane. https://crbug.com/875879 + // Drawing on the overlay_rect usually occurs on a different plane, but we + // still need to damage the overlay_rect when certain changes occur from one + // frame to the next. https://crbug.com/875879 https://crbug.com/1107460 if ((!previous_is_underlay && is_underlay) || + has_mask_filter != previous_has_mask_filter_ || this_frame_overlay_rect != previous_frame_overlay_rect_) { damage_rect->Union(previous_frame_overlay_rect_); @@ -293,6 +293,7 @@ } previous_frame_overlay_rect_ = this_frame_overlay_rect; + previous_has_mask_filter_ = has_mask_filter; previous_is_underlay = is_underlay; }
diff --git a/components/viz/service/display/overlay_processor_using_strategy.h b/components/viz/service/display/overlay_processor_using_strategy.h index 2b9220d..9ed5151 100644 --- a/components/viz/service/display/overlay_processor_using_strategy.h +++ b/components/viz/service/display/overlay_processor_using_strategy.h
@@ -175,6 +175,7 @@ gfx::Rect overlay_damage_rect_; bool previous_is_underlay = false; + bool previous_has_mask_filter_ = false; gfx::Rect previous_frame_overlay_rect_; struct OverlayPrioritizationConfig {
diff --git a/components/viz/service/display/overlay_strategy_single_on_top.cc b/components/viz/service/display/overlay_strategy_single_on_top.cc index 5303586..0a29619 100644 --- a/components/viz/service/display/overlay_strategy_single_on_top.cc +++ b/components/viz/service/display/overlay_strategy_single_on_top.cc
@@ -41,6 +41,7 @@ if (OverlayCandidate::FromDrawQuad( resource_provider, surface_damage_rect_list, output_color_matrix, *it, GetPrimaryPlaneDisplayRect(primary_plane), &candidate) && + !candidate.has_mask_filter && !OverlayCandidate::IsOccluded(candidate, quad_list->cbegin(), it)) { // If the candidate has been promoted previously and has not changed // (resource ID is the same) for 3 frames, do not use it as Overlay as @@ -95,6 +96,7 @@ if (OverlayCandidate::FromDrawQuad( resource_provider, surface_damage_rect_list, output_color_matrix, *it, GetPrimaryPlaneDisplayRect(primary_plane), &candidate) && + !candidate.has_mask_filter && !OverlayCandidate::IsOccluded(candidate, quad_list->cbegin(), it)) { candidates->push_back({it, candidate, this}); }
diff --git a/components/viz/service/display/overlay_strategy_underlay.cc b/components/viz/service/display/overlay_strategy_underlay.cc index 4008f9f..15080528 100644 --- a/components/viz/service/display/overlay_strategy_underlay.cc +++ b/components/viz/service/display/overlay_strategy_underlay.cc
@@ -75,9 +75,15 @@ } // If the candidate can be handled by an overlay, create a pass for it. We - // need to switch out the video quad with a black transparent one. + // need to switch out the video quad with an underlay hole quad. if (new_candidate_list.back().overlay_handled) { - render_pass->ReplaceExistingQuadWithOpaqueTransparentSolidColor(it); + if (candidate.has_mask_filter) { + render_pass->ReplaceExistingQuadWithSolidColor(it, SK_ColorBLACK, + SkBlendMode::kDstOut); + } else { + render_pass->ReplaceExistingQuadWithSolidColor(it, SK_ColorTRANSPARENT, + SkBlendMode::kSrcOver); + } candidate_list->swap(new_candidate_list); return true; } @@ -164,10 +170,16 @@ } // If the candidate can be handled by an overlay, create a pass for it. We - // need to switch out the video quad with a black transparent one. + // need to switch out the video quad with an underlay hole quad. if (new_candidate_list.back().overlay_handled) { - render_pass->ReplaceExistingQuadWithOpaqueTransparentSolidColor( - proposed_candidate->quad_iter); + if (proposed_candidate->candidate.has_mask_filter) { + render_pass->ReplaceExistingQuadWithSolidColor( + proposed_candidate->quad_iter, SK_ColorBLACK, SkBlendMode::kDstOut); + } else { + render_pass->ReplaceExistingQuadWithSolidColor( + proposed_candidate->quad_iter, SK_ColorTRANSPARENT, + SkBlendMode::kSrcOver); + } candidate_list->swap(new_candidate_list); return true;
diff --git a/components/viz/service/display/overlay_strategy_underlay_cast.cc b/components/viz/service/display/overlay_strategy_underlay_cast.cc index bae8d3a..045f9220 100644 --- a/components/viz/service/display/overlay_strategy_underlay_cast.cc +++ b/components/viz/service/display/overlay_strategy_underlay_cast.cc
@@ -122,7 +122,13 @@ VideoHoleDrawQuad::MaterialCast(*it)->overlay_plane_id); #endif - render_pass->ReplaceExistingQuadWithOpaqueTransparentSolidColor(it); + if (candidate.has_mask_filter) { + render_pass->ReplaceExistingQuadWithSolidColor(it, SK_ColorBLACK, + SkBlendMode::kDstOut); + } else { + render_pass->ReplaceExistingQuadWithSolidColor(it, SK_ColorTRANSPARENT, + SkBlendMode::kSrcOver); + } break; } @@ -255,7 +261,13 @@ VideoHoleDrawQuad::MaterialCast(*it)->overlay_plane_id); #endif - render_pass->ReplaceExistingQuadWithOpaqueTransparentSolidColor(it); + if (candidate.has_mask_filter) { + render_pass->ReplaceExistingQuadWithSolidColor(it, SK_ColorBLACK, + SkBlendMode::kDstOut); + } else { + render_pass->ReplaceExistingQuadWithSolidColor(it, SK_ColorTRANSPARENT, + SkBlendMode::kSrcOver); + } break; }
diff --git a/components/viz/service/display/overlay_unittest.cc b/components/viz/service/display/overlay_unittest.cc index d0f4a8c..a9565798 100644 --- a/components/viz/service/display/overlay_unittest.cc +++ b/components/viz/service/display/overlay_unittest.cc
@@ -2190,6 +2190,56 @@ } } +// A candidate with a mask filter must go to underlay, and not single on top. +// Also, the quad must be replaced by a black quad with |SkBlendMode::kDstOut|. +TEST_F(TransitionOverlayTypeTest, MaskFilterBringsUnderlay) { + auto pass = CreateRenderPass(); + damage_rect_ = kOverlayRect; + + SharedQuadState* default_damaged_shared_quad_state = + pass->shared_quad_state_list.AllocateAndCopyFrom( + pass->shared_quad_state_list.back()); + + SurfaceDamageRectList surface_damage_rect_list; + auto* sqs = pass->shared_quad_state_list.front(); + sqs->overlay_damage_index = 0; + surface_damage_rect_list.emplace_back(damage_rect_); + sqs->mask_filter_info = + gfx::MaskFilterInfo(gfx::RectF(kOverlayRect), gfx::RoundedCornersF(1.f)); + CreateFullscreenCandidateQuad(resource_provider_.get(), + child_resource_provider_.get(), + child_provider_.get(), sqs, pass.get()); + CreateFullscreenOpaqueQuad(resource_provider_.get(), + default_damaged_shared_quad_state, pass.get()); + + OverlayCandidateList candidate_list; + OverlayProcessorInterface::FilterOperationsMap render_pass_filters; + OverlayProcessorInterface::FilterOperationsMap render_pass_backdrop_filters; + AggregatedRenderPassList pass_list; + pass_list.push_back(std::move(pass)); + + overlay_processor_->ProcessForOverlays( + resource_provider_.get(), &pass_list, GetIdentityColorMatrix(), + render_pass_filters, render_pass_backdrop_filters, + std::move(surface_damage_rect_list), nullptr, &candidate_list, + &damage_rect_, &content_bounds_); + + ASSERT_EQ(candidate_list.size(), 1U); + EXPECT_LT(candidate_list.front().plane_z_order, 0); + + ASSERT_EQ(1U, pass_list.size()); + ASSERT_EQ(2U, pass_list.front()->quad_list.size()); + EXPECT_EQ(SK_ColorBLACK, static_cast<SolidColorDrawQuad*>( + pass_list.front()->quad_list.front()) + ->color); + EXPECT_FALSE(pass_list.front() + ->quad_list.front() + ->shared_quad_state->are_contents_opaque); + EXPECT_EQ( + SkBlendMode::kDstOut, + pass_list.front()->quad_list.front()->shared_quad_state->blend_mode); +} + // The first time an underlay is scheduled its damage must not be excluded. TEST_F(UnderlayTest, InitialUnderlayDamageNotExcluded) { auto pass = CreateRenderPass(); @@ -2372,6 +2422,58 @@ } } +// Underlay damage cannot be excluded if the underlay has a mask filter in the +// current frame but did not in the previous frame or vice versa. +TEST_F( + UnderlayTest, + DamageNotExcludedForConsecutiveUnderlaysIfOneHasMaskFilterAndOtherDoesNot) { + constexpr bool kHasMaskFilter[] = {true, false, true, false, true, + true, true, false, false, false}; + + for (int i = 0; i < 10; ++i) { + SCOPED_TRACE(i); + + auto pass = CreateRenderPass(); + damage_rect_ = kOverlayRect; + + SharedQuadState* default_damaged_shared_quad_state = + pass->shared_quad_state_list.AllocateAndCopyFrom( + pass->shared_quad_state_list.back()); + + SurfaceDamageRectList surface_damage_rect_list; + auto* sqs = pass->shared_quad_state_list.front(); + sqs->overlay_damage_index = 0; + surface_damage_rect_list.emplace_back(damage_rect_); + if (kHasMaskFilter[i]) { + sqs->mask_filter_info = gfx::MaskFilterInfo(gfx::RectF(kOverlayRect), + gfx::RoundedCornersF(1.f)); + } + CreateCandidateQuadAt(resource_provider_.get(), + child_resource_provider_.get(), child_provider_.get(), + sqs, pass.get(), kOverlayRect); + CreateFullscreenOpaqueQuad(resource_provider_.get(), + default_damaged_shared_quad_state, pass.get()); + + OverlayCandidateList candidate_list; + OverlayProcessorInterface::FilterOperationsMap render_pass_filters; + OverlayProcessorInterface::FilterOperationsMap render_pass_backdrop_filters; + AggregatedRenderPassList pass_list; + pass_list.push_back(std::move(pass)); + + overlay_processor_->ProcessForOverlays( + resource_provider_.get(), &pass_list, GetIdentityColorMatrix(), + render_pass_filters, render_pass_backdrop_filters, + std::move(surface_damage_rect_list), nullptr, &candidate_list, + &damage_rect_, &content_bounds_); + if (i == 0) { + EXPECT_FALSE(damage_rect_.IsEmpty()); + } else { + EXPECT_EQ(damage_rect_.IsEmpty(), + kHasMaskFilter[i] == kHasMaskFilter[i - 1]); + } + } +} + TEST_F(UnderlayTest, DamageExcludedForCandidateAndThoseOccluded) { for (int i = 0; i < 3; ++i) { SCOPED_TRACE(i); @@ -3067,6 +3169,45 @@ ASSERT_TRUE(candidate_list.empty()); EXPECT_TRUE(content_bounds_.empty()); } + +TEST_F(UnderlayCastTest, OverlayPromotionWithMaskFilter) { + auto pass = CreateRenderPass(); + + SurfaceDamageRectList surface_damage_rect_list; + auto* sqs = pass->shared_quad_state_list.front(); + sqs->overlay_damage_index = 0; + surface_damage_rect_list.emplace_back(damage_rect_); + sqs->mask_filter_info = + gfx::MaskFilterInfo(gfx::RectF(kOverlayRect), gfx::RoundedCornersF(1.f)); + CreateVideoHoleDrawQuadAt(sqs, pass.get(), kOverlayRect); + + OverlayCandidateList candidate_list; + OverlayProcessorInterface::FilterOperationsMap render_pass_filters; + OverlayProcessorInterface::FilterOperationsMap render_pass_backdrop_filters; + AggregatedRenderPassList pass_list; + pass_list.push_back(std::move(pass)); + + overlay_processor_->ProcessForOverlays( + resource_provider_.get(), &pass_list, GetIdentityColorMatrix(), + render_pass_filters, render_pass_backdrop_filters, + std::move(surface_damage_rect_list), nullptr, &candidate_list, + &damage_rect_, &content_bounds_); + + ASSERT_EQ(1U, content_bounds_.size()); + EXPECT_TRUE(content_bounds_.front().IsEmpty()); + + ASSERT_EQ(1U, pass_list.size()); + ASSERT_EQ(1U, pass_list.front()->quad_list.size()); + EXPECT_EQ(SK_ColorBLACK, static_cast<SolidColorDrawQuad*>( + pass_list.front()->quad_list.front()) + ->color); + EXPECT_FALSE(pass_list.front() + ->quad_list.front() + ->shared_quad_state->are_contents_opaque); + EXPECT_EQ( + SkBlendMode::kDstOut, + pass_list.front()->quad_list.front()->shared_quad_state->blend_mode); +} #endif #if defined(ALWAYS_ENABLE_BLENDING_FOR_PRIMARY)
diff --git a/components/viz/service/display/surface_aggregator_unittest.cc b/components/viz/service/display/surface_aggregator_unittest.cc index 7cbe095..5d6a31a6 100644 --- a/components/viz/service/display/surface_aggregator_unittest.cc +++ b/components/viz/service/display/surface_aggregator_unittest.cc
@@ -41,6 +41,7 @@ #include "components/viz/service/surfaces/pending_copy_output_request.h" #include "components/viz/service/surfaces/surface.h" #include "components/viz/service/surfaces/surface_manager.h" +#include "components/viz/test/begin_frame_args_test.h" #include "components/viz/test/compositor_frame_helpers.h" #include "components/viz/test/fake_compositor_frame_sink_client.h" #include "components/viz/test/fake_surface_observer.h" @@ -115,6 +116,8 @@ base::TimeTicks() + base::TimeDelta::FromSeconds(1); }; +} // namespace + class SurfaceAggregatorTest : public testing::Test, public DisplayTimeSource { public: explicit SurfaceAggregatorTest(bool use_damage_rect) @@ -5597,6 +5600,16 @@ gfx::OVERLAY_TRANSFORM_NONE); } + void SendBeginFrame(CompositorFrameSinkSupport* support, uint64_t id) { + BeginFrameArgs args = + CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 0, id); + support->OnBeginFrame(args); + } + + void EnableDocumentTransitions(CompositorFrameSinkSupport* support) { + support->document_transitions_enabled_ = true; + } + protected: ServerSharedBitmapManager shared_bitmap_manager_; FrameSinkManagerImpl manager_; @@ -5604,12 +5617,10 @@ std::unique_ptr<SurfaceAggregator> aggregator_; }; -void SubmitCompositorFrameWithResources( +CompositorFrame BuildCompositorFrameWithResources( const std::vector<ResourceId>& resource_ids, bool valid, - SurfaceId child_id, - CompositorFrameSinkSupport* support, - SurfaceId surface_id) { + SurfaceId child_id) { CompositorFrame frame = MakeEmptyCompositorFrame(); auto pass = CompositorRenderPass::Create(); pass->SetNew(CompositorRenderPassId{1}, gfx::Rect(0, 0, 20, 20), gfx::Rect(), @@ -5653,6 +5664,16 @@ secure_output_only, protected_video_type); } frame.render_pass_list.push_back(std::move(pass)); + return frame; +} + +void SubmitCompositorFrameWithResources( + const std::vector<ResourceId>& resource_ids, + bool valid, + SurfaceId child_id, + CompositorFrameSinkSupport* support, + SurfaceId surface_id) { + auto frame = BuildCompositorFrameWithResources(resource_ids, valid, child_id); support->SubmitCompositorFrame(surface_id.local_surface_id(), std::move(frame)); } @@ -8947,8 +8968,94 @@ } } +TEST_F(SurfaceAggregatorWithResourcesTest, TransitionDirectiveFrameBehind) { + FakeCompositorFrameSinkClient client; + auto support = std::make_unique<CompositorFrameSinkSupport>( + &client, &manager_, kArbitraryRootFrameSinkId, kRootIsRoot); + EnableDocumentTransitions(support.get()); + + LocalSurfaceId local_surface_id(7u, base::UnguessableToken::Create()); + SurfaceId surface_id(support->frame_sink_id(), local_surface_id); + + // Create and submit a 'save' frame. + SendBeginFrame(support.get(), 1); + { + auto frame = BuildCompositorFrameWithResources({}, true, SurfaceId()); + frame.metadata.transition_directives.emplace_back( + 1, CompositorFrameTransitionDirective::Type::kSave, + CompositorFrameTransitionDirective::Effect::kCoverLeft, + base::TimeDelta::FromMilliseconds(500)); + + support->SubmitCompositorFrame(local_surface_id, std::move(frame)); + auto* surface = support->GetLastCreatedSurfaceForTesting(); + ASSERT_TRUE(surface); + surface->GetSurfaceSavedFrameStorage()->CompleteForTesting(); + } + AggregateFrame(surface_id); + + // Create and submit an 'animate' frame. + SendBeginFrame(support.get(), 2); + { + auto frame = BuildCompositorFrameWithResources({}, true, SurfaceId()); + frame.metadata.transition_directives.emplace_back( + 2, CompositorFrameTransitionDirective::Type::kAnimate); + support->SubmitCompositorFrame(local_surface_id, std::move(frame)); + } + AggregateFrame(surface_id); + + // Create and submit a frame with some resources. + SendBeginFrame(support.get(), 3); + { + std::vector<ResourceId> ids = {ResourceId(11), ResourceId(12), + ResourceId(13)}; + SubmitCompositorFrameWithResources(ids, true, SurfaceId(), support.get(), + surface_id); + } + auto frame = AggregateFrame(surface_id); + auto count_textures = [](const AggregatedFrame& frame) { + size_t result = 0; + for (auto& render_pass : frame.render_pass_list) { + for (auto* quad : render_pass->quad_list) { + if (quad->material == DrawQuad::Material::kTextureContent) + ++result; + } + } + return result; + }; + // We should have 4 referenced textures (1 from interpolation and 3 from the + // original frame). + EXPECT_EQ(count_textures(frame), 4u); + + // At this point we will interpolate with the above frame (resources 11, 12, + // 13). + SendBeginFrame(support.get(), 4); + { + std::vector<ResourceId> ids = {ResourceId(15), ResourceId(16), + ResourceId(17)}; + // This will cause an activation which will unref 11, 12, 13. So, the + // activation must also interpolate a new frame. + SubmitCompositorFrameWithResources(ids, true, SurfaceId(), support.get(), + surface_id); + } + // Ensure that the interpolated frame is not using unreffed resources + // (otherwise this would DCHECK). + frame = AggregateFrame(surface_id); + // We should have 4 referenced textures (1 from interpolation and 3 (different + // ones) from the original frame). + EXPECT_EQ(count_textures(frame), 4u); + + ASSERT_EQ(3u, client.returned_resources().size()); + ResourceId returned_ids[3]; + for (size_t i = 0; i < 3; ++i) { + returned_ids[i] = client.returned_resources()[i].id; + } + // We expect that 11, 12, and 13 are now returned. + EXPECT_THAT(returned_ids, + testing::WhenSorted(testing::ElementsAreArray( + {ResourceId(11), ResourceId(12), ResourceId(13)}))); +} + INSTANTIATE_TEST_SUITE_P(, SurfaceAggregatorValidSurfaceWithMergingPassesTest, testing::Bool()); -} // namespace } // namespace viz
diff --git a/components/viz/service/frame_sinks/compositor_frame_sink_support.cc b/components/viz/service/frame_sinks/compositor_frame_sink_support.cc index c2a02f2d..7018272 100644 --- a/components/viz/service/frame_sinks/compositor_frame_sink_support.cc +++ b/components/viz/service/frame_sinks/compositor_frame_sink_support.cc
@@ -161,12 +161,10 @@ bool started_animation = surface_animation_manager_.ProcessTransitionDirectives( transition_directives, surface->GetSurfaceSavedFrameStorage()); - // If processing the new directives caused us to start an animation, then - // interpoate the frame immediately. This is needed since if we wait until - // the next BeginFrame to do the first interpolation, then we maybe have - // already drawn this destination frame. - if (started_animation) - surface_animation_manager_.InterpolateFrame(surface); + + // If we started an animation, then we must need a begin frame for the code + // below to work properly. + DCHECK(!started_animation || surface_animation_manager_.NeedsBeginFrame()); // The above call can cause us to start an animation, meaning we need begin // frames. If that's the case, make sure to update the begin frame @@ -175,6 +173,18 @@ UpdateNeedsBeginFramesInternal(); } + // If surface animation manager needs a frame, then we should interpolate + // here. Note that we also interpolate in OnBeginFrame. The reason for two + // calls is that we might not receive and active a frame from the client in + // time to draw, which is why OnBeginFrame interpolates and damages the + // surface. Here, we only interpolate in case we did receive a new frame. We + // always use the latest frame, because it may have new resources that need to + // be reffed by SurfaceAggregator. If we don't do this, then they will be + // unreffed and cleaned up causing a DCHECK in subsequent frames that do use + // the frame. + if (surface_animation_manager_.NeedsBeginFrame()) + surface_animation_manager_.InterpolateFrame(surface); + if (surface->surface_id() == last_activated_surface_id_) return;
diff --git a/components/viz/service/frame_sinks/compositor_frame_sink_support.h b/components/viz/service/frame_sinks/compositor_frame_sink_support.h index 9acefecb..2fdd13b 100644 --- a/components/viz/service/frame_sinks/compositor_frame_sink_support.h +++ b/components/viz/service/frame_sinks/compositor_frame_sink_support.h
@@ -212,6 +212,7 @@ friend class CompositorFrameSinkSupportTest; friend class DisplayTest; friend class FrameSinkManagerTest; + friend class SurfaceAggregatorWithResourcesTest; // Creates a surface reference from the top-level root to |surface_id|. SurfaceReference MakeTopLevelRootReference(const SurfaceId& surface_id); @@ -369,7 +370,7 @@ std::unique_ptr<power_scheduler::PowerModeVoter> animation_power_mode_voter_; // Represents whether the DocumentTransition feature is enabled. - const bool document_transitions_enabled_; + bool document_transitions_enabled_; base::WeakPtrFactory<CompositorFrameSinkSupport> weak_factory_{this};
diff --git a/components/viz/service/surfaces/surface_saved_frame.cc b/components/viz/service/surfaces/surface_saved_frame.cc index 54fb8d08..a4872013 100644 --- a/components/viz/service/surfaces/surface_saved_frame.cc +++ b/components/viz/service/surfaces/surface_saved_frame.cc
@@ -32,9 +32,7 @@ } bool SurfaceSavedFrame::IsValid() const { - // TODO(crbug.com/1174129): This needs to be updated with software copies as - // well. - return HasTextureResult(); + return result_.has_value(); } void SurfaceSavedFrame::RequestCopyOfOutput(Surface* surface) { @@ -63,45 +61,44 @@ return; auto copy_output_texture = *result->GetTextureResult(); - texture_result_.mailbox = copy_output_texture.mailbox; - texture_result_.sync_token = copy_output_texture.sync_token; - texture_result_.size = result->size(); - texture_result_.release_callback = result->TakeTextureOwnership(); + DCHECK(!result_.has_value()); + result_.emplace(); + result_->mailbox = copy_output_texture.mailbox; + result_->sync_token = copy_output_texture.sync_token; + result_->size = result->size(); + result_->release_callback = result->TakeTextureOwnership(); + result_->is_software = false; } -bool SurfaceSavedFrame::HasTextureResult() const { - return texture_result_.release_callback && !texture_result_.mailbox.IsZero(); -} - -SurfaceSavedFrame::TextureResult SurfaceSavedFrame::TakeTextureResult() { - DCHECK(HasTextureResult()); - // Note that the TextureResult move constructor resets sufficient state in the - // member so that HasTextureResult() returns false afterwards, effectively - // clearing the member variable. - return std::move(texture_result_); +base::Optional<SurfaceSavedFrame::OutputCopyResult> +SurfaceSavedFrame::TakeResult() { + return std::move(result_); } void SurfaceSavedFrame::CompleteSavedFrameForTesting( base::OnceCallback<void(const gpu::SyncToken&, bool)> release_callback) { - texture_result_.mailbox = gpu::Mailbox::GenerateForSharedImage(); - texture_result_.release_callback = + result_.emplace(); + result_->mailbox = gpu::Mailbox::GenerateForSharedImage(); + result_->release_callback = SingleReleaseCallback::Create(std::move(release_callback)); - texture_result_.size = kDefaultTextureSizeForTesting; + result_->size = kDefaultTextureSizeForTesting; + result_->is_software = true; DCHECK(IsValid()); } -SurfaceSavedFrame::TextureResult::TextureResult() = default; -SurfaceSavedFrame::TextureResult::TextureResult(TextureResult&& other) { +SurfaceSavedFrame::OutputCopyResult::OutputCopyResult() = default; +SurfaceSavedFrame::OutputCopyResult::OutputCopyResult( + OutputCopyResult&& other) { *this = std::move(other); } -SurfaceSavedFrame::TextureResult::~TextureResult() { +SurfaceSavedFrame::OutputCopyResult::~OutputCopyResult() { if (release_callback) release_callback->Run(sync_token, /*is_lost=*/false); } -SurfaceSavedFrame::TextureResult& SurfaceSavedFrame::TextureResult::operator=( - TextureResult&& other) { +SurfaceSavedFrame::OutputCopyResult& +SurfaceSavedFrame::OutputCopyResult::operator=(OutputCopyResult&& other) { mailbox = std::move(other.mailbox); other.mailbox = gpu::Mailbox(); @@ -111,6 +108,9 @@ size = std::move(other.size); release_callback = std::move(other.release_callback); + + is_software = other.is_software; + other.is_software = false; return *this; }
diff --git a/components/viz/service/surfaces/surface_saved_frame.h b/components/viz/service/surfaces/surface_saved_frame.h index 4eff862..343e6f7 100644 --- a/components/viz/service/surfaces/surface_saved_frame.h +++ b/components/viz/service/surfaces/surface_saved_frame.h
@@ -9,6 +9,7 @@ #include "base/compiler_specific.h" #include "base/memory/weak_ptr.h" +#include "base/optional.h" #include "base/time/time.h" #include "components/viz/common/frame_sinks/copy_output_result.h" #include "components/viz/common/quads/compositor_frame_transition_directive.h" @@ -24,16 +25,17 @@ using TransitionDirectiveCompleteCallback = base::RepeatingCallback<void(uint32_t)>; - struct TextureResult { - TextureResult(); - TextureResult(TextureResult&& other); - ~TextureResult(); + struct OutputCopyResult { + OutputCopyResult(); + OutputCopyResult(OutputCopyResult&& other); + ~OutputCopyResult(); - TextureResult& operator=(TextureResult&& other); + OutputCopyResult& operator=(OutputCopyResult&& other); gpu::Mailbox mailbox; gpu::SyncToken sync_token; gfx::Size size; + bool is_software = false; std::unique_ptr<SingleReleaseCallback> release_callback; }; @@ -50,10 +52,7 @@ // frame. void RequestCopyOfOutput(Surface* surface); - // Takes the root texture result. - // TODO(crbug.com/1174141): We need to support more than just the root result. - bool HasTextureResult() const; - TextureResult TakeTextureResult() WARN_UNUSED_RESULT; + base::Optional<OutputCopyResult> TakeResult() WARN_UNUSED_RESULT; // For testing functionality that ensures that we have a valid frame. void CompleteSavedFrameForTesting( @@ -65,7 +64,7 @@ CompositorFrameTransitionDirective directive_; TransitionDirectiveCompleteCallback directive_finished_callback_; - TextureResult texture_result_; + base::Optional<OutputCopyResult> result_; base::WeakPtrFactory<SurfaceSavedFrame> weak_factory_{this}; };
diff --git a/components/viz/service/transitions/surface_animation_manager.cc b/components/viz/service/transitions/surface_animation_manager.cc index 40f7c5e..7ec8327 100644 --- a/components/viz/service/transitions/surface_animation_manager.cc +++ b/components/viz/service/transitions/surface_animation_manager.cc
@@ -230,6 +230,12 @@ for (auto& render_pass : active_frame.render_pass_list) { if (render_pass->id > max_id) max_id = render_pass->id; + // TODO(vmpstr): If we are doing an interpolation, we fail requested copy + // requests, since we can't copy them into an interpolated frame below. The + // todo is to change DeepCopy into a function that copies everything and + // moves copy output requests, since we can satisfy the requests on the + // interpolated frame. + render_pass->copy_requests.clear(); interpolated_frame.render_pass_list.emplace_back(render_pass->DeepCopy()); }
diff --git a/components/viz/service/transitions/transferable_resource_tracker.cc b/components/viz/service/transitions/transferable_resource_tracker.cc index 5ef22346..8bca734 100644 --- a/components/viz/service/transitions/transferable_resource_tracker.cc +++ b/components/viz/service/transitions/transferable_resource_tracker.cc
@@ -29,27 +29,32 @@ TransferableResource TransferableResourceTracker::ImportResource( std::unique_ptr<SurfaceSavedFrame> saved_frame) { DCHECK(saved_frame); - DCHECK(saved_frame->IsValid()); - if (saved_frame->HasTextureResult()) - return ImportTextureResult(saved_frame->TakeTextureResult()); + // Since we will be dereferencing this blindly, CHECK that the frame is indeed + // valid. + CHECK(saved_frame->IsValid()); - NOTREACHED(); - return TransferableResource(); -} + SurfaceSavedFrame::OutputCopyResult output_copy = *saved_frame->TakeResult(); -TransferableResource TransferableResourceTracker::ImportTextureResult( - SurfaceSavedFrame::TextureResult texture) { - TransferableResource result = - TransferableResource::MakeGL(texture.mailbox, GL_LINEAR, GL_TEXTURE_2D, - texture.sync_token, texture.size, - /*is_overlay_candidate=*/false); - result.id = GetNextAvailableResourceId(); + TransferableResource resource; + if (output_copy.is_software) { + // TODO(vmpstr): This needs to be updated and tested in software. For + // example, we don't currently have a release callback in software, although + // tests do set one up. + resource = TransferableResource::MakeSoftware(output_copy.mailbox, + output_copy.size, RGBA_8888); + } else { + resource = TransferableResource::MakeGL( + output_copy.mailbox, GL_LINEAR, GL_TEXTURE_2D, output_copy.sync_token, + output_copy.size, + /*is_overlay_candidate=*/false); + } - DCHECK(!base::Contains(managed_resources_, result.id)); + resource.id = GetNextAvailableResourceId(); + DCHECK(!base::Contains(managed_resources_, resource.id)); managed_resources_.emplace( - result.id, - TransferableResourceHolder(result, std::move(texture.release_callback))); - return result; + resource.id, TransferableResourceHolder( + resource, std::move(output_copy.release_callback))); + return resource; } void TransferableResourceTracker::RefResource(ResourceId id) {
diff --git a/components/viz/service/transitions/transferable_resource_tracker.h b/components/viz/service/transitions/transferable_resource_tracker.h index 98743ac..bdf92e1 100644 --- a/components/viz/service/transitions/transferable_resource_tracker.h +++ b/components/viz/service/transitions/transferable_resource_tracker.h
@@ -43,9 +43,6 @@ ResourceId GetNextAvailableResourceId(); - TransferableResource ImportTextureResult( - SurfaceSavedFrame::TextureResult result); - static_assert(std::is_same<decltype(kInvalidResourceId.GetUnsafeValue()), uint32_t>::value, "ResourceId underlying type should be uint32_t");
diff --git a/content/browser/dom_storage/dom_storage_browsertest.cc b/content/browser/dom_storage/dom_storage_browsertest.cc index e70d7d1..e2658bb 100644 --- a/content/browser/dom_storage/dom_storage_browsertest.cc +++ b/content/browser/dom_storage/dom_storage_browsertest.cc
@@ -181,8 +181,7 @@ std::make_unique<storage::FilesystemProxy>( storage::FilesystemProxy::UNRESTRICTED, legacy_local_storage_path)); storage::LegacyDomStorageValuesMap data; - data[base::ASCIIToUTF16("foo")] = - base::NullableString16(base::ASCIIToUTF16("bar"), false); + data[u"foo"] = u"bar"; db.CommitChanges(false, data); EXPECT_TRUE(base::PathExists(db_path)); }
diff --git a/content/browser/media/session/media_session_service_impl_browsertest.cc b/content/browser/media/session/media_session_service_impl_browsertest.cc index 6fcab67..4b19362 100644 --- a/content/browser/media/session/media_session_service_impl_browsertest.cc +++ b/content/browser/media/session/media_session_service_impl_browsertest.cc
@@ -5,6 +5,7 @@ #include "content/browser/media/session/media_session_service_impl.h" #include "base/command_line.h" +#include "base/test/scoped_feature_list.h" #include "build/build_config.h" #include "content/browser/media/session/media_session_impl.h" #include "content/browser/media/session/media_session_player_observer.h" @@ -16,6 +17,7 @@ #include "content/public/test/content_browser_test_utils.h" #include "content/shell/browser/shell.h" #include "media/base/media_content_type.h" +#include "media/base/media_switches.h" #include "services/media_session/public/cpp/test/mock_media_session.h" #include "testing/gtest/include/gtest/gtest.h" @@ -97,6 +99,15 @@ "navigator.mediaSession.metadata = new MediaMetadata({ title: \"foo\" });\n" "navigator.mediaSession.setActionHandler(\"seekforward\", _ => {});"; +char kSetUpWebRTCMediaSessionScript[] = + "navigator.mediaSession.playbackState = \"playing\";\n" + "navigator.mediaSession.metadata = new MediaMetadata({ title: \"foo\" });\n" + "navigator.mediaSession.setMicrophoneActive(true);\n" + "navigator.mediaSession.setCameraActive(true);\n" + "navigator.mediaSession.setActionHandler(\"togglemicrophone\", _ => {});\n" + "navigator.mediaSession.setActionHandler(\"togglecamera\", _ => {});\n" + "navigator.mediaSession.setActionHandler(\"hangup\", _ => {});"; + const int kPlayerId = 0; } // anonymous namespace @@ -236,4 +247,62 @@ EXPECT_EQ(1u, GetService()->actions().size()); } +// Browser tests with the MediaSessionWebRTC feature enabled. +// TODO(steimel): Merge with above tests when the feature is enabled by default. +class MediaSessionServiceImplWebRTCBrowserTest + : public MediaSessionServiceImplBrowserTest { + protected: + void SetUpCommandLine(base::CommandLine* command_line) override { + MediaSessionServiceImplBrowserTest::SetUpCommandLine(command_line); + feature_list_.InitAndEnableFeature(media::kMediaSessionWebRTC); + } + + bool ExecuteScriptToSetUpWebRTCMediaSessionSync() { + bool result = ExecuteScript(shell(), kSetUpWebRTCMediaSessionScript); + media_session::test::MockMediaSessionMojoObserver observer(*GetSession()); + + std::set<media_session::mojom::MediaSessionAction> expected_actions; + expected_actions.insert(media_session::mojom::MediaSessionAction::kPlay); + expected_actions.insert(media_session::mojom::MediaSessionAction::kPause); + expected_actions.insert(media_session::mojom::MediaSessionAction::kStop); + expected_actions.insert( + media_session::mojom::MediaSessionAction::kToggleMicrophone); + expected_actions.insert( + media_session::mojom::MediaSessionAction::kToggleCamera); + expected_actions.insert(media_session::mojom::MediaSessionAction::kHangUp); + + observer.WaitForExpectedActions(expected_actions); + return result; + } + + private: + base::test::ScopedFeatureList feature_list_; +}; + +IN_PROC_BROWSER_TEST_F(MediaSessionServiceImplWebRTCBrowserTest, + MicrophoneAndCameraStatesInitiallyUnknown) { + EXPECT_TRUE(NavigateToURL(shell(), GetTestUrl(".", "title1.html"))); + EnsurePlayer(); + + EXPECT_TRUE(ExecuteScriptToSetUpMediaSessionSync()); + + media_session::test::MockMediaSessionMojoObserver observer(*GetSession()); + observer.WaitForMicrophoneState( + media_session::mojom::MicrophoneState::kUnknown); + observer.WaitForCameraState(media_session::mojom::CameraState::kUnknown); +} + +IN_PROC_BROWSER_TEST_F(MediaSessionServiceImplWebRTCBrowserTest, + MicrophoneAndCameraStatesCanBeSet) { + EXPECT_TRUE(NavigateToURL(shell(), GetTestUrl(".", "title1.html"))); + EnsurePlayer(); + + EXPECT_TRUE(ExecuteScriptToSetUpWebRTCMediaSessionSync()); + + media_session::test::MockMediaSessionMojoObserver observer(*GetSession()); + observer.WaitForMicrophoneState( + media_session::mojom::MicrophoneState::kUnmuted); + observer.WaitForCameraState(media_session::mojom::CameraState::kTurnedOn); +} + } // namespace content
diff --git a/content/browser/renderer_host/render_frame_host_impl.cc b/content/browser/renderer_host/render_frame_host_impl.cc index f2d027d3..3fe6115 100644 --- a/content/browser/renderer_host/render_frame_host_impl.cc +++ b/content/browser/renderer_host/render_frame_host_impl.cc
@@ -1139,6 +1139,7 @@ // Creating a RFH in kActive state implies that it is the RFH for a // newly-created FTN, which should not have committed a real load yet. DCHECK(!frame_tree_node_->has_committed_real_load()); + if (parent_) { SetPolicyContainerHost(parent_->policy_container_host()->Clone()); } else if (frame_tree_node_->opener()) { @@ -1149,6 +1150,14 @@ } else { SetPolicyContainerHost(base::MakeRefCounted<PolicyContainerHost>()); } + + // The initial empty document gets its sandbox flags from either: + // 1. The parent + iframe.sandbox for <iframe>. + // 2. The opener for popup, when "allow-popups-to-escape-sandbox" isn't set. + // + // Both are already computed and stored in the FrameTreeNode. So only a copy + // is needed here. + active_sandbox_flags_ = frame_tree_node_->active_sandbox_flags(); } if (!base::FeatureList::IsEnabled( @@ -9008,7 +9017,13 @@ DCHECK(!navigation_request->IsServedFromBackForwardCache()); ResetPermissionsPolicy(); - active_sandbox_flags_ = params.sandbox_flags; + // There are two type of navigations committing new documents: + // 1. The regular ones. They can change sandbox_flags. + // 2. The synchronous re-navigation to about:blank. A second about:blank + // document commits on top of initial one. No properties are changed, + // including sandbox_flags. + if (navigation_request->IsWaitingToCommit()) + active_sandbox_flags_ = navigation_request->SandboxFlagsToCommit(); permissions_policy_header_ = params.permissions_policy_header; permissions_policy_->SetHeaderPolicy(params.permissions_policy_header); document_policy_ = blink::DocumentPolicy::CreateWithHeaderPolicy({ @@ -9031,16 +9046,6 @@ renderer_reported_scheduler_tracked_features_ = 0; browser_reported_scheduler_tracked_features_ = 0; - // TODO(https://crbug.com/1041376): The sandbox flags computed from the - // browser must match with the ones computed from the renderer process. - // Ultimately, the one from the browser process should supersede the - // renderer one. The browser will just "push" the correct value. - // - // This currently doesn't match for about blank in the WPT test: - // window-open-blank-from-different-initiator-after-slow.html - DCHECK(navigation_request->GetURL().IsAboutBlank() || - params.sandbox_flags == navigation_request->SandboxFlagsToCommit()); - // TODO(https://crbug.com/888079): The origin computed from the browser must // match the one reported from the renderer process. VerifyThatBrowserAndRendererCalculatedOriginsToCommitMatch(navigation_request,
diff --git a/content/browser/renderer_host/render_widget_host_view_android.cc b/content/browser/renderer_host/render_widget_host_view_android.cc index f970514..54ac4f2 100644 --- a/content/browser/renderer_host/render_widget_host_view_android.cc +++ b/content/browser/renderer_host/render_widget_host_view_android.cc
@@ -2369,9 +2369,7 @@ RenderWidgetHostView* view) { DCHECK(!static_cast<RenderWidgetHostViewBase*>(view) ->IsRenderWidgetHostViewChildFrame()); - base::Optional<SkColor> color = view->GetBackgroundColor(); - if (color) - SetBackgroundColor(*color); + CopyBackgroundColorIfPresentFrom(*view); RenderWidgetHostViewAndroid* view_android = static_cast<RenderWidgetHostViewAndroid*>(view);
diff --git a/content/browser/renderer_host/render_widget_host_view_aura.cc b/content/browser/renderer_host/render_widget_host_view_aura.cc index 624f7f5..a989d1c 100644 --- a/content/browser/renderer_host/render_widget_host_view_aura.cc +++ b/content/browser/renderer_host/render_widget_host_view_aura.cc
@@ -2597,9 +2597,7 @@ ->IsRenderWidgetHostViewChildFrame()); RenderWidgetHostViewAura* view_aura = static_cast<RenderWidgetHostViewAura*>(view); - base::Optional<SkColor> color = view_aura->GetBackgroundColor(); - if (color) - SetBackgroundColor(*color); + CopyBackgroundColorIfPresentFrom(*view); DCHECK(delegated_frame_host_) << "Cannot be invoked during destruction."; DCHECK(view_aura->delegated_frame_host_);
diff --git a/content/browser/renderer_host/render_widget_host_view_base.cc b/content/browser/renderer_host/render_widget_host_view_base.cc index fd202aa..cb85ae3 100644 --- a/content/browser/renderer_host/render_widget_host_view_base.cc +++ b/content/browser/renderer_host/render_widget_host_view_base.cc
@@ -314,6 +314,33 @@ return default_background_color_; } +bool RenderWidgetHostViewBase::IsBackgroundColorOpaque() { + base::Optional<SkColor> bg_color = GetBackgroundColor(); + return bg_color ? SkColorGetA(*bg_color) == SK_AlphaOPAQUE : true; +} + +void RenderWidgetHostViewBase::CopyBackgroundColorIfPresentFrom( + const RenderWidgetHostView& other) { + const RenderWidgetHostViewBase& other_base = + static_cast<const RenderWidgetHostViewBase&>(other); + if (!other_base.content_background_color_ && + !other_base.default_background_color_) { + return; + } + if (content_background_color_ == other_base.content_background_color_ && + default_background_color_ == other_base.default_background_color_) { + return; + } + bool was_opaque = IsBackgroundColorOpaque(); + content_background_color_ = other_base.content_background_color_; + default_background_color_ = other_base.default_background_color_; + UpdateBackgroundColor(); + bool opaque = IsBackgroundColorOpaque(); + if (was_opaque != opaque && host()->owner_delegate()) { + host()->owner_delegate()->SetBackgroundOpaque(opaque); + } +} + bool RenderWidgetHostViewBase::IsMouseLocked() { return false; }
diff --git a/content/browser/renderer_host/render_widget_host_view_base.h b/content/browser/renderer_host/render_widget_host_view_base.h index db55b0b..4329bc2c 100644 --- a/content/browser/renderer_host/render_widget_host_view_base.h +++ b/content/browser/renderer_host/render_widget_host_view_base.h
@@ -97,6 +97,8 @@ bool LockKeyboard(base::Optional<base::flat_set<ui::DomCode>> codes) override; void SetBackgroundColor(SkColor color) override; base::Optional<SkColor> GetBackgroundColor() override; + void CopyBackgroundColorIfPresentFrom( + const RenderWidgetHostView& other) override; void UnlockKeyboard() override; bool IsKeyboardLocked() override; base::flat_map<std::string, std::string> GetKeyboardLayoutMap() override; @@ -611,6 +613,10 @@ const gfx::PointF& point, gfx::PointF* transformed_point) const; + // Helper function to return whether the current background color is fully + // opaque. + bool IsBackgroundColorOpaque(); + bool view_stopped_flinging_for_test() const { return view_stopped_flinging_for_test_; }
diff --git a/content/browser/webrtc/resources/OWNERS b/content/browser/webrtc/resources/OWNERS new file mode 100644 index 0000000..da6e6df --- /dev/null +++ b/content/browser/webrtc/resources/OWNERS
@@ -0,0 +1,2 @@ +hbos@chromium.org +philipp.hancke@googlemail.com
diff --git a/content/browser/webrtc/resources/data_series.js b/content/browser/webrtc/resources/data_series.js index 428d0dc..c39b1a9 100644 --- a/content/browser/webrtc/resources/data_series.js +++ b/content/browser/webrtc/resources/data_series.js
@@ -36,8 +36,8 @@ return {}; } - var values = []; - for (var i = 0; i < this.dataPoints_.length; ++i) { + const values = []; + for (let i = 0; i < this.dataPoints_.length; ++i) { values.push(this.dataPoints_[i].value); } return { @@ -52,7 +52,7 @@ * DataPoints are assumed to be received in chronological order. */ addPoint(timeTicks, value) { - var time = new Date(timeTicks); + const time = new Date(timeTicks); this.dataPoints_.push(new DataPoint(time, value)); if (this.dataPoints_.length > MAX_STATS_DATA_POINT_BUFFER_SIZE) { @@ -104,11 +104,11 @@ * Returns the cached |values| in the specified time period. */ getValuesInternal_(startTime, stepSize, count) { - var values = []; - var nextPoint = 0; - var currentValue = 0; - var time = startTime; - for (var i = 0; i < count; ++i) { + const values = []; + let nextPoint = 0; + let currentValue = 0; + let time = startTime; + for (let i = 0; i < count; ++i) { while (nextPoint < this.dataPoints_.length && this.dataPoints_[nextPoint].time < time) { currentValue = this.dataPoints_[nextPoint].value;
diff --git a/content/browser/webrtc/resources/dump_creator.js b/content/browser/webrtc/resources/dump_creator.js index 39dd506..13aec065 100644 --- a/content/browser/webrtc/resources/dump_creator.js +++ b/content/browser/webrtc/resources/dump_creator.js
@@ -5,9 +5,9 @@ import {$} from 'chrome://resources/js/util.m.js'; /** A list of getUserMedia requests. */ -export var userMediaRequests = []; +export const userMediaRequests = []; /** A map from peer connection id to the PeerConnectionRecord. */ -export var peerConnectionDataStore = {}; +export const peerConnectionDataStore = {}; // Also duplicating on window since tests access these from C++. window.userMediaRequests = userMediaRequests; @@ -31,10 +31,10 @@ this.root_.className = 'peer-connection-dump-root'; containerElement.appendChild(this.root_); - var summary = document.createElement('summary'); + const summary = document.createElement('summary'); this.root_.appendChild(summary); summary.textContent = 'Create Dump'; - var content = document.createElement('div'); + const content = document.createElement('div'); this.root_.appendChild(content); content.appendChild($('dump-template').content.cloneNode(true)); @@ -72,7 +72,7 @@ // https://crbug.com/817391 this.root_.getElementsByTagName('input')[1].disabled = !mutable; if (!mutable) { - var label = this.root_.getElementsByTagName('label')[2]; + const label = this.root_.getElementsByTagName('label')[2]; label.style = 'color:red;'; label.textContent = ' WebRTC event logging\'s state was set by a command line flag.'; @@ -85,16 +85,16 @@ * @private */ onDownloadData_() { - var dumpObject = { + const dumpObject = { 'getUserMedia': userMediaRequests, 'PeerConnections': peerConnectionDataStore, 'UserAgent': navigator.userAgent, }; - var textBlob = + const textBlob = new Blob([JSON.stringify(dumpObject, null, 1)], {type: 'octet/stream'}); - var URL = window.URL.createObjectURL(textBlob); + const URL = window.URL.createObjectURL(textBlob); - var anchor = this.root_.getElementsByTagName('a')[0]; + const anchor = this.root_.getElementsByTagName('a')[0]; anchor.href = URL; anchor.download = 'webrtc_internals_dump.txt'; // The default action of the anchor will download the URL. @@ -106,7 +106,7 @@ * @private */ onAudioDebugRecordingsChanged_() { - var enabled = this.root_.getElementsByTagName('input')[0].checked; + const enabled = this.root_.getElementsByTagName('input')[0].checked; if (enabled) { chrome.send('enableAudioDebugRecordings'); } else { @@ -120,7 +120,7 @@ * @private */ onEventLogRecordingsChanged_() { - var enabled = this.root_.getElementsByTagName('input')[1].checked; + const enabled = this.root_.getElementsByTagName('input')[1].checked; if (enabled) { chrome.send('enableEventLogRecordings'); } else {
diff --git a/content/browser/webrtc/resources/peer_connection_update_table.js b/content/browser/webrtc/resources/peer_connection_update_table.js index 2816857..6bd31be 100644 --- a/content/browser/webrtc/resources/peer_connection_update_table.js +++ b/content/browser/webrtc/resources/peer_connection_update_table.js
@@ -71,18 +71,18 @@ * @param {!PeerConnectionUpdateEntry} update The update to add. */ addPeerConnectionUpdate(peerConnectionElement, update) { - var tableElement = this.ensureUpdateContainer_(peerConnectionElement); + const tableElement = this.ensureUpdateContainer_(peerConnectionElement); - var row = document.createElement('tr'); + const row = document.createElement('tr'); tableElement.firstChild.appendChild(row); - var time = new Date(parseFloat(update.time)); + const time = new Date(parseFloat(update.time)); const timeItem = document.createElement('td'); timeItem.textContent = time.toLocaleString(); row.appendChild(timeItem); // map internal event names to spec event names. - var type = { + let type = { onRenegotiationNeeded: 'negotiationneeded', signalingStateChange: 'signalingstatechange', iceGatheringStateChange: 'icegatheringstatechange', @@ -103,7 +103,7 @@ if (update.type === 'onIceCandidate' || update.type === 'addIceCandidate') { // extract ICE candidate type from the field following typ. - var candidateType = update.value.match(/(?: typ )(host|srflx|relay)/); + const candidateType = update.value.match(/(?: typ )(host|srflx|relay)/); if (candidateType) { type += ' (' + candidateType[1] + ')'; } @@ -121,8 +121,8 @@ summary.textContent = type; row.appendChild(summaryItem); - var valueContainer = document.createElement('pre'); - var details = row.cells[1].childNodes[0]; + const valueContainer = document.createElement('pre'); + const details = row.cells[1].childNodes[0]; details.appendChild(valueContainer); // Highlight ICE failures and failure callbacks. @@ -133,7 +133,7 @@ valueContainer.parentElement.classList.add('update-log-failure'); } - var value = update.value; + let {value} = update; // map internal names and values to names and events from the // specification. This is a display change which shall not // change the JSON dump. @@ -211,10 +211,10 @@ * @private */ ensureUpdateContainer_(peerConnectionElement) { - var tableId = peerConnectionElement.id + this.UPDATE_LOG_ID_SUFFIX_; - var tableElement = $(tableId); + const tableId = peerConnectionElement.id + this.UPDATE_LOG_ID_SUFFIX_; + let tableElement = $(tableId); if (!tableElement) { - var tableContainer = document.createElement('div'); + const tableContainer = document.createElement('div'); tableContainer.className = this.UPDATE_LOG_CONTAINER_CLASS_; peerConnectionElement.appendChild(tableContainer);
diff --git a/content/browser/webrtc/resources/ssrc_info_manager.js b/content/browser/webrtc/resources/ssrc_info_manager.js index 4888f40..96bd005 100644 --- a/content/browser/webrtc/resources/ssrc_info_manager.js +++ b/content/browser/webrtc/resources/ssrc_info_manager.js
@@ -25,7 +25,7 @@ // TODO(jiayl): remove the fallback to id once the Libjingle change is rolled // to Chrome. if (report.stats && report.stats.values) { - for (var i = 0; i < report.stats.values.length - 1; i += 2) { + for (let i = 0; i < report.stats.values.length - 1; i += 2) { if (report.stats.values[i] === 'ssrc') { return report.stats.values[i + 1]; } @@ -47,7 +47,7 @@ this.streamInfoContainer_ = {}; /** - * The string separating attibutes in an SDP. + * The string separating attributes in an SDP. * @type {string} * @const * @private @@ -87,28 +87,29 @@ * @param {string} sdp The SDP string. */ addSsrcStreamInfo(sdp) { - var attributes = sdp.split(this.ATTRIBUTE_SEPARATOR_); - for (var i = 0; i < attributes.length; ++i) { + const attributes = sdp.split(this.ATTRIBUTE_SEPARATOR_); + for (let i = 0; i < attributes.length; ++i) { // Check if this is a ssrc attribute. if (attributes[i].indexOf(this.SSRC_ATTRIBUTE_PREFIX_) !== 0) { continue; } - var nextFieldIndex = attributes[i].search(this.FIELD_SEPARATOR_REGEX_); + let nextFieldIndex = attributes[i].search(this.FIELD_SEPARATOR_REGEX_); if (nextFieldIndex === -1) { continue; } - var ssrc = attributes[i].substring( + const ssrc = attributes[i].substring( this.SSRC_ATTRIBUTE_PREFIX_.length, nextFieldIndex); if (!this.streamInfoContainer_[ssrc]) { this.streamInfoContainer_[ssrc] = {}; } // Make |rest| starting at the next field. - var rest = attributes[i].substring(nextFieldIndex + 1); - var name, value; + let rest = attributes[i].substring(nextFieldIndex + 1); + let name; + let value; while (rest.length > 0) { nextFieldIndex = rest.search(this.FIELD_SEPARATOR_REGEX_); if (nextFieldIndex === -1) { @@ -149,8 +150,8 @@ parentElement.className = this.SSRC_INFO_BLOCK_CLASS; - var fieldElement; - for (var property in this.streamInfoContainer_[ssrc]) { + let fieldElement; + for (const property in this.streamInfoContainer_[ssrc]) { fieldElement = document.createElement('div'); parentElement.appendChild(fieldElement); fieldElement.textContent =
diff --git a/content/browser/webrtc/resources/stats_graph_helper.js b/content/browser/webrtc/resources/stats_graph_helper.js index d2c87cc..eca482d 100644 --- a/content/browser/webrtc/resources/stats_graph_helper.js +++ b/content/browser/webrtc/resources/stats_graph_helper.js
@@ -18,15 +18,15 @@ import {GetSsrcFromReport} from './ssrc_info_manager.js'; import {TimelineGraphView} from './timeline_graph_view.js'; -var STATS_GRAPH_CONTAINER_HEADING_CLASS = 'stats-graph-container-heading'; +const STATS_GRAPH_CONTAINER_HEADING_CLASS = 'stats-graph-container-heading'; -var RECEIVED_PROPAGATION_DELTA_LABEL = +const RECEIVED_PROPAGATION_DELTA_LABEL = 'googReceivedPacketGroupPropagationDeltaDebug'; -var RECEIVED_PACKET_GROUP_ARRIVAL_TIME_LABEL = +const RECEIVED_PACKET_GROUP_ARRIVAL_TIME_LABEL = 'googReceivedPacketGroupArrivalTimeDebug'; // Specifies which stats should be drawn on the 'bweCompound' graph and how. -var bweCompoundGraphConfig = { +const bweCompoundGraphConfig = { googAvailableSendBandwidth: {color: 'red'}, googTargetEncBitrateCorrected: {color: 'purple'}, googActualEncBitrate: {color: 'orange'}, @@ -36,11 +36,11 @@ // Converts the last entry of |srcDataSeries| from the total amount to the // amount per second. -var totalToPerSecond = function(srcDataSeries) { - var length = srcDataSeries.dataPoints_.length; +const totalToPerSecond = function(srcDataSeries) { + const length = srcDataSeries.dataPoints_.length; if (length >= 2) { - var lastDataPoint = srcDataSeries.dataPoints_[length - 1]; - var secondLastDataPoint = srcDataSeries.dataPoints_[length - 2]; + const lastDataPoint = srcDataSeries.dataPoints_[length - 1]; + const secondLastDataPoint = srcDataSeries.dataPoints_[length - 2]; return Math.floor( (lastDataPoint.value - secondLastDataPoint.value) * 1000 / (lastDataPoint.time - secondLastDataPoint.time)); @@ -50,7 +50,7 @@ }; // Converts the value of total bytes to bits per second. -var totalBytesToBitsPerSecond = function(srcDataSeries) { +const totalBytesToBitsPerSecond = function(srcDataSeries) { return totalToPerSecond(srcDataSeries) * 8; }; @@ -58,7 +58,7 @@ // |convertedName| is the name of the converted value, |convertFunction| // is the function used to calculate the new converted value based on the // original dataSeries. -var dataConversionConfig = { +const dataConversionConfig = { packetsSent: { convertedName: 'packetsSentPerSecond', convertFunction: totalToPerSecond, @@ -79,9 +79,9 @@ // TODO (jiayl): remove this when the unit bug is fixed. googTargetEncBitrate: { convertedName: 'googTargetEncBitrateCorrected', - convertFunction: function(srcDataSeries) { - var length = srcDataSeries.dataPoints_.length; - var lastDataPoint = srcDataSeries.dataPoints_[length - 1]; + convertFunction(srcDataSeries) { + const length = srcDataSeries.dataPoints_.length; + const lastDataPoint = srcDataSeries.dataPoints_[length - 1]; if (lastDataPoint.value < 5000) { return lastDataPoint.value * 1000; } @@ -93,7 +93,7 @@ // The object contains the stats names that should not be added to the graph, // even if they are numbers. -var statsNameBlackList = { +const statsNameBlackList = { 'ssrc': true, 'googTrackId': true, 'googComponent': true, @@ -128,7 +128,7 @@ } function readReportStat(report, stat) { - let values = report.stats.values; + const values = report.stats.values; for (let i = 0; i < values.length; i += 2) { if (values[i] === stat) { return values[i + 1]; @@ -150,10 +150,10 @@ return false; } -var graphViews = {}; +const graphViews = {}; // Export on |window| since tests access this directly from C++. window.graphViews = graphViews; -let graphElementsByPeerConnectionId = new Map(); +const graphElementsByPeerConnectionId = new Map(); // Returns number parsed from |value|, or NaN if the stats name is black-listed. function getNumberFromValue(name, value) { @@ -170,9 +170,9 @@ // |peerConnectionElement|. export function drawSingleReport( peerConnectionElement, report, isLegacyReport) { - var reportType = report.type; - var reportId = report.id; - var stats = report.stats; + const reportType = report.type; + const reportId = report.id; + const stats = report.stats; if (!stats || !stats.values) { return; } @@ -181,16 +181,16 @@ Array.from(peerConnectionElement.childNodes) : []; - for (var i = 0; i < stats.values.length - 1; i = i + 2) { - var rawLabel = stats.values[i]; + for (let i = 0; i < stats.values.length - 1; i = i + 2) { + const rawLabel = stats.values[i]; // Propagation deltas are handled separately. if (rawLabel === RECEIVED_PROPAGATION_DELTA_LABEL) { drawReceivedPropagationDelta( peerConnectionElement, report, stats.values[i + 1]); continue; } - var rawDataSeriesId = reportId + '-' + rawLabel; - var rawValue = getNumberFromValue(rawLabel, stats.values[i + 1]); + const rawDataSeriesId = reportId + '-' + rawLabel; + const rawValue = getNumberFromValue(rawLabel, stats.values[i + 1]); if (isNaN(rawValue)) { // We do not draw non-numerical values, but still want to record it in the // data series. @@ -199,9 +199,9 @@ [stats.values[i + 1]]); continue; } - var finalDataSeriesId = rawDataSeriesId; - var finalLabel = rawLabel; - var finalValue = rawValue; + let finalDataSeriesId = rawDataSeriesId; + let finalLabel = rawLabel; + let finalValue = rawValue; // We need to convert the value if dataConversionConfig[rawLabel] exists. if (isLegacyReport && dataConversionConfig[rawLabel]) { // Updates the original dataSeries before the conversion. @@ -232,20 +232,20 @@ } // Updates the graph. - var graphType = + const graphType = bweCompoundGraphConfig[finalLabel] ? 'bweCompound' : finalLabel; - var graphViewId = + const graphViewId = peerConnectionElement.id + '-' + reportId + '-' + graphType; if (!graphViews[graphViewId]) { graphViews[graphViewId] = createStatsGraphView(peerConnectionElement, report, graphType); - var date = new Date(stats.timestamp); + const date = new Date(stats.timestamp); graphViews[graphViewId].setDateRange(date, date); } // Adds the new dataSeries to the graphView. We have to do it here to cover // both the simple and compound graph cases. - var dataSeries = + const dataSeries = peerConnectionDataStore[peerConnectionElement.id].getDataSeries( finalDataSeriesId); if (!graphViews[graphViewId].hasDataSeries(dataSeries)) { @@ -292,7 +292,7 @@ // each data point, and |values| is the list of the data point values. function addDataSeriesPoints( peerConnectionElement, dataSeriesId, label, times, values) { - var dataSeries = + let dataSeries = peerConnectionDataStore[peerConnectionElement.id].getDataSeries( dataSeriesId); if (!dataSeries) { @@ -303,7 +303,7 @@ dataSeries.setColor(bweCompoundGraphConfig[label].color); } } - for (var i = 0; i < times.length; ++i) { + for (let i = 0; i < times.length; ++i) { dataSeries.addPoint(times[i], values[i]); } } @@ -313,11 +313,11 @@ // ['googReceivedPacketGroupArrivalTimeDebug', '[123456, 234455, 344566]', // 'googReceivedPacketGroupPropagationDeltaDebug', '[23, 45, 56]', ...]. function drawReceivedPropagationDelta(peerConnectionElement, report, deltas) { - var reportId = report.id; - var stats = report.stats; - var times = null; + const reportId = report.id; + const stats = report.stats; + let times = null; // Find the packet group arrival times. - for (var i = 0; i < stats.values.length - 1; i = i + 2) { + for (let i = 0; i < stats.values.length - 1; i = i + 2) { if (stats.values[i] === RECEIVED_PACKET_GROUP_ARRIVAL_TIME_LABEL) { times = stats.values[i + 1]; break; @@ -338,20 +338,20 @@ } // Update the data series. - var dataSeriesId = reportId + '-' + RECEIVED_PROPAGATION_DELTA_LABEL; + const dataSeriesId = reportId + '-' + RECEIVED_PROPAGATION_DELTA_LABEL; addDataSeriesPoints( peerConnectionElement, dataSeriesId, RECEIVED_PROPAGATION_DELTA_LABEL, times, deltas); // Update the graph. - var graphViewId = peerConnectionElement.id + '-' + reportId + '-' + + const graphViewId = peerConnectionElement.id + '-' + reportId + '-' + RECEIVED_PROPAGATION_DELTA_LABEL; - var date = new Date(times[times.length - 1]); + const date = new Date(times[times.length - 1]); if (!graphViews[graphViewId]) { graphViews[graphViewId] = createStatsGraphView( peerConnectionElement, report, RECEIVED_PROPAGATION_DELTA_LABEL); graphViews[graphViewId].setScale(10); graphViews[graphViewId].setDateRange(date, date); - var dataSeries = + const dataSeries = peerConnectionDataStore[peerConnectionElement.id].getDataSeries( dataSeriesId); graphViews[graphViewId].addDataSeries(dataSeries); @@ -386,9 +386,9 @@ // Ensures a div container to hold all stats graphs for one track is created as // a child of |peerConnectionElement|. function ensureStatsGraphTopContainer(peerConnectionElement, report) { - var containerId = peerConnectionElement.id + '-' + report.type + '-' + + const containerId = peerConnectionElement.id + '-' + report.type + '-' + report.id + '-graph-container'; - var container = $(containerId); + let container = $(containerId); if (!container) { container = document.createElement('details'); container.id = containerId; @@ -400,13 +400,13 @@ STATS_GRAPH_CONTAINER_HEADING_CLASS; container.firstChild.firstChild.textContent = 'Stats graphs for ' + report.id + ' (' + report.type + ')'; - var statsType = getSsrcReportType(report); + const statsType = getSsrcReportType(report); if (statsType !== '') { container.firstChild.firstChild.textContent += ' (' + statsType + ')'; } if (report.type === 'ssrc') { - var ssrcInfoElement = document.createElement('div'); + const ssrcInfoElement = document.createElement('div'); container.firstChild.appendChild(ssrcInfoElement); ssrcInfoManager.populateSsrcInfo( ssrcInfoElement, GetSsrcFromReport(report)); @@ -418,14 +418,14 @@ // Creates the container elements holding a timeline graph // and the TimelineGraphView object. function createStatsGraphView(peerConnectionElement, report, statsName) { - var topContainer = + const topContainer = ensureStatsGraphTopContainer(peerConnectionElement, report); - var graphViewId = + const graphViewId = peerConnectionElement.id + '-' + report.id + '-' + statsName; - var divId = graphViewId + '-div'; - var canvasId = graphViewId + '-canvas'; - var container = document.createElement('div'); + const divId = graphViewId + '-div'; + const canvasId = graphViewId + '-canvas'; + const container = document.createElement('div'); container.className = 'stats-graph-sub-container'; topContainer.appendChild(container); @@ -444,9 +444,9 @@ // Creates the legend section for the bweCompound graph. // Returns the legend element. function createBweCompoundLegend(peerConnectionElement, reportId) { - var legend = document.createElement('div'); - for (var prop in bweCompoundGraphConfig) { - var div = document.createElement('div'); + const legend = document.createElement('div'); + for (const prop in bweCompoundGraphConfig) { + const div = document.createElement('div'); legend.appendChild(div); div.appendChild($('checkbox-template').content.cloneNode(true)); div.appendChild(document.createTextNode(prop)); @@ -454,8 +454,8 @@ div.dataSeriesId = reportId + '-' + prop; div.graphViewId = peerConnectionElement.id + '-' + reportId + '-bweCompound'; - div.firstChild.addEventListener('click', function(event) { - var target = + div.firstChild.addEventListener('click', event => { + const target = peerConnectionDataStore[peerConnectionElement.id].getDataSeries( event.target.parentNode.dataSeriesId); target.show(event.target.checked);
diff --git a/content/browser/webrtc/resources/stats_rates_calculator.js b/content/browser/webrtc/resources/stats_rates_calculator.js index ccebdc2..c4de59d 100644 --- a/content/browser/webrtc/resources/stats_rates_calculator.js +++ b/content/browser/webrtc/resources/stats_rates_calculator.js
@@ -52,7 +52,7 @@ // Gets the calculated metrics associated with |originalName| in the order // that they were added, or an empty list if there are no associated metrics. getCalculatedMetrics(originalName) { - let calculatedMetrics = + const calculatedMetrics = this.calculatedMetricsByOriginalName.get(originalName); if (!calculatedMetrics) { return []; @@ -62,7 +62,7 @@ toString() { let str = '{id:"' + this.id + '"'; - for (let originalName of this.calculatedMetricsByOriginalName.keys()) { + for (const originalName of this.calculatedMetricsByOriginalName.keys()) { const calculatedMetrics = this.calculatedMetricsByOriginalName.get(originalName); str += ',' + originalName + ':['; @@ -125,7 +125,7 @@ toInternalsReportList() { const result = []; - for (let stats of this.statsById.values()) { + for (const stats of this.statsById.values()) { const internalReport = { id: stats.id, type: stats.type, @@ -158,14 +158,14 @@ toString() { let str = ''; - for (let stats of this.statsById.values()) { + for (const stats of this.statsById.values()) { if (str !== '') { str += ','; } str += JSON.stringify(stats); } let str2 = ''; - for (let stats of this.calculatedStatsById.values()) { + for (const stats of this.calculatedStatsById.values()) { if (str2 !== '') { str2 += ','; } @@ -180,7 +180,7 @@ getByType(type) { const result = []; - for (let stats of this.statsById.values()) { + for (const stats of this.statsById.values()) { if (stats.type === type) { result.push(stats); }
diff --git a/content/browser/webrtc/resources/stats_table.js b/content/browser/webrtc/resources/stats_table.js index af96e8e..67f5919 100644 --- a/content/browser/webrtc/resources/stats_table.js +++ b/content/browser/webrtc/resources/stats_table.js
@@ -35,7 +35,7 @@ if (report.type === 'codec') { return; } - var statsTable = this.ensureStatsTable_(peerConnectionElement, report); + const statsTable = this.ensureStatsTable_(peerConnectionElement, report); if (report.stats) { this.addStatsToTable_( @@ -44,8 +44,8 @@ } clearStatsLists(peerConnectionElement) { - let containerId = peerConnectionElement.id + '-table-container'; - let container = $(containerId); + const containerId = peerConnectionElement.id + '-table-container'; + const container = $(containerId); if (container) { peerConnectionElement.removeChild(container); this.ensureStatsTableContainer_(peerConnectionElement); @@ -61,13 +61,13 @@ * @private */ ensureStatsTableContainer_(peerConnectionElement) { - var containerId = peerConnectionElement.id + '-table-container'; - var container = $(containerId); + const containerId = peerConnectionElement.id + '-table-container'; + let container = $(containerId); if (!container) { container = document.createElement('div'); container.id = containerId; container.className = 'stats-table-container'; - var head = document.createElement('div'); + const head = document.createElement('div'); head.textContent = 'Stats Tables'; container.appendChild(head); peerConnectionElement.appendChild(container); @@ -88,14 +88,14 @@ * @private */ ensureStatsTable_(peerConnectionElement, report) { - var tableId = peerConnectionElement.id + '-table-' + report.id; - var table = $(tableId); + const tableId = peerConnectionElement.id + '-table-' + report.id; + let table = $(tableId); if (!table) { - var container = this.ensureStatsTableContainer_(peerConnectionElement); - var details = document.createElement('details'); + const container = this.ensureStatsTableContainer_(peerConnectionElement); + const details = document.createElement('details'); container.appendChild(details); - var summary = document.createElement('summary'); + const summary = document.createElement('summary'); summary.textContent = report.id + ' (' + report.type + ')'; details.appendChild(summary); @@ -126,9 +126,9 @@ * @private */ addStatsToTable_(statsTable, time, statsData) { - var date = new Date(time); + const date = new Date(time); this.updateStatsTableRow_(statsTable, 'timestamp', date.toLocaleString()); - for (var i = 0; i < statsData.length - 1; i = i + 2) { + for (let i = 0; i < statsData.length - 1; i = i + 2) { this.updateStatsTableRow_(statsTable, statsData[i], statsData[i + 1]); } } @@ -143,9 +143,9 @@ * @private */ updateStatsTableRow_(statsTable, rowName, value) { - var trId = statsTable.id + '-' + rowName; - var trElement = $(trId); - var activeConnectionClass = 'stats-table-active-connection'; + const trId = statsTable.id + '-' + rowName; + let trElement = $(trId); + const activeConnectionClass = 'stats-table-active-connection'; if (!trElement) { trElement = document.createElement('tr'); trElement.id = trId;
diff --git a/content/browser/webrtc/resources/tab_view.js b/content/browser/webrtc/resources/tab_view.js index 6edcd7d..85cb3a5 100644 --- a/content/browser/webrtc/resources/tab_view.js +++ b/content/browser/webrtc/resources/tab_view.js
@@ -49,14 +49,14 @@ throw 'Tab already exists: ' + id; } - var head = document.createElement('span'); + const head = document.createElement('span'); head.className = this.TAB_HEAD_CLASS_; head.textContent = title; head.title = title; this.headBar_.appendChild(head); head.addEventListener('click', this.switchTab_.bind(this, id)); - var body = document.createElement('div'); + const body = document.createElement('div'); body.className = this.TAB_BODY_CLASS_; body.id = id; this.root_.appendChild(body); @@ -114,4 +114,4 @@ this.root_.appendChild(this.headBar_); this.headBar_.style.textAlign = 'center'; } -}; +}
diff --git a/content/browser/webrtc/resources/timeline_graph_view.js b/content/browser/webrtc/resources/timeline_graph_view.js index 9a82774..bb89b43 100644 --- a/content/browser/webrtc/resources/timeline_graph_view.js +++ b/content/browser/webrtc/resources/timeline_graph_view.js
@@ -5,26 +5,26 @@ import {$} from 'chrome://resources/js/util.m.js'; // Maximum number of labels placed vertically along the sides of the graph. -var MAX_VERTICAL_LABELS = 6; +const MAX_VERTICAL_LABELS = 6; // Vertical spacing between labels and between the graph and labels. -var LABEL_VERTICAL_SPACING = 4; +const LABEL_VERTICAL_SPACING = 4; // Horizontal spacing between vertically placed labels and the edges of the // graph. -var LABEL_HORIZONTAL_SPACING = 3; +const LABEL_HORIZONTAL_SPACING = 3; // Horizintal spacing between two horitonally placed labels along the bottom // of the graph. -var LABEL_LABEL_HORIZONTAL_SPACING = 25; +const LABEL_LABEL_HORIZONTAL_SPACING = 25; // Length of ticks, in pixels, next to y-axis labels. The x-axis only has // one set of labels, so it can use lines instead. -var Y_AXIS_TICK_LENGTH = 10; +const Y_AXIS_TICK_LENGTH = 10; -var GRID_COLOR = '#CCC'; -var TEXT_COLOR = '#000'; -var BACKGROUND_COLOR = '#FFF'; +const GRID_COLOR = '#CCC'; +const TEXT_COLOR = '#000'; +const BACKGROUND_COLOR = '#FFF'; -var MAX_DECIMAL_PRECISION = 3; +const MAX_DECIMAL_PRECISION = 3; /** * A TimelineGraphView displays a timeline graph on a canvas element. @@ -59,7 +59,7 @@ // Returns the total length of the graph, in pixels. getLength_() { - var timeRange = this.endTime_ - this.startTime_; + const timeRange = this.endTime_ - this.startTime_; // Math.floor is used to ignore the last partial area, of length less // than this.scale_. return Math.floor(timeRange / this.scale_); @@ -78,7 +78,7 @@ * repaint. */ updateScrollbarRange_(resetPosition) { - var scrollbarRange = this.getLength_() - this.canvas_.width; + let scrollbarRange = this.getLength_() - this.canvas_.width; if (scrollbarRange < 0) { scrollbarRange = 0; } @@ -133,7 +133,7 @@ setDataSeries(dataSeries) { // Simply recreates the Graph. this.graph_ = new Graph(); - for (var i = 0; i < dataSeries.length; ++i) { + for (let i = 0; i < dataSeries.length; ++i) { this.graph_.addDataSeries(dataSeries[i]); } this.repaint(); @@ -160,17 +160,17 @@ this.repaintTimerRunning_ = false; - var width = this.canvas_.width; - var height = this.canvas_.height; - var context = this.canvas_.getContext('2d'); + const width = this.canvas_.width; + let height = this.canvas_.height; + const context = this.canvas_.getContext('2d'); // Clear the canvas. context.fillStyle = BACKGROUND_COLOR; context.fillRect(0, 0, width, height); // Try to get font height in pixels. Needed for layout. - var fontHeightString = context.font.match(/([0-9]+)px/)[1]; - var fontHeight = parseInt(fontHeightString); + const fontHeightString = context.font.match(/([0-9]+)px/)[1]; + const fontHeight = parseInt(fontHeightString); // Safety check, to avoid drawing anything too ugly. if (fontHeightString.length === 0 || fontHeight <= 0 || @@ -187,17 +187,17 @@ context.translate(0.5, 0.5); // Figure out what time values to display. - var position = this.scrollbar_.position_; + let position = this.scrollbar_.position_; // If the entire time range is being displayed, align the right edge of // the graph to the end of the time range. if (this.scrollbar_.range_ === 0) { position = this.getLength_() - this.canvas_.width; } - var visibleStartTime = this.startTime_ + position * this.scale_; + const visibleStartTime = this.startTime_ + position * this.scale_; // Make space at the bottom of the graph for the time labels, and then // draw the labels. - var textHeight = height; + const textHeight = height; height -= fontHeight + LABEL_VERTICAL_SPACING; this.drawTimeLabels(context, width, height, textHeight, visibleStartTime); @@ -227,11 +227,11 @@ */ drawTimeLabels(context, width, height, textHeight, startTime) { // Draw the labels 1 minute apart. - var timeStep = 1000 * 60; + const timeStep = 1000 * 60; // Find the time for the first label. This time is a perfect multiple of // timeStep because of how UTC times work. - var time = Math.ceil(startTime / timeStep) * timeStep; + let time = Math.ceil(startTime / timeStep) * timeStep; context.textBaseline = 'bottom'; context.textAlign = 'center'; @@ -240,11 +240,11 @@ // Draw labels and vertical grid lines. while (true) { - var x = Math.round((time - startTime) / this.scale_); + const x = Math.round((time - startTime) / this.scale_); if (x >= width) { break; } - var text = (new Date(time)).toLocaleTimeString(); + const text = (new Date(time)).toLocaleTimeString(); context.fillText(text, x, textHeight); context.beginPath(); context.lineTo(x, 0); @@ -310,7 +310,7 @@ } hasDataSeries(dataSeries) { - for (var i = 0; i < this.dataSeries_.length; ++i) { + for (let i = 0; i < this.dataSeries_.length; ++i) { if (this.dataSeries_[i] === dataSeries) { return true; } @@ -342,13 +342,14 @@ this.scale_ = scale; // Find largest value. - var max = 0, min = 0; - for (var i = 0; i < this.dataSeries_.length; ++i) { - var values = this.getValues(this.dataSeries_[i]); + let max = 0; + let min = 0; + for (let i = 0; i < this.dataSeries_.length; ++i) { + const values = this.getValues(this.dataSeries_[i]); if (!values) { continue; } - for (var j = 0; j < values.length; ++j) { + for (let j = 0; j < values.length; ++j) { if (values[j] > max) { max = values[j]; } else if (values[j] < min) { @@ -373,10 +374,10 @@ } // Find appropriate units to use. - var units = ['', 'k', 'M', 'G', 'T', 'P']; + const units = ['', 'k', 'M', 'G', 'T', 'P']; // Units to use for labels. 0 is '1', 1 is K, etc. // We start with 1, and work our way up. - var unit = 1; + let unit = 1; minValue /= 1024; maxValue /= 1024; while (units[unit + 1] && maxValue - minValue >= 1024) { @@ -389,7 +390,7 @@ this.layoutLabelsBasic_(minValue, maxValue, MAX_DECIMAL_PRECISION); // Append units to labels. - for (var i = 0; i < this.labels_.length; ++i) { + for (let i = 0; i < this.labels_.length; ++i) { this.labels_[i] += ' ' + units[unit]; } @@ -405,7 +406,7 @@ */ layoutLabelsBasic_(minValue, maxValue, maxDecimalDigits) { this.labels_ = []; - var range = maxValue - minValue; + const range = maxValue - minValue; // No labels if the range is 0. if (range === 0) { this.min_ = this.max_ = maxValue; @@ -415,10 +416,10 @@ // The maximum number of equally spaced labels allowed. |fontHeight_| // is doubled because the top two labels are both drawn in the same // gap. - var minLabelSpacing = 2 * this.fontHeight_ + LABEL_VERTICAL_SPACING; + const minLabelSpacing = 2 * this.fontHeight_ + LABEL_VERTICAL_SPACING; // The + 1 is for the top label. - var maxLabels = 1 + this.height_ / minLabelSpacing; + let maxLabels = 1 + this.height_ / minLabelSpacing; if (maxLabels < 2) { maxLabels = 2; } else if (maxLabels > MAX_VERTICAL_LABELS) { @@ -426,10 +427,10 @@ } // Initial try for step size between consecutive labels. - var stepSize = Math.pow(10, -maxDecimalDigits); + let stepSize = Math.pow(10, -maxDecimalDigits); // Number of digits to the right of the decimal of |stepSize|. // Used for formatting label strings. - var stepSizeDecimalDigits = maxDecimalDigits; + let stepSizeDecimalDigits = maxDecimalDigits; // Pick a reasonable step size. while (true) { @@ -465,7 +466,7 @@ this.min_ = Math.floor(minValue / stepSize) * stepSize; // Create labels. - for (var label = this.max_; label >= this.min_; label -= stepSize) { + for (let label = this.max_; label >= this.min_; label -= stepSize) { this.labels_.push(label.toFixed(stepSizeDecimalDigits)); } } @@ -474,17 +475,15 @@ * Draws tick marks for each of the labels in |labels_|. */ drawTicks(context) { - var x1; - var x2; - x1 = this.width_ - 1; - x2 = this.width_ - 1 - Y_AXIS_TICK_LENGTH; + const x1 = this.width_ - 1; + const x2 = this.width_ - 1 - Y_AXIS_TICK_LENGTH; context.fillStyle = GRID_COLOR; context.beginPath(); - for (var i = 1; i < this.labels_.length - 1; ++i) { + for (let i = 1; i < this.labels_.length - 1; ++i) { // The rounding is needed to avoid ugly 2-pixel wide anti-aliased // lines. - var y = Math.round(this.height_ * i / (this.labels_.length - 1)); + const y = Math.round(this.height_ * i / (this.labels_.length - 1)); context.moveTo(x1, y); context.lineTo(x2, y); } @@ -497,22 +496,22 @@ drawLines(context) { // Factor by which to scale all values to convert them to a number from // 0 to height - 1. - var scale = 0; - var bottom = this.height_ - 1; + let scale = 0; + const bottom = this.height_ - 1; if (this.max_) { scale = bottom / (this.max_ - this.min_); } // Draw in reverse order, so earlier data series are drawn on top of // subsequent ones. - for (var i = this.dataSeries_.length - 1; i >= 0; --i) { - var values = this.getValues(this.dataSeries_[i]); + for (let i = this.dataSeries_.length - 1; i >= 0; --i) { + const values = this.getValues(this.dataSeries_[i]); if (!values) { continue; } context.strokeStyle = this.dataSeries_[i].getColor(); context.beginPath(); - for (var x = 0; x < values.length; ++x) { + for (let x = 0; x < values.length; ++x) { // The rounding is needed to avoid ugly 2-pixel wide anti-aliased // horizontal lines. context.lineTo(x, bottom - Math.round((values[x] - this.min_) * scale)); @@ -528,7 +527,7 @@ if (this.labels_.length === 0) { return; } - var x = this.width_ - LABEL_HORIZONTAL_SPACING; + const x = this.width_ - LABEL_HORIZONTAL_SPACING; // Set up the context. context.fillStyle = TEXT_COLOR; @@ -541,8 +540,8 @@ // Draw all the other labels. context.textBaseline = 'bottom'; - var step = (this.height_ - 1) / (this.labels_.length - 1); - for (var i = 1; i < this.labels_.length; ++i) { + const step = (this.height_ - 1) / (this.labels_.length - 1); + for (let i = 1; i < this.labels_.length; ++i) { context.fillText(this.labels_[i], x, step * i); } }
diff --git a/content/browser/webrtc/resources/webrtc_internals.js b/content/browser/webrtc/resources/webrtc_internals.js index 29db9906..d13d820f 100644 --- a/content/browser/webrtc/resources/webrtc_internals.js +++ b/content/browser/webrtc/resources/webrtc_internals.js
@@ -14,18 +14,18 @@ import {StatsTable} from './stats_table.js'; import {TabView} from './tab_view.js'; -var USER_MEDIA_TAB_ID = 'user-media-tab-id'; +const USER_MEDIA_TAB_ID = 'user-media-tab-id'; const OPTION_GETSTATS_STANDARD = 'Standardized (promise-based) getStats() API'; const OPTION_GETSTATS_LEGACY = 'Legacy Non-Standard (callback-based) getStats() API'; let currentGetStatsMethod = OPTION_GETSTATS_STANDARD; -var tabView = null; -var ssrcInfoManager = null; -var peerConnectionUpdateTable = null; -var statsTable = null; -var dumpCreator = null; +let tabView = null; +let ssrcInfoManager = null; +let peerConnectionUpdateTable = null; +let statsTable = null; +let dumpCreator = null; // Exporting these on window since they are directly accessed by tests. window.setCurrentGetStatsMethod = function(method) { @@ -92,7 +92,7 @@ * "value". */ addUpdate(update) { - var time = new Date(parseFloat(update.time)); + const time = new Date(parseFloat(update.time)); this.record_.updateLog.push({ time: time.toLocaleString(), type: update.type, @@ -181,9 +181,9 @@ } function requestStats() { - if (currentGetStatsMethod == OPTION_GETSTATS_STANDARD) { + if (currentGetStatsMethod === OPTION_GETSTATS_STANDARD) { requestStandardStats(); - } else if (currentGetStatsMethod == OPTION_GETSTATS_LEGACY) { + } else if (currentGetStatsMethod === OPTION_GETSTATS_LEGACY) { requestLegacyStats(); } } @@ -238,8 +238,8 @@ * @param {!PeerConnectionUpdateEntry} data The peer connection update data. */ function extractSsrcInfo(data) { - if (data.type == 'setLocalDescription' || - data.type == 'setRemoteDescription') { + if (data.type === 'setLocalDescription' || + data.type === 'setRemoteDescription') { ssrcInfoManager.addSsrcStreamInfo(data.value); } } @@ -254,7 +254,7 @@ * @return {!Element} the new DIV element. */ function appendChildWithText(parent, tag, text) { - var child = document.createElement(tag); + const child = document.createElement(tag); child.textContent = text; parent.appendChild(child); return child; @@ -284,7 +284,7 @@ * connection. */ function removePeerConnection(data) { - var element = $(getPeerConnectionId(data)); + const element = $(getPeerConnectionId(data)); if (element) { delete peerConnectionDataStore[element.id]; tabView.removeTab(element.id); @@ -299,7 +299,7 @@ * rtcConfiguration, and constraints of a peer connection. */ function addPeerConnection(data) { - var id = getPeerConnectionId(data); + const id = getPeerConnectionId(data); if (!peerConnectionDataStore[id]) { peerConnectionDataStore[id] = new PeerConnectionRecord(); @@ -307,12 +307,12 @@ peerConnectionDataStore[id].initialize( data.url, data.rtcConfiguration, data.constraints); - var peerConnectionElement = $(id); + let peerConnectionElement = $(id); if (!peerConnectionElement) { peerConnectionElement = tabView.addTab(id, data.url + ' [' + id + ']'); } - var p = document.createElement('p'); + const p = document.createElement('p'); p.textContent = data.url + ', ' + data.rtcConfiguration + ', ' + data.constraints; peerConnectionElement.appendChild(p); @@ -327,7 +327,7 @@ * @param {!PeerConnectionUpdateEntry} data The peer connection update data. */ function updatePeerConnection(data) { - var peerConnectionElement = $(getPeerConnectionId(data)); + const peerConnectionElement = $(getPeerConnectionId(data)); addPeerConnectionUpdate(peerConnectionElement, data); } @@ -340,14 +340,14 @@ * constraints, and an array of updates as the log. */ function updateAllPeerConnections(data) { - for (var i = 0; i < data.length; ++i) { - var peerConnection = addPeerConnection(data[i]); + for (let i = 0; i < data.length; ++i) { + const peerConnection = addPeerConnection(data[i]); - var log = data[i].log; + const log = data[i].log; if (!log) { continue; } - for (var j = 0; j < log.length; ++j) { + for (let j = 0; j < log.length; ++j) { addPeerConnectionUpdate(peerConnection, log[j]); } } @@ -367,7 +367,7 @@ if (currentGetStatsMethod != OPTION_GETSTATS_STANDARD) { return; // Obsolete! } - var peerConnectionElement = $(getPeerConnectionId(data)); + const peerConnectionElement = $(getPeerConnectionId(data)); if (!peerConnectionElement) { return; } @@ -380,8 +380,8 @@ const r = StatsReport.fromInternalsReportList(data.reports); statsRatesCalculator.addStatsReport(r); data.reports = statsRatesCalculator.currentReport.toInternalsReportList(); - for (var i = 0; i < data.reports.length; ++i) { - var report = data.reports[i]; + for (let i = 0; i < data.reports.length; ++i) { + const report = data.reports[i]; statsTable.addStatsReport(peerConnectionElement, report); drawSingleReport(peerConnectionElement, report, false); } @@ -400,13 +400,13 @@ if (currentGetStatsMethod != OPTION_GETSTATS_LEGACY) { return; // Obsolete! } - var peerConnectionElement = $(getPeerConnectionId(data)); + const peerConnectionElement = $(getPeerConnectionId(data)); if (!peerConnectionElement) { return; } - for (var i = 0; i < data.reports.length; ++i) { - var report = data.reports[i]; + for (let i = 0; i < data.reports.length; ++i) { + const report = data.reports[i]; statsTable.addStatsReport(peerConnectionElement, report); drawSingleReport(peerConnectionElement, report, true); } @@ -426,7 +426,7 @@ tabView.addTab(USER_MEDIA_TAB_ID, 'GetUserMedia Requests'); } - var requestDiv = document.createElement('div'); + const requestDiv = document.createElement('div'); requestDiv.className = 'user-media-request-div-class'; requestDiv.rid = data.rid; $(USER_MEDIA_TAB_ID).appendChild(requestDiv); @@ -450,19 +450,19 @@ * @param {!Object} data The object containing rid {number}, the render id. */ function removeGetUserMediaForRenderer(data) { - for (var i = userMediaRequests.length - 1; i >= 0; --i) { - if (userMediaRequests[i].rid == data.rid) { + for (let i = userMediaRequests.length - 1; i >= 0; --i) { + if (userMediaRequests[i].rid === data.rid) { userMediaRequests.splice(i, 1); } } - var requests = $(USER_MEDIA_TAB_ID).childNodes; - for (var i = 0; i < requests.length; ++i) { - if (requests[i].rid == data.rid) { + const requests = $(USER_MEDIA_TAB_ID).childNodes; + for (let i = 0; i < requests.length; ++i) { + if (requests[i].rid === data.rid) { $(USER_MEDIA_TAB_ID).removeChild(requests[i]); } } - if ($(USER_MEDIA_TAB_ID).childNodes.length == 0) { + if ($(USER_MEDIA_TAB_ID).childNodes.length === 0) { tabView.removeTab(USER_MEDIA_TAB_ID); } }
diff --git a/content/child/runtime_features.cc b/content/child/runtime_features.cc index 71f8542d..94d69bc 100644 --- a/content/child/runtime_features.cc +++ b/content/child/runtime_features.cc
@@ -266,6 +266,7 @@ {wf::EnableMediaEngagementBypassAutoplayPolicies, media::kMediaEngagementBypassAutoplayPolicies}, {wf::EnableMediaFeeds, media::kMediaFeeds}, + {wf::EnableMediaSessionWebRTC, media::kMediaSessionWebRTC}, {wf::EnableMouseSubframeNoImplicitCapture, features::kMouseSubframeNoImplicitCapture}, {wf::EnableNeverSlowMode, features::kNeverSlowMode},
diff --git a/content/common/navigation_client.mojom b/content/common/navigation_client.mojom index e6fee95..c623787b 100644 --- a/content/common/navigation_client.mojom +++ b/content/common/navigation_client.mojom
@@ -12,7 +12,6 @@ import "services/network/public/mojom/url_loader.mojom"; import "services/network/public/mojom/url_loader_factory.mojom"; import "services/network/public/mojom/url_response_head.mojom"; -import "services/network/public/mojom/web_sandbox_flags.mojom"; import "third_party/blink/public/mojom/commit_result/commit_result.mojom"; import "third_party/blink/public/mojom/permissions_policy/document_policy_feature.mojom"; import "third_party/blink/public/mojom/permissions_policy/permissions_policy.mojom"; @@ -121,10 +120,6 @@ // RenderFrameProxies. url.mojom.Origin origin; - // TODO(https://crbug.com/1041376): Remove this, once we trust the value - // computed by the browser process. - network.mojom.WebSandboxFlags sandbox_flags; - // The 'Permissions-Policy' headers applied to the document. // https://w3c.github.io/webappsec-permissions-policy/#permissions-policy-http-header-field // Note: For backward compatibility, this field also contains
diff --git a/content/public/browser/render_widget_host_view.h b/content/public/browser/render_widget_host_view.h index 4a9d541..444da0c 100644 --- a/content/public/browser/render_widget_host_view.h +++ b/content/public/browser/render_widget_host_view.h
@@ -157,6 +157,9 @@ virtual void SetBackgroundColor(SkColor color) = 0; // GetBackgroundColor returns the current background color of the view. virtual base::Optional<SkColor> GetBackgroundColor() = 0; + // Copy background color from another view if other view has background color. + virtual void CopyBackgroundColorIfPresentFrom( + const RenderWidgetHostView& other) = 0; // Return value indicates whether the mouse is locked successfully or a // reason why it failed.
diff --git a/content/public/common/content_features.cc b/content/public/common/content_features.cc index 81e1a34..967f7dd 100644 --- a/content/public/common/content_features.cc +++ b/content/public/common/content_features.cc
@@ -981,7 +981,7 @@ "RetryGetVideoCaptureDeviceInfos", base::FEATURE_DISABLED_BY_DEFAULT}; const base::Feature kDesktopCaptureMacV2{"DesktopCaptureMacV2", - base::FEATURE_ENABLED_BY_DEFAULT}; + base::FEATURE_DISABLED_BY_DEFAULT}; const base::Feature kWindowCaptureMacV2{"WindowCaptureMacV2", base::FEATURE_DISABLED_BY_DEFAULT};
diff --git a/content/renderer/render_frame_impl.cc b/content/renderer/render_frame_impl.cc index ca0f2893..e24fe5b 100644 --- a/content/renderer/render_frame_impl.cc +++ b/content/renderer/render_frame_impl.cc
@@ -4007,7 +4007,6 @@ void RenderFrameImpl::DidCommitNavigation( blink::WebHistoryCommitType commit_type, bool should_reset_browser_interface_broker, - network::mojom::WebSandboxFlags sandbox_flags, const blink::ParsedPermissionsPolicy& permissions_policy_header, const blink::DocumentPolicyFeatureState& document_policy_header) { CHECK_EQ(NavigationCommitState::kWillCommit, navigation_commit_state_); @@ -4120,7 +4119,7 @@ GetTransitionType(frame_->GetDocumentLoader(), IsMainFrame()); DidCommitNavigationInternal( - commit_type, transition, sandbox_flags, permissions_policy_header, + commit_type, transition, permissions_policy_header, document_policy_header, should_reset_browser_interface_broker ? mojom::DidCommitProvisionalLoadInterfaceParams::New( @@ -4282,7 +4281,7 @@ same_document_params->is_history_api_navigation = is_history_api_navigation; same_document_params->is_client_redirect = is_client_redirect; DidCommitNavigationInternal( - commit_type, transition, network::mojom::WebSandboxFlags(), + commit_type, transition, blink::ParsedPermissionsPolicy(), // permissions_policy_header blink::DocumentPolicyFeatureState(), // document_policy_header nullptr, // interface_params @@ -4758,7 +4757,6 @@ RenderFrameImpl::MakeDidCommitProvisionalLoadParams( blink::WebHistoryCommitType commit_type, ui::PageTransition transition, - network::mojom::WebSandboxFlags sandbox_flags, const blink::ParsedPermissionsPolicy& permissions_policy_header, const blink::DocumentPolicyFeatureState& document_policy_header, const base::Optional<base::UnguessableToken>& embedding_token) { @@ -4809,7 +4807,6 @@ WebSecurityOrigin frame_origin = frame_document.GetSecurityOrigin(); params->origin = frame_origin; - params->sandbox_flags = sandbox_flags; params->permissions_policy_header = permissions_policy_header; params->document_policy_header = document_policy_header; @@ -5037,7 +5034,6 @@ void RenderFrameImpl::DidCommitNavigationInternal( blink::WebHistoryCommitType commit_type, ui::PageTransition transition, - network::mojom::WebSandboxFlags sandbox_flags, const blink::ParsedPermissionsPolicy& permissions_policy_header, const blink::DocumentPolicyFeatureState& document_policy_header, mojom::DidCommitProvisionalLoadInterfaceParamsPtr interface_params, @@ -5055,7 +5051,7 @@ // after the browser process has already been informed of the provisional // load committing. auto params = MakeDidCommitProvisionalLoadParams( - commit_type, transition, sandbox_flags, permissions_policy_header, + commit_type, transition, permissions_policy_header, document_policy_header, embedding_token); if (same_document_params) {
diff --git a/content/renderer/render_frame_impl.h b/content/renderer/render_frame_impl.h index b15a0cd..5e1bd3e 100644 --- a/content/renderer/render_frame_impl.h +++ b/content/renderer/render_frame_impl.h
@@ -547,7 +547,6 @@ void DidCommitNavigation( blink::WebHistoryCommitType commit_type, bool should_reset_browser_interface_broker, - network::mojom::WebSandboxFlags sandbox_flags, const blink::ParsedPermissionsPolicy& permissions_policy_header, const blink::DocumentPolicyFeatureState& document_policy_header) override; void DidCommitDocumentReplacementNavigation( @@ -1029,7 +1028,6 @@ mojom::DidCommitProvisionalLoadParamsPtr MakeDidCommitProvisionalLoadParams( blink::WebHistoryCommitType commit_type, ui::PageTransition transition, - network::mojom::WebSandboxFlags sandbox_flags, const blink::ParsedPermissionsPolicy& permissions_policy_header, const blink::DocumentPolicyFeatureState& document_policy_header, const base::Optional<base::UnguessableToken>& embedding_token); @@ -1054,7 +1052,6 @@ void DidCommitNavigationInternal( blink::WebHistoryCommitType commit_type, ui::PageTransition transition, - network::mojom::WebSandboxFlags sandbox_flags, const blink::ParsedPermissionsPolicy& permissions_policy_header, const blink::DocumentPolicyFeatureState& document_policy_header, mojom::DidCommitProvisionalLoadInterfaceParamsPtr interface_params,
diff --git a/content/test/BUILD.gn b/content/test/BUILD.gn index 07167c9..e3f52d6 100644 --- a/content/test/BUILD.gn +++ b/content/test/BUILD.gn
@@ -662,6 +662,19 @@ "//fuchsia/engine:web_engine_with_webui", ] } + group("performance_web_engine_test_suite") { + testonly = true + data = [ + "//content/test/gpu/run_telemetry_test_fuchsia.py", + "//content/test/gpu/", + ] + data_deps = [ + "//fuchsia/engine:web_engine_shell", + "//fuchsia/engine:web_engine_with_webui", + "//testing:run_perf_test", + "//tools/perf/:perf", + ] + } } group("telemetry_gpu_integration_test_scripts_only") {
diff --git a/content/test/gpu/fuchsia_util.py b/content/test/gpu/fuchsia_util.py new file mode 100644 index 0000000..32207df --- /dev/null +++ b/content/test/gpu/fuchsia_util.py
@@ -0,0 +1,85 @@ +# Copyright 2021 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 argparse +import logging +import os +import shutil +import subprocess +import sys +import tempfile + +from gpu_tests import path_util + +sys.path.insert(0, + os.path.join(path_util.GetChromiumSrcDir(), 'build', 'fuchsia')) +from common_args import (AddCommonArgs, ConfigureLogging, + GetDeploymentTargetForArgs) +from symbolizer import RunSymbolizer + + +def RunTestOnFuchsiaDevice(script_cmd): + """Preps Fuchsia device with pave and package update, then runs script.""" + parser = argparse.ArgumentParser() + AddCommonArgs(parser) + args, test_args = parser.parse_known_args() + ConfigureLogging(args) + + additional_target_args = {} + + # If output_dir is not set, assume the script is being launched + # from the output directory. + if not args.out_dir: + args.out_dir = os.getcwd() + additional_target_args['out_dir'] = args.out_dir + + # Create a temporary log file that Telemetry will look to use to build + # an artifact when tests fail. + temp_log_file = False + if not args.system_log_file: + args.system_log_file = os.path.join(tempfile.mkdtemp(), 'system-log') + temp_log_file = True + additional_target_args['system_log_file'] = args.system_log_file + + package_names = ['web_engine_with_webui', 'web_engine_shell'] + web_engine_dir = os.path.join(args.out_dir, 'gen', 'fuchsia', 'engine') + + # Pass all other arguments to the integration tests. + script_cmd.extend(test_args) + try: + with GetDeploymentTargetForArgs(additional_target_args) as target: + target.Start() + fuchsia_device_address, fuchsia_ssh_port = target._GetEndpoint() + script_cmd.extend(['--chromium-output-directory', args.out_dir]) + script_cmd.extend(['--fuchsia-device-address', fuchsia_device_address]) + script_cmd.extend(['--fuchsia-ssh-config', target._GetSshConfigPath()]) + if fuchsia_ssh_port: + script_cmd.extend(['--fuchsia-ssh-port', str(fuchsia_ssh_port)]) + script_cmd.extend(['--fuchsia-system-log-file', args.system_log_file]) + # Add to the script + if args.verbose: + script_cmd.append('-v') + + # Set up logging of WebEngine + listener = target.RunCommandPiped(['log_listener'], + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT) + build_ids_paths = map( + lambda package_name: os.path.join(web_engine_dir, package_name, + 'ids.txt'), package_names) + RunSymbolizer(listener.stdout, open(args.system_log_file, 'w'), + build_ids_paths) + + # Keep the Amber repository live while the test runs. + with target.GetAmberRepo(): + # Install necessary packages on the device. + far_files = map( + lambda package_name: os.path.join(web_engine_dir, package_name, + package_name + '.far'), + package_names) + target.InstallPackage(far_files) + return subprocess.call(script_cmd) + finally: + if temp_log_file: + shutil.rmtree(os.path.dirname(args.system_log_file))
diff --git a/content/test/gpu/run_gpu_integration_test_fuchsia.py b/content/test/gpu/run_gpu_integration_test_fuchsia.py index 7e33505..128ab57 100755 --- a/content/test/gpu/run_gpu_integration_test_fuchsia.py +++ b/content/test/gpu/run_gpu_integration_test_fuchsia.py
@@ -4,90 +4,20 @@ # found in the LICENSE file. """Wrapper for running gpu integration tests on Fuchsia devices.""" -import argparse -import logging import os import shutil -import subprocess import sys -import tempfile +import fuchsia_util from gpu_tests import path_util -sys.path.insert(0, - os.path.join(path_util.GetChromiumSrcDir(), 'build', 'fuchsia')) -from common_args import (AddCommonArgs, ConfigureLogging, - GetDeploymentTargetForArgs) -from symbolizer import RunSymbolizer - def main(): - parser = argparse.ArgumentParser() - AddCommonArgs(parser) - args, gpu_test_args = parser.parse_known_args() - ConfigureLogging(args) - - additional_target_args = {} - - # If output_dir is not set, assume the script is being launched - # from the output directory. - if not args.out_dir: - args.out_dir = os.getcwd() - additional_target_args['out_dir'] = args.out_dir - - # Create a temporary log file that Telemetry will look to use to build - # an artifact when tests fail. - temp_log_file = False - if not args.system_log_file: - args.system_log_file = os.path.join(tempfile.mkdtemp(), 'system-log') - temp_log_file = True - additional_target_args['system_log_file'] = args.system_log_file - - package_names = ['web_engine_with_webui', 'web_engine_shell'] - web_engine_dir = os.path.join(args.out_dir, 'gen', 'fuchsia', 'engine') gpu_script = [ os.path.join(path_util.GetChromiumSrcDir(), 'content', 'test', 'gpu', 'run_gpu_integration_test.py') ] - - # Pass all other arguments to the gpu integration tests. - gpu_script.extend(gpu_test_args) - try: - with GetDeploymentTargetForArgs(additional_target_args) as target: - target.Start() - fuchsia_device_address, fuchsia_ssh_port = target._GetEndpoint() - gpu_script.extend(['--chromium-output-directory', args.out_dir]) - gpu_script.extend(['--fuchsia-device-address', fuchsia_device_address]) - gpu_script.extend(['--fuchsia-ssh-config', target._GetSshConfigPath()]) - if fuchsia_ssh_port: - gpu_script.extend(['--fuchsia-ssh-port', str(fuchsia_ssh_port)]) - gpu_script.extend(['--fuchsia-system-log-file', args.system_log_file]) - if args.verbose: - gpu_script.append('-v') - - # Set up logging of WebEngine - listener = target.RunCommandPiped(['log_listener'], - stdout=subprocess.PIPE, - stderr=subprocess.STDOUT) - build_ids_paths = map( - lambda package_name: os.path.join( - web_engine_dir, package_name, 'ids.txt'), - package_names) - RunSymbolizer(listener.stdout, open(args.system_log_file, 'w'), - build_ids_paths) - - # Keep the Amber repository live while the test runs. - with target.GetAmberRepo(): - # Install necessary packages on the device. - far_files = map( - lambda package_name: os.path.join( - web_engine_dir, package_name, package_name + '.far'), - package_names) - target.InstallPackage(far_files) - return subprocess.call(gpu_script) - finally: - if temp_log_file: - shutil.rmtree(os.path.dirname(args.system_log_file)) + return fuchsia_util.RunTestOnFuchsiaDevice(gpu_script) if __name__ == '__main__':
diff --git a/content/test/gpu/run_telemetry_benchmark_fuchsia.py b/content/test/gpu/run_telemetry_benchmark_fuchsia.py new file mode 100755 index 0000000..1ff7570 --- /dev/null +++ b/content/test/gpu/run_telemetry_benchmark_fuchsia.py
@@ -0,0 +1,23 @@ +#!/usr/bin/env vpython +# Copyright 2021 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. +"""Wrapper for running Telemetry benchmarks on Fuchsia devices.""" + +import os +import sys + +import fuchsia_util +from gpu_tests import path_util + + +def main(): + telemetry_script = [ + os.path.join(path_util.GetChromiumSrcDir(), 'tools', 'perf', + 'run_benchmark') + ] + return fuchsia_util.RunTestOnFuchsiaDevice(telemetry_script) + + +if __name__ == '__main__': + sys.exit(main())
diff --git a/content/test/mock_platform_notification_service.cc b/content/test/mock_platform_notification_service.cc index 1d8a0687..96e59b4 100644 --- a/content/test/mock_platform_notification_service.cc +++ b/content/test/mock_platform_notification_service.cc
@@ -10,7 +10,6 @@ #include "base/bind.h" #include "base/callback_helpers.h" #include "base/guid.h" -#include "base/strings/nullable_string16.h" #include "base/strings/utf_string_conversions.h" #include "content/public/browser/browser_context.h" #include "content/public/browser/browser_task_traits.h"
diff --git a/content/test/navigation_simulator_impl.cc b/content/test/navigation_simulator_impl.cc index 58fef0d..7e79362 100644 --- a/content/test/navigation_simulator_impl.cc +++ b/content/test/navigation_simulator_impl.cc
@@ -1331,10 +1331,8 @@ } else { params->should_update_history = true; if (same_document) { - params->sandbox_flags = current_rfh->active_sandbox_flags(); params->origin = current_rfh->GetLastCommittedOrigin(); } else { - params->sandbox_flags = request_->SandboxFlagsToCommit(); params->origin = origin_.value_or(request_->GetOriginForURLLoaderFactory()); }
diff --git a/content/test/test_render_view_host.cc b/content/test/test_render_view_host.cc index 9c7a4077..fbc091f 100644 --- a/content/test/test_render_view_host.cc +++ b/content/test/test_render_view_host.cc
@@ -168,9 +168,7 @@ void TestRenderWidgetHostView::TakeFallbackContentFrom( RenderWidgetHostView* view) { - base::Optional<SkColor> color = view->GetBackgroundColor(); - if (color) - SetBackgroundColor(*color); + CopyBackgroundColorIfPresentFrom(*view); } blink::mojom::PointerLockResult TestRenderWidgetHostView::LockMouse(bool) {
diff --git a/content/web_test/browser/web_test_control_host.cc b/content/web_test/browser/web_test_control_host.cc index ded0dcfa..12a94df0 100644 --- a/content/web_test/browser/web_test_control_host.cc +++ b/content/web_test/browser/web_test_control_host.cc
@@ -27,7 +27,6 @@ #include "base/run_loop.h" #include "base/single_thread_task_runner.h" #include "base/stl_util.h" -#include "base/strings/nullable_string16.h" #include "base/strings/string_number_conversions.h" #include "base/strings/string_util.h" #include "base/strings/stringprintf.h"
diff --git a/content/web_test/renderer/test_runner.cc b/content/web_test/renderer/test_runner.cc index f0ce8736..0ee2aba 100644 --- a/content/web_test/renderer/test_runner.cc +++ b/content/web_test/renderer/test_runner.cc
@@ -17,7 +17,6 @@ #include "base/logging.h" #include "base/macros.h" #include "base/stl_util.h" -#include "base/strings/nullable_string16.h" #include "base/strings/stringprintf.h" #include "base/strings/utf_string_conversions.h" #include "build/build_config.h"
diff --git a/docs/asan.md b/docs/asan.md index db215fc..0e0e1a5 100644 --- a/docs/asan.md +++ b/docs/asan.md
@@ -98,7 +98,7 @@ ```shell out/asan/base_unittests \ --gtest_filter=ToolsSanityTest.DISABLED_AddressSanitizerLocalOOBCrashTest \ - --gtest_also_run_disabled_tests 2>&1 | tools/valgrind/asan/asan_symbolize.py + --gtest_also_run_disabled_tests ``` The test will crash with the following error report: @@ -124,9 +124,9 @@ ## Run chrome under ASan And finally, have fun with the `out/Release/chrome` binary. The filter script -`tools/valgrind/asan/asan_symbolize.py` should be used to symbolize the output. -(Note that `asan_symbolize.py` is absolutely necessary if you need the symbols - -there is no built-in symbolizer for ASan in Chrome). +`tools/valgrind/asan/asan_symbolize.py` can be used to symbolize the output, +although it shouldn't be necessary on Linux and Windows, where Chrome uses the +llvm-symbolizer in its source tree by default. ASan should perfectly work with Chrome's sandbox. You should only need to run with `--no-sandbox` on Linux if you're debugging ASan.
diff --git a/extensions/browser/allowlist_state.h b/extensions/browser/allowlist_state.h index f2bf6ab7..ae49511 100644 --- a/extensions/browser/allowlist_state.h +++ b/extensions/browser/allowlist_state.h
@@ -18,6 +18,22 @@ ALLOWLIST_ALLOWLISTED = 1, // The extension is not included in the Safe Browsing extension allowlist. ALLOWLIST_NOT_ALLOWLISTED = 2, + ALLOWLIST_LAST = 3 +}; + +// The acknowledge states for the Safe Browsing CRX allowlist enforcements. +enum AllowlistAcknowledgeState { + ALLOWLIST_ACKNOWLEDGE_NONE = 0, + // Used to notify the user that an extension was disabled or re-enabled by the + // allowlist enforcement. + ALLOWLIST_ACKNOWLEDGE_NEEDED = 1, + // State set when the user dismiss the notification in the extension menu. + ALLOWLIST_ACKNOWLEDGE_DONE = 2, + // The user clicked through the install friction dialog or re-enabled the + // extension after it was disabled. The extension should not be disabled again + // from the allowlist enforcement. + ALLOWLIST_ACKNOWLEDGE_ENABLED_BY_USER = 3, + ALLOWLIST_ACKNOWLEDGE_LAST = 4 }; } // namespace extensions
diff --git a/extensions/browser/extension_prefs.cc b/extensions/browser/extension_prefs.cc index 5d6b633..dc0ecb3b 100644 --- a/extensions/browser/extension_prefs.cc +++ b/extensions/browser/extension_prefs.cc
@@ -29,6 +29,7 @@ #include "extensions/browser/api/declarative_net_request/utils.h" #include "extensions/browser/app_sorting.h" #include "extensions/browser/blocklist_state.h" +#include "extensions/browser/disable_reason.h" #include "extensions/browser/event_router.h" #include "extensions/browser/extension_pref_store.h" #include "extensions/browser/extension_prefs_factory.h" @@ -80,6 +81,9 @@ // Indicates whether an extension is included in the Safe Browsing allowlist. constexpr const char kPrefAllowlist[] = "allowlist"; +// Indicates the enforcement state for the Safe Browsing allowlist. +constexpr const char kPrefAllowlistAcknowledge[] = "allowlist_acknowledge"; + // If extension is greylisted. constexpr const char kPrefBlocklistState[] = "blacklist_state"; @@ -1079,10 +1083,15 @@ AllowlistState ExtensionPrefs::GetExtensionAllowlistState( const std::string& extension_id) const { - int value; + int value = 0; if (!ReadPrefAsInteger(extension_id, kPrefAllowlist, &value)) return ALLOWLIST_UNDEFINED; + if (value < 0 || value >= ALLOWLIST_LAST) { + LOG(ERROR) << "Bad pref 'allowlist' for extension '" << extension_id << "'"; + return ALLOWLIST_UNDEFINED; + } + return static_cast<AllowlistState>(value); } @@ -1096,6 +1105,30 @@ } } +AllowlistAcknowledgeState ExtensionPrefs::GetExtensionAllowlistAcknowledgeState( + const std::string& extension_id) const { + int value = 0; + if (!ReadPrefAsInteger(extension_id, kPrefAllowlistAcknowledge, &value)) + return ALLOWLIST_ACKNOWLEDGE_NONE; + + if (value < 0 || value >= ALLOWLIST_ACKNOWLEDGE_LAST) { + LOG(ERROR) << "Bad pref 'allowlist_acknowledge' for extension '" + << extension_id << "'"; + return ALLOWLIST_ACKNOWLEDGE_NONE; + } + + return static_cast<AllowlistAcknowledgeState>(value); +} + +void ExtensionPrefs::SetExtensionAllowlistAcknowledgeState( + const std::string& extension_id, + AllowlistAcknowledgeState state) { + if (state != GetExtensionAllowlistAcknowledgeState(extension_id)) { + UpdateExtensionPref(extension_id, kPrefAllowlistAcknowledge, + std::make_unique<base::Value>(state)); + } +} + namespace { // Serializes a 64bit integer as a string value.
diff --git a/extensions/browser/extension_prefs.h b/extensions/browser/extension_prefs.h index 0bf3375..61c4105 100644 --- a/extensions/browser/extension_prefs.h +++ b/extensions/browser/extension_prefs.h
@@ -391,6 +391,14 @@ void SetExtensionAllowlistState(const std::string& extension_id, AllowlistState state); + // Gets the Safe Browsing allowlist acknowledge state. + AllowlistAcknowledgeState GetExtensionAllowlistAcknowledgeState( + const std::string& extension_id) const; + + // Sets the Safe Browsing allowlist acknowledge state. + void SetExtensionAllowlistAcknowledgeState(const std::string& extension_id, + AllowlistAcknowledgeState state); + // Increment the count of how many times we prompted the user to acknowledge // the given extension, and return the new count. int IncrementAcknowledgePromptCount(const std::string& extension_id);
diff --git a/extensions/common/api/alarms.idl b/extensions/common/api/alarms.idl index e5a0aeae..50a7bd0a 100644 --- a/extensions/common/api/alarms.idl +++ b/extensions/common/api/alarms.idl
@@ -75,18 +75,16 @@ // Retrieves details about the specified alarm. // |name|: The name of the alarm to get. Defaults to the empty string. - [supportsPromises] static void get( - optional DOMString name, - AlarmCallback callback); + [supportsPromises] static void get(optional DOMString name, + AlarmCallback callback); // Gets an array of all the alarms. [supportsPromises] static void getAll(AlarmListCallback callback); // Clears the alarm with the given name. // |name|: The name of the alarm to clear. Defaults to the empty string. - [supportsPromises] static void clear( - optional DOMString name, - optional ClearCallback callback); + [supportsPromises] static void clear(optional DOMString name, + optional ClearCallback callback); // Clears all alarms. [supportsPromises] static void clearAll(optional ClearCallback callback);
diff --git a/extensions/common/api/bluetooth_low_energy.idl b/extensions/common/api/bluetooth_low_energy.idl index aa3b58e0..3fd9168 100644 --- a/extensions/common/api/bluetooth_low_energy.idl +++ b/extensions/common/api/bluetooth_low_energy.idl
@@ -236,16 +236,14 @@ // may be other apps with open connections. // |deviceAddress|: The Bluetooth address of the remote device. // |callback|: Called when the disconnect request has completed. - [supportsPromises] static void disconnect( - DOMString deviceAddress, - optional ResultCallback callback); + [supportsPromises] static void disconnect(DOMString deviceAddress, + optional ResultCallback callback); // Get the GATT service with the given instance ID. // |serviceId|: The instance ID of the requested GATT service. // |callback|: Called with the requested Service object. - [supportsPromises] static void getService( - DOMString serviceId, - ServiceCallback callback); + [supportsPromises] static void getService(DOMString serviceId, + ServiceCallback callback); // Create a locally hosted GATT service. This service can be registered // to be available on a local GATT server. @@ -256,7 +254,7 @@ // |callback|: Called with the created services's unique ID. [supportsPromises] static void createService( Service service, - CreateServiceCallback callback); + CreateServiceCallback callback); // Get all the GATT services that were discovered on the remote device with // the given device address. @@ -269,9 +267,8 @@ // |deviceAddress|: The Bluetooth address of the remote device whose GATT // services should be returned. // |callback|: Called with the list of requested Service objects. - [supportsPromises] static void getServices( - DOMString deviceAddress, - ServicesCallback callback); + [supportsPromises] static void getServices(DOMString deviceAddress, + ServicesCallback callback); // Get the GATT characteristic with the given instance ID that belongs to // the given GATT service, if the characteristic exists. @@ -319,9 +316,8 @@ // |descriptorId|: The instance ID of the requested GATT characteristic // descriptor. // |callback|: Called with the requested Descriptor object. - [supportsPromises] static void getDescriptor( - DOMString descriptorId, - DescriptorCallback callback); + [supportsPromises] static void getDescriptor(DOMString descriptorId, + DescriptorCallback callback); // Create a locally hosted GATT descriptor. This descriptor must // be hosted under a valid characteristic. If the characteristic ID is not @@ -344,9 +340,8 @@ // descriptors should be returned. // |callback|: Called with the list of descriptors that belong to the given // characteristic. - [supportsPromises] static void getDescriptors( - DOMString characteristicId, - DescriptorsCallback callback); + [supportsPromises] static void getDescriptors(DOMString characteristicId, + DescriptorsCallback callback); // Retrieve the value of a specified characteristic from a remote // peripheral. @@ -418,7 +413,7 @@ // the result of the read request. [supportsPromises] static void readDescriptorValue( DOMString descriptorId, - DescriptorCallback callback); + DescriptorCallback callback); // Write the value of a specified characteristic descriptor from a remote // peripheral. @@ -439,9 +434,8 @@ // true. The peripheral permission may not be available to all apps. // |serviceId|: Unique ID of a created service. // |callback|: Callback with the result of the register operation. - [supportsPromises] static void registerService( - DOMString serviceId, - ResultCallback callback); + [supportsPromises] static void registerService(DOMString serviceId, + ResultCallback callback); // Unregister the given service with the local GATT server. If the service // ID is invalid, the lastError will be set. @@ -450,9 +444,8 @@ // true. The peripheral permission may not be available to all apps. // |serviceId|: Unique ID of a current registered service. // |callback|: Callback with the result of the register operation. - [supportsPromises] static void unregisterService( - DOMString serviceId, - ResultCallback callback); + [supportsPromises] static void unregisterService(DOMString serviceId, + ResultCallback callback); // Remove the specified service, unregistering it if it was registered. // If the service ID is invalid, the lastError will be set. @@ -463,7 +456,7 @@ // |callback|: Callback called once the service is removed. [supportsPromises] static void removeService( DOMString serviceId, - optional ResultCallback callback); + optional ResultCallback callback); // Create an advertisement and register it for advertising. To call this // function, the app must have the bluetooth:low_energy and @@ -483,7 +476,7 @@ // advertising. Returns the id of the created advertisement. [supportsPromises] static void registerAdvertisement( Advertisement advertisement, - RegisterAdvertisementCallback callback); + RegisterAdvertisementCallback callback); // Unregisters an advertisement and stops its advertising. If the // advertisement fails to unregister the only way to stop advertising @@ -512,7 +505,7 @@ // |callback|: Called once the interval has been set. [supportsPromises] static void setAdvertisingInterval( long minInterval, - long maxInterval, + long maxInterval, ResultCallback callback); // Sends a response for a characteristic or descriptor read/write
diff --git a/ios/chrome/app/strings/ios_strings.grd b/ios/chrome/app/strings/ios_strings.grd index 1a7c1888..683cb03 100644 --- a/ios/chrome/app/strings/ios_strings.grd +++ b/ios/chrome/app/strings/ios_strings.grd
@@ -2127,6 +2127,9 @@ <message name="IDS_IOS_COOKIES_BLOCK_THIRD_PARTY_INCOGNITO" desc="Label that informs the user that Third-Party cookies are blocked only in Incognito"> Block in Incognito </message> + <message name="IDS_IOS_RETURN_TO_RECENT_TAB_TITLE" desc="Title of the Return to Recent Tab Content Suggestons Tile."> + Open Most Recent Tab + </message> <message name="IDS_IOS_SETTINGS_PASSWORDS_SAVED_HEADING" desc="The title for a list of username/site/password items. These items are already saved by the browser and can be deleted/edited. [Length: one line] [iOS only]"> Saved Passwords </message>
diff --git a/ios/chrome/app/strings/ios_strings_grd/IDS_IOS_RETURN_TO_RECENT_TAB_TITLE.png.sha1 b/ios/chrome/app/strings/ios_strings_grd/IDS_IOS_RETURN_TO_RECENT_TAB_TITLE.png.sha1 new file mode 100644 index 0000000..505c5e7 --- /dev/null +++ b/ios/chrome/app/strings/ios_strings_grd/IDS_IOS_RETURN_TO_RECENT_TAB_TITLE.png.sha1
@@ -0,0 +1 @@ +2b59f020db8ddc5f5edd9c40d74e473030e312f2 \ No newline at end of file
diff --git a/ios/chrome/browser/main/BUILD.gn b/ios/chrome/browser/main/BUILD.gn index ae4eb73..aa4db98 100644 --- a/ios/chrome/browser/main/BUILD.gn +++ b/ios/chrome/browser/main/BUILD.gn
@@ -61,6 +61,7 @@ "//ios/chrome/browser/snapshots", "//ios/chrome/browser/tabs", "//ios/chrome/browser/ui/commands", + "//ios/chrome/browser/ui/start_surface", "//ios/chrome/browser/url_loading", "//ios/chrome/browser/web", "//ios/chrome/browser/web:tab_id_tab_helper",
diff --git a/ios/chrome/browser/main/browser_agent_util.mm b/ios/chrome/browser/main/browser_agent_util.mm index 5df3a24..b13580cf 100644 --- a/ios/chrome/browser/main/browser_agent_util.mm +++ b/ios/chrome/browser/main/browser_agent_util.mm
@@ -20,6 +20,7 @@ #import "ios/chrome/browser/snapshots/snapshot_browser_agent.h" #import "ios/chrome/browser/tabs/closing_web_state_observer_browser_agent.h" #include "ios/chrome/browser/tabs/synced_window_delegate_browser_agent.h" +#import "ios/chrome/browser/ui/start_surface/start_surface_recent_tab_browser_agent.h" #import "ios/chrome/browser/url_loading/url_loading_browser_agent.h" #import "ios/chrome/browser/url_loading/url_loading_notifier_browser_agent.h" #import "ios/chrome/browser/web/web_navigation_browser_agent.h" @@ -72,6 +73,9 @@ if (!browser->GetBrowserState()->IsOffTheRecord()) TabUsageRecorderBrowserAgent::CreateForBrowser(browser); + if (!browser->GetBrowserState()->IsOffTheRecord()) + StartSurfaceRecentTabBrowserAgent::CreateForBrowser(browser); + // This needs to be called last in case any downstream browser agents need to // access upstream agents created earlier in this function. ios::GetChromeBrowserProvider()->AttachBrowserAgents(browser);
diff --git a/ios/chrome/browser/ui/content_suggestions/BUILD.gn b/ios/chrome/browser/ui/content_suggestions/BUILD.gn index 68dcccd..6621b8e 100644 --- a/ios/chrome/browser/ui/content_suggestions/BUILD.gn +++ b/ios/chrome/browser/ui/content_suggestions/BUILD.gn
@@ -36,6 +36,7 @@ ":metrics", "//base", "//components/favicon/core", + "//components/favicon/ios", "//components/feature_engagement/public", "//components/feed/core/shared_prefs:feed_shared_prefs", "//components/ntp_snippets", @@ -88,6 +89,7 @@ "//ios/chrome/browser/ui/reading_list", "//ios/chrome/browser/ui/settings/utils:utils", "//ios/chrome/browser/ui/sharing", + "//ios/chrome/browser/ui/start_surface", "//ios/chrome/browser/ui/start_surface:feature_flags", "//ios/chrome/browser/ui/toolbar/public", "//ios/chrome/browser/ui/util", @@ -177,6 +179,7 @@ "//ios/chrome/browser/ui/omnibox:omnibox_internal", "//ios/chrome/browser/ui/omnibox:omnibox_popup_shared", "//ios/chrome/browser/ui/overscroll_actions", + "//ios/chrome/browser/ui/start_surface:feature_flags", "//ios/chrome/browser/ui/thumb_strip:public", "//ios/chrome/browser/ui/toolbar/buttons", "//ios/chrome/browser/ui/toolbar/public",
diff --git a/ios/chrome/browser/ui/content_suggestions/cells/BUILD.gn b/ios/chrome/browser/ui/content_suggestions/cells/BUILD.gn index fb8bbe3..cdc45de 100644 --- a/ios/chrome/browser/ui/content_suggestions/cells/BUILD.gn +++ b/ios/chrome/browser/ui/content_suggestions/cells/BUILD.gn
@@ -11,6 +11,8 @@ "content_suggestions_most_visited_action_item.mm", "content_suggestions_most_visited_item.h", "content_suggestions_most_visited_item.mm", + "content_suggestions_return_to_recent_tab_item.h", + "content_suggestions_return_to_recent_tab_item.mm", ] deps = [ ":cells_ui",
diff --git a/ios/chrome/browser/ui/content_suggestions/cells/content_suggestions_return_to_recent_tab_item.h b/ios/chrome/browser/ui/content_suggestions/cells/content_suggestions_return_to_recent_tab_item.h new file mode 100644 index 0000000..b2eb8f8 --- /dev/null +++ b/ios/chrome/browser/ui/content_suggestions/cells/content_suggestions_return_to_recent_tab_item.h
@@ -0,0 +1,51 @@ +// Copyright 2021 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_CELLS_CONTENT_SUGGESTIONS_RETURN_TO_RECENT_TAB_ITEM_H_ +#define IOS_CHROME_BROWSER_UI_CONTENT_SUGGESTIONS_CELLS_CONTENT_SUGGESTIONS_RETURN_TO_RECENT_TAB_ITEM_H_ + +#import "ios/chrome/browser/ui/collection_view/cells/collection_view_item.h" + +#import <MaterialComponents/MaterialCollectionCells.h> +#import <UIKit/UIKit.h> + +#import "ios/chrome/browser/ui/content_suggestions/cells/suggested_content.h" + +@protocol ContentSuggestionsGestureCommands; +@class FaviconAttributes; + +// Item containing a Return to Recent Tab Start Surface tile. +@interface ContentSuggestionsReturnToRecentTabItem + : CollectionViewItem <SuggestedContent> + +// Favicon image of the page of the most recent tab. +@property(nonatomic, strong) UIImage* icon; + +// Title of the most recent tab tile. +@property(nonatomic, copy) NSString* title; + +// Subtitle of the most recent tab tile. +@property(nonatomic, copy) NSString* subtitle; + +// Command handler for the accessibility custom actions. +@property(nonatomic, weak) id<ContentSuggestionsGestureCommands> commandHandler; + +@end + +@interface ContentSuggestionsReturnToRecentTabCell : MDCCollectionViewCell + +// Sets the title of the most recent tab tile. +- (void)setTitle:(NSString*)title; + +// sets the subtitle of the most recent tab tile. +- (void)setSubtitle:(NSString*)subtitle; + ++ (CGSize)defaultSize; + +// Sets the image that should be displayed at the leading edge of the cell. +- (void)setIconImage:(UIImage*)image; + +@end + +#endif // IOS_CHROME_BROWSER_UI_CONTENT_SUGGESTIONS_CELLS_CONTENT_SUGGESTIONS_RETURN_TO_RECENT_TAB_ITEM_H_
diff --git a/ios/chrome/browser/ui/content_suggestions/cells/content_suggestions_return_to_recent_tab_item.mm b/ios/chrome/browser/ui/content_suggestions/cells/content_suggestions_return_to_recent_tab_item.mm new file mode 100644 index 0000000..0797ae5 --- /dev/null +++ b/ios/chrome/browser/ui/content_suggestions/cells/content_suggestions_return_to_recent_tab_item.mm
@@ -0,0 +1,97 @@ +// Copyright 2021 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/ui/content_suggestions/cells/content_suggestions_return_to_recent_tab_item.h" + +#import "ios/chrome/browser/ui/content_suggestions/cells/content_suggestions_gesture_commands.h" +#include "ui/base/l10n/l10n_util.h" + +#if !defined(__has_feature) || !__has_feature(objc_arc) +#error "This file requires ARC support." +#endif + +namespace { +const CGSize regularCellSize = {/*width=*/343, /*height=*/72}; +} + +@implementation ContentSuggestionsReturnToRecentTabItem +@synthesize metricsRecorded; +@synthesize suggestionIdentifier; + +- (instancetype)initWithType:(NSInteger)type { + self = [super initWithType:type]; + if (self) { + self.cellClass = [ContentSuggestionsReturnToRecentTabCell class]; + } + return self; +} + +- (void)configureCell:(ContentSuggestionsReturnToRecentTabCell*)cell { + [super configureCell:cell]; + [cell setTitle:self.title]; + [cell setSubtitle:self.subtitle]; + cell.accessibilityLabel = self.title; + if (self.icon) { + [cell setIconImage:self.icon]; + } + cell.accessibilityCustomActions = [self customActions]; +} + +- (CGFloat)cellHeightForWidth:(CGFloat)width { + return [ContentSuggestionsReturnToRecentTabCell defaultSize].height; +} + +// Custom action for a cell configured with this item. +- (NSArray<UIAccessibilityCustomAction*>*)customActions { + UIAccessibilityCustomAction* openMostRecentTab = + [[UIAccessibilityCustomAction alloc] + initWithName:@"Open Most Recent Tab" + target:self + selector:@selector(openMostRecentTab)]; + + return @[ openMostRecentTab ]; +} + +- (BOOL)openMostRecentTab { + // TODO:(crbug.com/1173160) implement. + return YES; +} + +@end + +#pragma mark - ContentSuggestionsReturnToRecentTabCell + +@interface ContentSuggestionsReturnToRecentTabCell () + +// Favicon image. +@property(nonatomic, strong) UIImageView* iconImageView; + +// Title of the most recent tab tile. +@property(nonatomic, strong, readonly) UILabel* titleLabel; + +// Subtitle of the most recent tab tile. +@property(nonatomic, strong, readonly) UILabel* subtitleLabel; + +@end + +@implementation ContentSuggestionsReturnToRecentTabCell + +- (void)setTitle:(NSString*)title { + self.titleLabel.text = title; +} + +- (void)setSubtitle:(NSString*)subtitle { + self.subtitleLabel.text = subtitle; +} + ++ (CGSize)defaultSize { + return regularCellSize; +} + +- (void)setIconImage:(UIImage*)image { + _iconImageView.image = image; + _iconImageView.hidden = image == nil; +} + +@end
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 09bfc7d..67a1bdd 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
@@ -25,6 +25,7 @@ ContentSuggestionTypeArticle, ContentSuggestionTypeReadingList, ContentSuggestionTypeMostVisited, + ContentSuggestionTypeReturnToRecentTab, ContentSuggestionTypePromo, ContentSuggestionTypeLearnMore, ContentSuggestionTypeDiscover,
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 1f7e4b53..c7ed780 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
@@ -54,6 +54,7 @@ ItemTypePromo, ItemTypeLearnMore, ItemTypeDiscover, + ItemTypeReturnToRecentTab, ItemTypeUnknown, }; @@ -64,6 +65,7 @@ SectionIdentifierReadingList, SectionIdentifierMostVisited, SectionIdentifierLogo, + SectionIdentifierReturnToRecentTab, SectionIdentifierPromo, SectionIdentifierLearnMore, SectionIdentifierDiscover, @@ -78,6 +80,8 @@ return ContentSuggestionTypeEmpty; if (type == ItemTypeReadingList) return ContentSuggestionTypeReadingList; + if (type == ItemTypeReturnToRecentTab) + return ContentSuggestionTypeReturnToRecentTab; if (type == ItemTypeMostVisited) return ContentSuggestionTypeMostVisited; if (type == ItemTypePromo) @@ -99,6 +103,8 @@ return ItemTypeArticle; case ContentSuggestionsSectionReadingList: return ItemTypeReadingList; + case ContentSuggestionsSectionReturnToRecentTab: + return ItemTypeReturnToRecentTab; case ContentSuggestionsSectionMostVisited: return ItemTypeMostVisited; case ContentSuggestionsSectionPromo: @@ -125,6 +131,8 @@ return SectionIdentifierMostVisited; case ContentSuggestionsSectionLogo: return SectionIdentifierLogo; + case ContentSuggestionsSectionReturnToRecentTab: + return SectionIdentifierReturnToRecentTab; case ContentSuggestionsSectionPromo: return SectionIdentifierPromo; case ContentSuggestionsSectionLearnMore:
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 ee07d09..caa4dc35 100644 --- a/ios/chrome/browser/ui/content_suggestions/content_suggestions_commands.h +++ b/ios/chrome/browser/ui/content_suggestions/content_suggestions_commands.h
@@ -14,21 +14,23 @@ // Opens the Reading List. - (void)openReadingList; // Opens the page associated with the item at |indexPath|. -- (void)openPageForItemAtIndexPath:(nonnull NSIndexPath*)indexPath; +- (void)openPageForItemAtIndexPath:(NSIndexPath*)indexPath; // Opens the Most Visited associated with this |item| at the |mostVisitedItem|. -- (void)openMostVisitedItem:(nonnull CollectionViewItem*)item +- (void)openMostVisitedItem:(CollectionViewItem*)item atIndex:(NSInteger)mostVisitedIndex; +// Handles the actions tapping the "Return to Recent Tab" item that returns the +// user to the last opened tab. +- (void)openMostRecentTab:(CollectionViewItem*)item; // Displays a context menu for the |suggestionItem|. -- (void)displayContextMenuForSuggestion: - (nonnull CollectionViewItem*)suggestionItem +- (void)displayContextMenuForSuggestion:(CollectionViewItem*)suggestionItem atPoint:(CGPoint)touchLocation - atIndexPath:(nonnull NSIndexPath*)indexPath + atIndexPath:(NSIndexPath*)indexPath readLaterAction:(BOOL)readLaterAction; // Displays a context menu for the |mostVisitedItem|. - (void)displayContextMenuForMostVisitedItem: - (nonnull CollectionViewItem*)mostVisitedItem + (CollectionViewItem*)mostVisitedItem atPoint:(CGPoint)touchLocation - atIndexPath:(nonnull NSIndexPath*)indexPath; + atIndexPath:(NSIndexPath*)indexPath; // Dismisses the context menu if it is displayed. - (void)dismissModals; // Handles the actions following a tap on the promo.
diff --git a/ios/chrome/browser/ui/content_suggestions/content_suggestions_coordinator.h b/ios/chrome/browser/ui/content_suggestions/content_suggestions_coordinator.h index 5089742a..c6fa5580 100644 --- a/ios/chrome/browser/ui/content_suggestions/content_suggestions_coordinator.h +++ b/ios/chrome/browser/ui/content_suggestions/content_suggestions_coordinator.h
@@ -84,6 +84,8 @@ // Constrains the named layout guide for the Discover header menu button. - (void)constrainDiscoverHeaderMenuButtonNamedGuide; +// Configure Content Suggestions if showing the Start Surface. +- (void)configureStartSurfaceIfNeeded; @end #endif // IOS_CHROME_BROWSER_UI_CONTENT_SUGGESTIONS_CONTENT_SUGGESTIONS_COORDINATOR_H_
diff --git a/ios/chrome/browser/ui/content_suggestions/content_suggestions_coordinator.mm b/ios/chrome/browser/ui/content_suggestions/content_suggestions_coordinator.mm index ec7edbc..cfdc765 100644 --- a/ios/chrome/browser/ui/content_suggestions/content_suggestions_coordinator.mm +++ b/ios/chrome/browser/ui/content_suggestions/content_suggestions_coordinator.mm
@@ -9,6 +9,7 @@ #include "base/metrics/user_metrics.h" #include "base/metrics/user_metrics_action.h" #include "base/scoped_observer.h" +#include "base/strings/sys_string_conversions.h" #import "components/feature_engagement/public/event_constants.h" #import "components/feature_engagement/public/tracker.h" #include "components/feed/core/shared_prefs/pref_names.h" @@ -74,10 +75,14 @@ #import "ios/chrome/browser/ui/overscroll_actions/overscroll_actions_controller.h" #import "ios/chrome/browser/ui/settings/utils/pref_backed_boolean.h" #import "ios/chrome/browser/ui/sharing/sharing_coordinator.h" +#import "ios/chrome/browser/ui/start_surface/start_surface_features.h" +#import "ios/chrome/browser/ui/start_surface/start_surface_recent_tab_browser_agent.h" +#import "ios/chrome/browser/ui/start_surface/start_surface_util.h" #import "ios/chrome/browser/ui/util/named_guide.h" #import "ios/chrome/browser/ui/util/uikit_ui_util.h" #import "ios/chrome/browser/url_loading/url_loading_browser_agent.h" #import "ios/chrome/browser/url_loading/url_loading_params.h" +#import "ios/chrome/browser/web_state_list/web_state_list.h" #include "ios/chrome/grit/ios_strings.h" #import "ios/public/provider/chrome/browser/chrome_browser_provider.h" #import "ios/public/provider/chrome/browser/discover_feed/discover_feed_provider.h" @@ -99,6 +104,10 @@ OverscrollActionsControllerDelegate, ThemeChangeDelegate, URLDropDelegate> { + // Observer bridge for mediator to listen to + // StartSurfaceRecentTabRemovalObserverBridge. + std::unique_ptr<StartSurfaceRecentTabRemovalObserverBridge> + _startSurfaceObserver; } @property(nonatomic, strong) @@ -245,6 +254,9 @@ self.contentSuggestionsExpanded; } self.contentSuggestionsMediator.discoverFeedDelegate = self; + self.contentSuggestionsMediator.webStateList = + self.browser->GetWebStateList(); + [self configureStartSurfaceIfNeeded]; self.headerController.promoCanShow = [self.contentSuggestionsMediator notificationPromo]->CanShow(); @@ -355,6 +367,11 @@ ->RemoveFeedViewController(self.discoverFeedViewController); } self.contentSuggestionsExpanded = nil; + if (_startSurfaceObserver) { + StartSurfaceRecentTabBrowserAgent::FromBrowser(self.browser) + ->RemoveObserver(_startSurfaceObserver.get()); + _startSurfaceObserver.reset(); + } _started = NO; } @@ -394,6 +411,12 @@ } } +- (void)viewDidDisappear { + if (ShouldShowReturnToMostRecentTabForStartSurface()) { + [self.contentSuggestionsMediator hideRecentTabTile]; + } +} + #pragma mark - OverscrollActionsControllerDelegate - (void)overscrollActionsController:(OverscrollActionsController*)controller @@ -710,6 +733,30 @@ #pragma mark - Helpers +- (void)configureStartSurfaceIfNeeded { + SceneState* scene = + SceneStateBrowserAgent::FromBrowser(self.browser)->GetSceneState(); + BOOL shouldShowReturnToRecentTabTile = + scene.modifytVisibleNTPForStartSurface && + ShouldShowReturnToMostRecentTabForStartSurface(); + if (shouldShowReturnToRecentTabTile) { + web::WebState* most_recent_tab = + StartSurfaceRecentTabBrowserAgent::FromBrowser(self.browser) + ->most_recent_tab(); + DCHECK(most_recent_tab); + [self.contentSuggestionsMediator + configureMostRecentTabItemWithWebState:most_recent_tab]; + if (!_startSurfaceObserver) { + _startSurfaceObserver = + std::make_unique<StartSurfaceRecentTabRemovalObserverBridge>( + self.contentSuggestionsMediator); + StartSurfaceRecentTabBrowserAgent::FromBrowser(self.browser) + ->AddObserver(_startSurfaceObserver.get()); + } + scene.modifytVisibleNTPForStartSurface = NO; + } +} + // Creates, configures and returns a DiscoverFeed ViewController. - (UIViewController*)discoverFeed { if (!IsDiscoverFeedEnabled() || IsRefactoredNTP() ||
diff --git a/ios/chrome/browser/ui/content_suggestions/content_suggestions_mediator.h b/ios/chrome/browser/ui/content_suggestions/content_suggestions_mediator.h index fdb6564..865f924 100644 --- a/ios/chrome/browser/ui/content_suggestions/content_suggestions_mediator.h +++ b/ios/chrome/browser/ui/content_suggestions/content_suggestions_mediator.h
@@ -14,6 +14,7 @@ #import "ios/chrome/browser/ui/content_suggestions/content_suggestions_mediator.h" #import "ios/chrome/browser/ui/content_suggestions/content_suggestions_metrics_recorder.h" #import "ios/chrome/browser/ui/settings/utils/pref_backed_boolean.h" +#import "ios/chrome/browser/ui/start_surface/start_surface_recent_tab_removal_observer_bridge.h" namespace favicon { class LargeIconService; @@ -41,13 +42,15 @@ class LargeIconCache; class NotificationPromoWhatsNew; class ReadingListModel; +class WebStateList; // Mediator for ContentSuggestions. Makes the interface between a // ntp_snippets::ContentSuggestionsService and the Objective-C services using // its data. @interface ContentSuggestionsMediator - : NSObject<ContentSuggestionsDataSource, - ContentSuggestionsMetricsRecorderDelegate> + : NSObject <ContentSuggestionsDataSource, + ContentSuggestionsMetricsRecorderDelegate, + StartSurfaceRecentTabRemovalObserving> // Initialize the mediator with the |contentService| to mediate. - (instancetype) @@ -91,6 +94,9 @@ // The consumer for this mediator. @property(nonatomic, weak) id<ContentSuggestionsConsumer> consumer; +// WebStateList associated with this mediator. +@property(nonatomic, assign) WebStateList* webStateList; + // Disconnects the mediator. - (void)disconnect; @@ -109,6 +115,12 @@ // Get the maximum number of sites shown. + (NSUInteger)maxSitesShown; +// Configures the most recent tab item for |webState|. +- (void)configureMostRecentTabItemWithWebState:(web::WebState*)webState; + +// Indicates that the "Return to Recent Tab" tile should be hidden. +- (void)hideRecentTabTile; + @end #endif // IOS_CHROME_BROWSER_UI_CONTENT_SUGGESTIONS_CONTENT_SUGGESTIONS_MEDIATOR_H_
diff --git a/ios/chrome/browser/ui/content_suggestions/content_suggestions_mediator.mm b/ios/chrome/browser/ui/content_suggestions/content_suggestions_mediator.mm index 67bcd86..1fed49b 100644 --- a/ios/chrome/browser/ui/content_suggestions/content_suggestions_mediator.mm +++ b/ios/chrome/browser/ui/content_suggestions/content_suggestions_mediator.mm
@@ -8,6 +8,7 @@ #include "base/mac/foundation_util.h" #include "base/optional.h" #include "base/strings/sys_string_conversions.h" +#include "components/favicon/ios/web_favicon_driver.h" #include "components/ntp_snippets/category.h" #include "components/ntp_snippets/category_info.h" #include "components/ntp_snippets/content_suggestion.h" @@ -28,6 +29,7 @@ #import "ios/chrome/browser/ui/content_suggestions/cells/content_suggestions_learn_more_item.h" #import "ios/chrome/browser/ui/content_suggestions/cells/content_suggestions_most_visited_action_item.h" #import "ios/chrome/browser/ui/content_suggestions/cells/content_suggestions_most_visited_item.h" +#import "ios/chrome/browser/ui/content_suggestions/cells/content_suggestions_return_to_recent_tab_item.h" #import "ios/chrome/browser/ui/content_suggestions/cells/content_suggestions_whats_new_item.h" #import "ios/chrome/browser/ui/content_suggestions/cells/suggested_content.h" #import "ios/chrome/browser/ui/content_suggestions/content_suggestions_category_wrapper.h" @@ -47,10 +49,13 @@ #include "ios/chrome/browser/ui/ntp/ntp_tile_saver.h" #import "ios/chrome/browser/ui/start_surface/start_surface_features.h" #include "ios/chrome/browser/ui/util/ui_util.h" +#import "ios/chrome/browser/web_state_list/web_state_list.h" #include "ios/chrome/common/app_group/app_group_constants.h" +#include "ios/chrome/grit/ios_strings.h" #include "ios/public/provider/chrome/browser/chrome_browser_provider.h" #import "ios/public/provider/chrome/browser/discover_feed/discover_feed_observer_bridge.h" #include "ios/public/provider/chrome/browser/images/branded_image_provider.h" +#include "ui/base/l10n/l10n_util_mac.h" #if !defined(__has_feature) || !__has_feature(objc_arc) #error "This file requires ARC support." @@ -104,6 +109,12 @@ // Section Info for the logo and omnibox section. @property(nonatomic, strong) ContentSuggestionsSectionInformation* logoSectionInfo; +// Section Info for the "Return to Recent Tab" section. +@property(nonatomic, strong) + ContentSuggestionsSectionInformation* returnToRecentTabSectionInfo; +// Item for the "Return to Recent Tab" tile. +@property(nonatomic, strong) + ContentSuggestionsReturnToRecentTabItem* returnToRecentTabItem; // Section Info for the What's New promo section. @property(nonatomic, strong) ContentSuggestionsSectionInformation* promoSectionInfo; @@ -139,7 +150,8 @@ @property(nonatomic, strong) ContentSuggestionsDiscoverItem* discoverItem; // Number of unread items in reading list model. @property(nonatomic, assign) NSInteger readingListUnreadCount; - +// Whether to show the most recent tab tile. +@property(nonatomic, assign) BOOL showMostRecentTabStartSurfaceTile; // Whether the incognito mode is available. @property(nonatomic, assign) BOOL incognitoAvailable; @@ -267,6 +279,47 @@ return kMaxNumMostVisitedTiles; } +- (void)configureMostRecentTabItemWithWebState:(web::WebState*)webState { + DCHECK(IsStartSurfaceEnabled()); + self.returnToRecentTabSectionInfo = ReturnToRecentTabSectionInformation(); + if (!self.returnToRecentTabItem) { + self.returnToRecentTabItem = + [[ContentSuggestionsReturnToRecentTabItem alloc] initWithType:0]; + } + + // Retrieve favicon associated with the page. + favicon::WebFaviconDriver* driver = + favicon::WebFaviconDriver::FromWebState(webState); + if (driver->FaviconIsValid()) { + gfx::Image favicon = driver->GetFavicon(); + if (!favicon.IsEmpty()) { + self.returnToRecentTabItem.icon = favicon.ToUIImage(); + } + } + + self.returnToRecentTabItem.title = + l10n_util::GetNSString(IDS_IOS_RETURN_TO_RECENT_TAB_TITLE); + self.returnToRecentTabItem.subtitle = + base::SysUTF16ToNSString(webState->GetTitle()); + self.showMostRecentTabStartSurfaceTile = YES; + + // TODO(crbug.com/1187303): Create insert section to add a section. + [self.dataSink reloadAllData]; +} + +- (void)hideRecentTabTile { + DCHECK(IsStartSurfaceEnabled()); + self.showMostRecentTabStartSurfaceTile = NO; + [self.dataSink clearSection:self.returnToRecentTabSectionInfo]; +} + +#pragma mark - StartSurfaceRecentTabRemovalObserving + +- (void)mostRecentTabWasRemoved:(web::WebState*)web_state { + DCHECK(IsStartSurfaceEnabled()); + [self hideRecentTabTile]; +} + #pragma mark - ContentSuggestionsDataSource - (NSArray<ContentSuggestionsSectionInformation*>*)sectionsInfo { @@ -275,6 +328,11 @@ [sectionsInfo addObject:self.logoSectionInfo]; + if (self.showMostRecentTabStartSurfaceTile) { + DCHECK(IsStartSurfaceEnabled()); + [sectionsInfo addObject:self.returnToRecentTabSectionInfo]; + } + if (_notificationPromo->CanShow()) { [sectionsInfo addObject:self.promoSectionInfo]; } @@ -327,6 +385,9 @@ item.text = base::SysUTF8ToNSString(_notificationPromo->promo_text()); [convertedSuggestions addObject:item]; } + } else if (sectionInfo == self.returnToRecentTabSectionInfo) { + DCHECK(IsStartSurfaceEnabled()); + [convertedSuggestions addObject:self.returnToRecentTabItem]; } else if (sectionInfo == self.mostVisitedSectionInfo) { [convertedSuggestions addObjectsFromArray:self.mostVisitedItems]; if (!ShouldHideShortcutsForStartSurface()) {
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 47bb324..3cf99cc 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
@@ -37,6 +37,7 @@ #import "ios/chrome/browser/ui/ntp/new_tab_page_header_constants.h" #import "ios/chrome/browser/ui/ntp_tile_views/ntp_tile_layout_util.h" #import "ios/chrome/browser/ui/overscroll_actions/overscroll_actions_controller.h" +#import "ios/chrome/browser/ui/start_surface/start_surface_features.h" #import "ios/chrome/browser/ui/toolbar/public/toolbar_utils.h" #import "ios/chrome/browser/ui/ui_feature_flags.h" #import "ios/chrome/browser/ui/util/menu_util.h" @@ -341,6 +342,9 @@ - (void)viewDidDisappear:(BOOL)animated { [super viewDidDisappear:animated]; self.headerSynchronizer.showing = NO; + if (ShouldShowReturnToMostRecentTabForStartSurface()) { + [self.audience viewDidDisappear]; + } } - (void)didMoveToParentViewController:(UIViewController*)parent { @@ -426,6 +430,9 @@ [self.suggestionCommandHandler openMostVisitedItem:item atIndex:indexPath.item]; break; + case ContentSuggestionTypeReturnToRecentTab: + [self.suggestionCommandHandler openMostRecentTab:item]; + break; case ContentSuggestionTypePromo: [self dismissSection:indexPath.section]; [self.suggestionCommandHandler handlePromoTapped];
diff --git a/ios/chrome/browser/ui/content_suggestions/content_suggestions_view_controller_audience.h b/ios/chrome/browser/ui/content_suggestions/content_suggestions_view_controller_audience.h index 450c9e7..920bcc8f 100644 --- a/ios/chrome/browser/ui/content_suggestions/content_suggestions_view_controller_audience.h +++ b/ios/chrome/browser/ui/content_suggestions/content_suggestions_view_controller_audience.h
@@ -20,6 +20,9 @@ // called multiple times. - (void)discoverFeedShown; +// Notifies the audience of the UIKit viewDidDisappear: callback. +- (void)viewDidDisappear; + @end #endif // IOS_CHROME_BROWSER_UI_CONTENT_SUGGESTIONS_CONTENT_SUGGESTIONS_VIEW_CONTROLLER_AUDIENCE_H_
diff --git a/ios/chrome/browser/ui/content_suggestions/identifier/content_suggestions_section_information.h b/ios/chrome/browser/ui/content_suggestions/identifier/content_suggestions_section_information.h index d853b1a..5397557 100644 --- a/ios/chrome/browser/ui/content_suggestions/identifier/content_suggestions_section_information.h +++ b/ios/chrome/browser/ui/content_suggestions/identifier/content_suggestions_section_information.h
@@ -19,6 +19,7 @@ // different ID. typedef NS_ENUM(NSInteger, ContentSuggestionsSectionID) { ContentSuggestionsSectionLogo = 0, + ContentSuggestionsSectionReturnToRecentTab, ContentSuggestionsSectionPromo, ContentSuggestionsSectionMostVisited, ContentSuggestionsSectionArticles,
diff --git a/ios/chrome/browser/ui/content_suggestions/mediator_util.h b/ios/chrome/browser/ui/content_suggestions/mediator_util.h index d4d3900b..aaff7a9 100644 --- a/ios/chrome/browser/ui/content_suggestions/mediator_util.h +++ b/ios/chrome/browser/ui/content_suggestions/mediator_util.h
@@ -47,6 +47,10 @@ ContentSuggestionsCategoryWrapper* category, const std::string& id_in_category); +// Creates and returns a SectionInfo for the section containing the "Return to +// Recent Tab" tile for the Start Surface. +ContentSuggestionsSectionInformation* ReturnToRecentTabSectionInformation(); + // Creates and returns a SectionInfo for the section containing the logo and // omnibox. ContentSuggestionsSectionInformation* LogoSectionInformation();
diff --git a/ios/chrome/browser/ui/content_suggestions/mediator_util.mm b/ios/chrome/browser/ui/content_suggestions/mediator_util.mm index 7c913eae..6029ef8f 100644 --- a/ios/chrome/browser/ui/content_suggestions/mediator_util.mm +++ b/ios/chrome/browser/ui/content_suggestions/mediator_util.mm
@@ -120,6 +120,10 @@ return sectionInfo; } +ContentSuggestionsSectionInformation* ReturnToRecentTabSectionInformation() { + return EmptySectionInfo(ContentSuggestionsSectionReturnToRecentTab); +} + ContentSuggestionsSectionInformation* PromoSectionInformation() { return EmptySectionInfo(ContentSuggestionsSectionPromo); }
diff --git a/ios/chrome/browser/ui/content_suggestions/ntp_home_mediator.mm b/ios/chrome/browser/ui/content_suggestions/ntp_home_mediator.mm index a3e15172..07d5eac0 100644 --- a/ios/chrome/browser/ui/content_suggestions/ntp_home_mediator.mm +++ b/ios/chrome/browser/ui/content_suggestions/ntp_home_mediator.mm
@@ -33,6 +33,7 @@ #import "ios/chrome/browser/ui/content_suggestions/cells/content_suggestions_item.h" #import "ios/chrome/browser/ui/content_suggestions/cells/content_suggestions_most_visited_action_item.h" #import "ios/chrome/browser/ui/content_suggestions/cells/content_suggestions_most_visited_item.h" +#import "ios/chrome/browser/ui/content_suggestions/cells/content_suggestions_return_to_recent_tab_item.h" #import "ios/chrome/browser/ui/content_suggestions/content_suggestions_alert_factory.h" #import "ios/chrome/browser/ui/content_suggestions/content_suggestions_header_synchronizer.h" #import "ios/chrome/browser/ui/content_suggestions/content_suggestions_mediator.h" @@ -53,6 +54,7 @@ #import "ios/chrome/browser/url_loading/url_loading_browser_agent.h" #import "ios/chrome/browser/url_loading/url_loading_params.h" #import "ios/chrome/browser/voice/voice_search_availability.h" +#import "ios/chrome/browser/web_state_list/web_state_list.h" #import "ios/chrome/common/ui/favicon/favicon_attributes.h" #include "ios/chrome/grit/ios_strings.h" #import "ios/public/provider/chrome/browser/chrome_browser_provider.h" @@ -431,6 +433,17 @@ [self.NTPMetrics recordAction:new_tab_page_uma::ACTION_OPENED_LEARN_MORE]; } +- (void)openMostRecentTab:(CollectionViewItem*)item { + DCHECK([item isKindOfClass:[ContentSuggestionsReturnToRecentTabItem class]]); + [self.suggestionsMediator hideRecentTabTile]; + WebStateList* web_state_list = self.browser->GetWebStateList(); + web::WebState* web_state = + StartSurfaceRecentTabBrowserAgent::FromBrowser(self.browser) + ->most_recent_tab(); + int index = web_state_list->GetIndexOfWebState(web_state); + web_state_list->ActivateWebStateAt(index); +} + #pragma mark - ContentSuggestionsGestureCommands - (void)openNewTabWithSuggestionsItem:(ContentSuggestionsItem*)item
diff --git a/ios/chrome/browser/ui/find_bar/find_in_page_egtest.mm b/ios/chrome/browser/ui/find_bar/find_in_page_egtest.mm index 9de3659..8c3c056 100644 --- a/ios/chrome/browser/ui/find_bar/find_in_page_egtest.mm +++ b/ios/chrome/browser/ui/find_bar/find_in_page_egtest.mm
@@ -130,7 +130,8 @@ // Tests that Find In Page search term retention is working as expected, e.g. // the search term is persisted between FIP runs, but in incognito search term // is not retained and not autofilled. -- (void)testFindInPageRetainsSearchTerm { +// TODO(crbug.com/1188709) : Fix failing test. +- (void)DISABLED_testFindInPageRetainsSearchTerm { // Type "find". [self typeFindInPageText:@"find"]; [self assertResultStringIsResult:1 outOfTotal:2];
diff --git a/ios/chrome/browser/ui/main/scene_controller.mm b/ios/chrome/browser/ui/main/scene_controller.mm index 6b0a46a..a110cc9 100644 --- a/ios/chrome/browser/ui/main/scene_controller.mm +++ b/ios/chrome/browser/ui/main/scene_controller.mm
@@ -91,6 +91,7 @@ #import "ios/chrome/browser/ui/main/ui_blocker_scene_agent.h" #import "ios/chrome/browser/ui/scoped_ui_blocker/scoped_ui_blocker.h" #import "ios/chrome/browser/ui/settings/settings_navigation_controller.h" +#import "ios/chrome/browser/ui/start_surface/start_surface_recent_tab_browser_agent.h" #import "ios/chrome/browser/ui/start_surface/start_surface_scene_agent.h" #import "ios/chrome/browser/ui/start_surface/start_surface_util.h" #include "ios/chrome/browser/ui/tab_switcher/tab_grid/tab_grid_coordinator.h" @@ -559,10 +560,13 @@ if (!ShouldShowStartSurfaceForSceneState(self.sceneState)) { return; } + self.sceneState.modifytVisibleNTPForStartSurface = YES; + Browser* browser = self.currentInterface.browser; + StartSurfaceRecentTabBrowserAgent::FromBrowser(browser)->SaveMostRecentTab(); // Activate the existing NTP tab for the Start surface. - WebStateList* webStateList = self.currentInterface.browser->GetWebStateList(); + WebStateList* webStateList = browser->GetWebStateList(); for (int i = 0; i < webStateList->count(); i++) { if (IsURLNtp(webStateList->GetWebStateAt(i)->GetVisibleURL())) { webStateList->ActivateWebStateAt(i); @@ -574,7 +578,6 @@ OpenNewTabCommand* command = [OpenNewTabCommand commandWithIncognito:self.currentInterface.incognito]; command.userInitiated = NO; - Browser* browser = self.currentInterface.browser; id<ApplicationCommands> applicationHandler = HandlerForProtocol(browser->GetCommandDispatcher(), ApplicationCommands); [applicationHandler openURLInNewTab:command];
diff --git a/ios/chrome/browser/ui/ntp/new_tab_page_coordinator.mm b/ios/chrome/browser/ui/ntp/new_tab_page_coordinator.mm index 9eb1c560..51cf121c 100644 --- a/ios/chrome/browser/ui/ntp/new_tab_page_coordinator.mm +++ b/ios/chrome/browser/ui/ntp/new_tab_page_coordinator.mm
@@ -416,6 +416,9 @@ } - (void)ntpDidChangeVisibility:(BOOL)visible { + if (visible) { + [self.contentSuggestionsCoordinator configureStartSurfaceIfNeeded]; + } self.viewPresented = visible; [self updateVisible]; }
diff --git a/ios/chrome/browser/ui/start_surface/BUILD.gn b/ios/chrome/browser/ui/start_surface/BUILD.gn index 7912b6a..28920a58 100644 --- a/ios/chrome/browser/ui/start_surface/BUILD.gn +++ b/ios/chrome/browser/ui/start_surface/BUILD.gn
@@ -18,6 +18,10 @@ source_set("start_surface") { sources = [ + "start_surface_recent_tab_browser_agent.h", + "start_surface_recent_tab_browser_agent.mm", + "start_surface_recent_tab_removal_observer_bridge.h", + "start_surface_recent_tab_removal_observer_bridge.mm", "start_surface_scene_agent.h", "start_surface_scene_agent.mm", "start_surface_util.h", @@ -29,7 +33,9 @@ deps = [ ":feature_flags", "//base", + "//ios/chrome/browser/ui/main:browser_interface_provider", "//ios/chrome/browser/ui/main:observing_scene_agent", "//ios/chrome/browser/ui/main:scene_state_header", + "//ios/chrome/browser/web_state_list", ] }
diff --git a/ios/chrome/browser/ui/start_surface/start_surface_recent_tab_browser_agent.h b/ios/chrome/browser/ui/start_surface/start_surface_recent_tab_browser_agent.h new file mode 100644 index 0000000..75e4f39 --- /dev/null +++ b/ios/chrome/browser/ui/start_surface/start_surface_recent_tab_browser_agent.h
@@ -0,0 +1,84 @@ +// Copyright 2021 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_START_SURFACE_START_SURFACE_RECENT_TAB_BROWSER_AGENT_H_ +#define IOS_CHROME_BROWSER_UI_START_SURFACE_START_SURFACE_RECENT_TAB_BROWSER_AGENT_H_ + +#include "base/observer_list.h" +#include "ios/chrome/browser/main/browser_observer.h" +#import "ios/chrome/browser/main/browser_user_data.h" +#import "ios/chrome/browser/web_state_list/web_state_list_observer.h" + +namespace web { +class WebState; +} // namespace web + +class Browser; + +// Interface for listening to the removal of the most recent tab. +class StartSurfaceRecentTabRemovalObserver { + public: + StartSurfaceRecentTabRemovalObserver() {} + + // Not copyable or moveable. + StartSurfaceRecentTabRemovalObserver( + const StartSurfaceRecentTabRemovalObserver&) = delete; + StartSurfaceRecentTabRemovalObserver& operator=( + const StartSurfaceRecentTabRemovalObserver&) = delete; + + // Notifies the receiver that the most recent tab was removed. + virtual void MostRecentTabRemoved(web::WebState* web_state) {} + + protected: + virtual ~StartSurfaceRecentTabRemovalObserver() = default; +}; + +// Browser Agent that manages the most recent WebState for the Start Surface and +// listens to WebStateListObserver for instances of that WebState's removal. +class StartSurfaceRecentTabBrowserAgent + : public BrowserUserData<StartSurfaceRecentTabBrowserAgent>, + BrowserObserver, + public WebStateListObserver { + public: + // Notifies the Browser Agent to save the most recent WebState. + void SaveMostRecentTab(); + // Returns the most recent WebState. + web::WebState* most_recent_tab() { return most_recent_tab_; } + // Add/Remove observers for this Browser Agent. + void AddObserver(StartSurfaceRecentTabRemovalObserver* observer); + void RemoveObserver(StartSurfaceRecentTabRemovalObserver* observer); + + ~StartSurfaceRecentTabBrowserAgent() override; + + // Not copyable or moveable. + StartSurfaceRecentTabBrowserAgent(const StartSurfaceRecentTabBrowserAgent&) = + delete; + StartSurfaceRecentTabBrowserAgent& operator=( + const StartSurfaceRecentTabBrowserAgent&) = delete; + + private: + // Constructor used by CreateForBrowser(). + friend class BrowserUserData<StartSurfaceRecentTabBrowserAgent>; + explicit StartSurfaceRecentTabBrowserAgent(Browser* browser); + BROWSER_USER_DATA_KEY_DECL(); + + // BrowserObserver + void BrowserDestroyed(Browser* browser) override; + + // WebStateListObserver: + void WebStateDetachedAt(WebStateList* web_state_list, + web::WebState* web_state, + int index) override; + + // A list of observers notified when the most recent tab is removed. Weak + // references. + base::ObserverList<StartSurfaceRecentTabRemovalObserver, true>::Unchecked + observers_; + // The most recent tab managed by this Browser Agent. + web::WebState* most_recent_tab_ = nullptr; + // Browser. + Browser* browser_ = nullptr; +}; + +#endif // IOS_CHROME_BROWSER_UI_START_SURFACE_START_SURFACE_RECENT_TAB_BROWSER_AGENT_H_
diff --git a/ios/chrome/browser/ui/start_surface/start_surface_recent_tab_browser_agent.mm b/ios/chrome/browser/ui/start_surface/start_surface_recent_tab_browser_agent.mm new file mode 100644 index 0000000..b055a56 --- /dev/null +++ b/ios/chrome/browser/ui/start_surface/start_surface_recent_tab_browser_agent.mm
@@ -0,0 +1,70 @@ +// Copyright 2021 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/ui/start_surface/start_surface_recent_tab_browser_agent.h" + +#import "ios/chrome/browser/ui/main/scene_state_browser_agent.h" +#import "ios/chrome/browser/ui/start_surface/start_surface_util.h" +#import "ios/chrome/browser/web_state_list/web_state_list.h" + +#if !defined(__has_feature) || !__has_feature(objc_arc) +#error "This file requires ARC support." +#endif + +#pragma mark - StartSurfaceBrowserAgent + +BROWSER_USER_DATA_KEY_IMPL(StartSurfaceRecentTabBrowserAgent) + +StartSurfaceRecentTabBrowserAgent::StartSurfaceRecentTabBrowserAgent( + Browser* browser) + : browser_(browser) { + browser_->AddObserver(this); + browser_->GetWebStateList()->AddObserver(this); +} + +StartSurfaceRecentTabBrowserAgent::~StartSurfaceRecentTabBrowserAgent() = + default; + +#pragma mark - Public + +void StartSurfaceRecentTabBrowserAgent::SaveMostRecentTab() { + most_recent_tab_ = browser_->GetWebStateList()->GetActiveWebState(); +} + +void StartSurfaceRecentTabBrowserAgent::AddObserver( + StartSurfaceRecentTabRemovalObserver* observer) { + DCHECK(!observers_.HasObserver(observer)); + observers_.AddObserver(observer); +} + +void StartSurfaceRecentTabBrowserAgent::RemoveObserver( + StartSurfaceRecentTabRemovalObserver* observer) { + observers_.RemoveObserver(observer); +} + +#pragma mark - BrowserObserver + +void StartSurfaceRecentTabBrowserAgent::BrowserDestroyed(Browser* browser) { + browser_->GetWebStateList()->RemoveObserver(this); + browser_->RemoveObserver(this); +} + +#pragma mark - WebStateListObserver + +void StartSurfaceRecentTabBrowserAgent::WebStateDetachedAt( + WebStateList* web_state_list, + web::WebState* web_state, + int index) { + if (!most_recent_tab_) { + return; + } + + if (most_recent_tab_ == web_state) { + for (auto& observer : observers_) { + observer.MostRecentTabRemoved(most_recent_tab_); + } + most_recent_tab_ = nullptr; + return; + } +}
diff --git a/ios/chrome/browser/ui/start_surface/start_surface_recent_tab_removal_observer_bridge.h b/ios/chrome/browser/ui/start_surface/start_surface_recent_tab_removal_observer_bridge.h new file mode 100644 index 0000000..f2859d4 --- /dev/null +++ b/ios/chrome/browser/ui/start_surface/start_surface_recent_tab_removal_observer_bridge.h
@@ -0,0 +1,46 @@ +// Copyright 2021 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_START_SURFACE_START_SURFACE_RECENT_TAB_REMOVAL_OBSERVER_BRIDGE_H_ +#define IOS_CHROME_BROWSER_UI_START_SURFACE_START_SURFACE_RECENT_TAB_REMOVAL_OBSERVER_BRIDGE_H_ + +#import <Foundation/Foundation.h> + +#import "ios/chrome/browser/ui/start_surface/start_surface_recent_tab_browser_agent.h" + +namespace web { +class WebState; +} // namespace web + +// Protocol that corresponds to StartSurfaceRecentTabRemovalObserver API. Allows +// registering Objective-C objects to listen to removal of the most recent tab. +@protocol StartSurfaceRecentTabRemovalObserving <NSObject> +@optional +// Notifies the receiver that the most recent tab was removed. +- (void)mostRecentTabWasRemoved:(web::WebState*)web_state; +@end + +// Bridge to use an id<StartSurfaceRecentTabRemovalObserving> as a +// StartSurfaceRecentTabRemovalObserver. +class StartSurfaceRecentTabRemovalObserverBridge + : public StartSurfaceRecentTabRemovalObserver { + public: + StartSurfaceRecentTabRemovalObserverBridge( + id<StartSurfaceRecentTabRemovalObserving> delegate); + ~StartSurfaceRecentTabRemovalObserverBridge() override; + + // Not copyable or moveable. + StartSurfaceRecentTabRemovalObserverBridge( + const StartSurfaceRecentTabRemovalObserverBridge&) = delete; + StartSurfaceRecentTabRemovalObserverBridge& operator=( + const StartSurfaceRecentTabRemovalObserverBridge&) = delete; + + private: + // StartSurfaceBrowserAgentObserver. + void MostRecentTabRemoved(web::WebState* web_state) override; + + __weak id<StartSurfaceRecentTabRemovalObserving> delegate_ = nil; +}; + +#endif // IOS_CHROME_BROWSER_UI_START_SURFACE_START_SURFACE_RECENT_TAB_REMOVAL_OBSERVER_BRIDGE_H_
diff --git a/ios/chrome/browser/ui/start_surface/start_surface_recent_tab_removal_observer_bridge.mm b/ios/chrome/browser/ui/start_surface/start_surface_recent_tab_removal_observer_bridge.mm new file mode 100644 index 0000000..b26dc032 --- /dev/null +++ b/ios/chrome/browser/ui/start_surface/start_surface_recent_tab_removal_observer_bridge.mm
@@ -0,0 +1,26 @@ +// Copyright 2021 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/ui/start_surface/start_surface_recent_tab_removal_observer_bridge.h" + +#if !defined(__has_feature) || !__has_feature(objc_arc) +#error "This file requires ARC support." +#endif + +StartSurfaceRecentTabRemovalObserverBridge:: + StartSurfaceRecentTabRemovalObserverBridge( + id<StartSurfaceRecentTabRemovalObserving> delegate) + : delegate_(delegate) {} + +StartSurfaceRecentTabRemovalObserverBridge:: + ~StartSurfaceRecentTabRemovalObserverBridge() = default; + +void StartSurfaceRecentTabRemovalObserverBridge::MostRecentTabRemoved( + web::WebState* web_state) { + const SEL selector = @selector(mostRecentTabWasRemoved:); + if (![delegate_ respondsToSelector:selector]) + return; + + [delegate_ mostRecentTabWasRemoved:web_state]; +}
diff --git a/ios/showcase/content_suggestions/sc_content_suggestions_data_source.mm b/ios/showcase/content_suggestions/sc_content_suggestions_data_source.mm index 572f7e0..1662a84 100644 --- a/ios/showcase/content_suggestions/sc_content_suggestions_data_source.mm +++ b/ios/showcase/content_suggestions/sc_content_suggestions_data_source.mm
@@ -120,6 +120,7 @@ return @[ learnMore ]; } case ContentSuggestionsSectionLogo: + case ContentSuggestionsSectionReturnToRecentTab: case ContentSuggestionsSectionPromo: case ContentSuggestionsSectionDiscover: case ContentSuggestionsSectionUnknown:
diff --git a/ipc/ipc_message_utils.cc b/ipc/ipc_message_utils.cc index f9477ed..473fb14 100644 --- a/ipc/ipc_message_utils.cc +++ b/ipc/ipc_message_utils.cc
@@ -11,7 +11,6 @@ #include "base/json/json_writer.h" #include "base/logging.h" #include "base/memory/ptr_util.h" -#include "base/strings/nullable_string16.h" #include "base/strings/string_number_conversions.h" #include "base/strings/utf_string_conversions.h" #include "base/time/time.h" @@ -1205,34 +1204,6 @@ l->append(json); } -void ParamTraits<base::NullableString16>::Write(base::Pickle* m, - const param_type& p) { - WriteParam(m, p.string()); - WriteParam(m, p.is_null()); -} - -bool ParamTraits<base::NullableString16>::Read(const base::Pickle* m, - base::PickleIterator* iter, - param_type* r) { - std::u16string string; - if (!ReadParam(m, iter, &string)) - return false; - bool is_null; - if (!ReadParam(m, iter, &is_null)) - return false; - *r = base::NullableString16(string, is_null); - return true; -} - -void ParamTraits<base::NullableString16>::Log(const param_type& p, - std::string* l) { - l->append("("); - LogParam(p.string(), l); - l->append(", "); - LogParam(p.is_null(), l); - l->append(")"); -} - void ParamTraits<base::File::Info>::Write(base::Pickle* m, const param_type& p) { WriteParam(m, p.size);
diff --git a/ipc/ipc_message_utils.h b/ipc/ipc_message_utils.h index 413bf5c..97e7f0a 100644 --- a/ipc/ipc_message_utils.h +++ b/ipc/ipc_message_utils.h
@@ -52,7 +52,6 @@ class DictionaryValue; class FilePath; class ListValue; -class NullableString16; class Time; class TimeDelta; class TimeTicks; @@ -762,16 +761,6 @@ }; template <> -struct COMPONENT_EXPORT(IPC) ParamTraits<base::NullableString16> { - typedef base::NullableString16 param_type; - static void Write(base::Pickle* m, const param_type& p); - static bool Read(const base::Pickle* m, - base::PickleIterator* iter, - param_type* r); - static void Log(const param_type& p, std::string* l); -}; - -template <> struct COMPONENT_EXPORT(IPC) ParamTraits<base::File::Info> { typedef base::File::Info param_type; static void Write(base::Pickle* m, const param_type& p);
diff --git a/media/base/android/java/src/org/chromium/media/MediaCodecUtil.java b/media/base/android/java/src/org/chromium/media/MediaCodecUtil.java index 8410ecf5..a7cfe41 100644 --- a/media/base/android/java/src/org/chromium/media/MediaCodecUtil.java +++ b/media/base/android/java/src/org/chromium/media/MediaCodecUtil.java
@@ -419,7 +419,13 @@ // MediaTek decoders do not work properly on vp8. See http://crbug.com/446974 and // http://crbug.com/597836. - if (Build.HARDWARE.startsWith("mt")) return false; + if (Build.HARDWARE.startsWith("mt")) { + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.P) return false; + + // The following chipsets have been confirmed by MediaTek to work on P+ + return Build.HARDWARE.startsWith("mt5599") || Build.HARDWARE.startsWith("mt5895") + || Build.HARDWARE.startsWith("m7332"); + } } else if (mime.equals(MimeTypes.VIDEO_VP9)) { // Nexus Player VP9 decoder performs poorly at >= 1080p resolution. if (Build.MODEL.equals("Nexus Player")) {
diff --git a/media/base/android/media_codec_util.cc b/media/base/android/media_codec_util.cc index ab1975a..b917029 100644 --- a/media/base/android/media_codec_util.cc +++ b/media/base/android/media_codec_util.cc
@@ -31,6 +31,7 @@ using base::android::SDK_VERSION_KITKAT; using base::android::SDK_VERSION_LOLLIPOP; using base::android::SDK_VERSION_LOLLIPOP_MR1; +using base::android::SDK_VERSION_P; namespace media { @@ -385,12 +386,17 @@ // MediaTek hardware vp9 is known crashy, see http://crbug.com/446974 and // http://crbug.com/597836. if (base::StartsWith(codec_name, "OMX.MTK.", base::CompareCase::SENSITIVE)) { - if (codec == kCodecVP8) - return true; + if (codec == kCodecVP8) { + // We may still reject VP8 hardware decoding later on certain chipsets, + // see isDecoderSupportedForDevice(). We don't have the the chipset ID + // here to check now though. + return base::android::BuildInfo::GetInstance()->sdk_int() < SDK_VERSION_P; + } - if (codec == kCodecVP9) + if (codec == kCodecVP9) { return base::android::BuildInfo::GetInstance()->sdk_int() < SDK_VERSION_LOLLIPOP; + } return false; }
diff --git a/media/base/media_switches.cc b/media/base/media_switches.cc index 21c30667..ac2bd4b 100644 --- a/media/base/media_switches.cc +++ b/media/base/media_switches.cc
@@ -836,6 +836,10 @@ const base::Feature kMediaPowerExperiment{"MediaPowerExperiment", base::FEATURE_DISABLED_BY_DEFAULT}; +// Enable WebRTC actions for the Media Session API. +const base::Feature kMediaSessionWebRTC{"MediaSessionWebRTC", + base::FEATURE_DISABLED_BY_DEFAULT}; + // Enables flash to be ducked by audio focus. This is enabled on Chrome OS which // has audio focus enabled. const base::Feature kAudioFocusDuckFlash {
diff --git a/media/base/media_switches.h b/media/base/media_switches.h index 013babd..2955055 100644 --- a/media/base/media_switches.h +++ b/media/base/media_switches.h
@@ -164,6 +164,7 @@ MEDIA_EXPORT extern const base::Feature kMediaLearningSmoothnessExperiment; MEDIA_EXPORT extern const base::Feature kMediaOptimizer; MEDIA_EXPORT extern const base::Feature kMediaPowerExperiment; +MEDIA_EXPORT extern const base::Feature kMediaSessionWebRTC; MEDIA_EXPORT extern const base::Feature kMemoryPressureBasedSourceBufferGC; MEDIA_EXPORT extern const base::Feature kOverlayFullscreenVideo; MEDIA_EXPORT extern const base::Feature kPictureInPicture;
diff --git a/media/gpu/vaapi/vaapi_video_decoder.cc b/media/gpu/vaapi/vaapi_video_decoder.cc index 8800452..e04ae1a 100644 --- a/media/gpu/vaapi/vaapi_video_decoder.cc +++ b/media/gpu/vaapi/vaapi_video_decoder.cc
@@ -163,7 +163,8 @@ DVLOGF(2) << config.AsHumanReadableString(); DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); DCHECK(config.IsValidConfig()); - DCHECK(state_ == State::kUninitialized || state_ == State::kWaitingForInput); + DCHECK(state_ == State::kError || state_ == State::kUninitialized || + state_ == State::kWaitingForInput); // Reinitializing the decoder is allowed if there are no pending decodes. if (current_decode_task_ || !decode_task_queue_.empty()) { @@ -173,43 +174,6 @@ return; } - if (config.is_encrypted()) { -#if !BUILDFLAG(IS_CHROMEOS_ASH) - std::move(init_cb).Run(StatusCode::kEncryptedContentUnsupported); - return; -#else - if (!cdm_context || !cdm_context->GetChromeOsCdmContext()) { - LOG(ERROR) << "Cannot support encrypted stream w/out ChromeOsCdmContext"; - std::move(init_cb).Run(StatusCode::kDecoderMissingCdmForEncryptedContent); - return; - } - if (config.codec() != kCodecH264 && config.codec() != kCodecVP9 && - config.codec() != kCodecHEVC) { - VLOGF(1) - << "Vaapi decoder does not support this codec for encrypted content"; - std::move(init_cb).Run(StatusCode::kEncryptedContentUnsupported); - return; - } - cdm_event_cb_registration_ = cdm_context->RegisterEventCB( - base::BindRepeating(&VaapiVideoDecoder::OnCdmContextEvent, - weak_this_factory_.GetWeakPtr())); - cdm_context_ref_ = cdm_context->GetChromeOsCdmContext()->GetCdmContextRef(); -#endif -#if BUILDFLAG(ENABLE_PLATFORM_HEVC) - } else if (config.codec() == kCodecHEVC && - !base::CommandLine::ForCurrentProcess()->HasSwitch( - switches::kEnableClearHevcForTesting)) { - DVLOG(1) << "Clear HEVC content is not supported"; - std::move(init_cb).Run(StatusCode::kClearContentUnsupported); - return; -#endif - } - - // We expect the decoder to have released all output buffers (by the client - // triggering a flush or reset), even if the - // DecoderInterface API doesn't explicitly specify this. - DCHECK(output_frames_.empty()); - if (state_ != State::kUninitialized) { DVLOGF(3) << "Reinitializing decoder"; @@ -230,8 +194,62 @@ DCHECK(vaapi_wrapper_->HasOneRef()); vaapi_wrapper_ = nullptr; decoder_delegate_ = nullptr; + +#if BUILDFLAG(IS_CHROMEOS_ASH) + // |cdm_context_ref_| is reset after |decoder_| because we passed + // |cdm_context_ref_->GetCdmContext()| when creating the |decoder_|, so we + // don't want |decoder_| to have a dangling pointer. We also destroy + // |cdm_event_cb_registration_| before |cdm_context_ref_| so that we have a + // CDM at the moment of destroying the callback registration. + cdm_event_cb_registration_ = nullptr; + cdm_context_ref_ = nullptr; +#endif + SetState(State::kUninitialized); } + DCHECK(!current_decode_task_); + DCHECK(decode_task_queue_.empty()); + + // Destroying the |decoder_| during re-initialization should release all + // output buffers (and there should be no output buffers to begin with if the + // decoder was previously uninitialized). + DCHECK(output_frames_.empty()); + + if (config.is_encrypted()) { +#if !BUILDFLAG(IS_CHROMEOS_ASH) + SetState(State::kError); + std::move(init_cb).Run(StatusCode::kEncryptedContentUnsupported); + return; +#else + if (!cdm_context || !cdm_context->GetChromeOsCdmContext()) { + LOG(ERROR) << "Cannot support encrypted stream w/out ChromeOsCdmContext"; + SetState(State::kError); + std::move(init_cb).Run(StatusCode::kDecoderMissingCdmForEncryptedContent); + return; + } + if (config.codec() != kCodecH264 && config.codec() != kCodecVP9 && + config.codec() != kCodecHEVC) { + VLOGF(1) + << "Vaapi decoder does not support this codec for encrypted content"; + SetState(State::kError); + std::move(init_cb).Run(StatusCode::kEncryptedContentUnsupported); + return; + } + cdm_event_cb_registration_ = cdm_context->RegisterEventCB( + base::BindRepeating(&VaapiVideoDecoder::OnCdmContextEvent, + weak_this_factory_.GetWeakPtr())); + cdm_context_ref_ = cdm_context->GetChromeOsCdmContext()->GetCdmContextRef(); +#endif +#if BUILDFLAG(ENABLE_PLATFORM_HEVC) + } else if (config.codec() == kCodecHEVC && + !base::CommandLine::ForCurrentProcess()->HasSwitch( + switches::kEnableClearHevcForTesting)) { + DVLOG(1) << "Clear HEVC content is not supported"; + SetState(State::kError); + std::move(init_cb).Run(StatusCode::kClearContentUnsupported); + return; +#endif + } // Initialize VAAPI wrapper. const VideoCodecProfile profile = config.profile(); @@ -250,6 +268,7 @@ if (!vaapi_wrapper_.get()) { VLOGF(1) << "Failed initializing VAAPI for profile " << GetProfileName(profile); + SetState(State::kError); std::move(init_cb).Run(StatusCode::kDecoderUnsupportedProfile); return; } @@ -259,6 +278,7 @@ encryption_scheme_ = config.encryption_scheme(); auto accel_status = CreateAcceleratedVideoDecoder(); if (!accel_status.is_ok()) { + SetState(State::kError); std::move(init_cb).Run(std::move(accel_status)); return; } @@ -994,7 +1014,7 @@ // Check whether the state change is valid. switch (state) { case State::kUninitialized: - DCHECK_EQ(state_, State::kWaitingForInput); + DCHECK(state_ == State::kWaitingForInput || state_ == State::kError); break; case State::kWaitingForInput: DCHECK(decode_task_queue_.empty());
diff --git a/net/base/features.cc b/net/base/features.cc index 0fdfedf6..884efbe4 100644 --- a/net/base/features.cc +++ b/net/base/features.cc
@@ -141,6 +141,8 @@ #if defined(OS_MAC) const base::FeatureParam<int> kCertVerifierBuiltinImpl{ &kCertVerifierBuiltinFeature, "impl", 0}; +const base::FeatureParam<int> kCertVerifierBuiltinCacheSize{ + &kCertVerifierBuiltinFeature, "cachesize", 0}; #endif /* defined(OS_MAC) */ #endif @@ -152,6 +154,8 @@ #if defined(OS_MAC) const base::FeatureParam<int> kCertDualVerificationTrialImpl{ &kCertDualVerificationTrialFeature, "impl", 0}; +const base::FeatureParam<int> kCertDualVerificationTrialCacheSize{ + &kCertDualVerificationTrialFeature, "cachesize", 0}; #endif /* defined(OS_MAC) */ #endif
diff --git a/net/base/features.h b/net/base/features.h index 7023f41..fe39446 100644 --- a/net/base/features.h +++ b/net/base/features.h
@@ -218,6 +218,7 @@ NET_EXPORT extern const base::Feature kCertVerifierBuiltinFeature; #if defined(OS_MAC) NET_EXPORT extern const base::FeatureParam<int> kCertVerifierBuiltinImpl; +NET_EXPORT extern const base::FeatureParam<int> kCertVerifierBuiltinCacheSize; #endif /* defined(OS_MAC) */ #endif /* BUILDFLAG(BUILTIN_CERT_VERIFIER_FEATURE_SUPPORTED) */ @@ -225,6 +226,8 @@ NET_EXPORT extern const base::Feature kCertDualVerificationTrialFeature; #if defined(OS_MAC) NET_EXPORT extern const base::FeatureParam<int> kCertDualVerificationTrialImpl; +NET_EXPORT extern const base::FeatureParam<int> + kCertDualVerificationTrialCacheSize; #endif /* defined(OS_MAC) */ #endif /* BUILDFLAG(BUILTIN_CERT_VERIFIER_FEATURE_SUPPORTED) */
diff --git a/net/cert/internal/system_trust_store.cc b/net/cert/internal/system_trust_store.cc index 21b3123..326ef5c 100644 --- a/net/cert/internal/system_trust_store.cc +++ b/net/cert/internal/system_trust_store.cc
@@ -210,9 +210,23 @@ return kDefaultTrustImpl; } + static size_t GetTrustStoreCacheSize() { + if (base::FeatureList::IsEnabled(features::kCertVerifierBuiltinFeature) && + features::kCertVerifierBuiltinCacheSize.Get() > 0) { + return features::kCertVerifierBuiltinCacheSize.Get(); + } + if (base::FeatureList::IsEnabled( + features::kCertDualVerificationTrialFeature) && + features::kCertDualVerificationTrialCacheSize.Get() > 0) { + return features::kCertDualVerificationTrialCacheSize.Get(); + } + constexpr size_t kDefaultCacheSize = 512; + return kDefaultCacheSize; + } + static TrustStoreMac* GetGlobalTrustStoreMac() { static base::NoDestructor<TrustStoreMac> static_trust_store_mac( - kSecPolicyAppleSSL, GetTrustStoreImplParam()); + kSecPolicyAppleSSL, GetTrustStoreImplParam(), GetTrustStoreCacheSize()); return static_trust_store_mac.get(); } };
diff --git a/net/cert/internal/trust_store_mac.cc b/net/cert/internal/trust_store_mac.cc index dffc3474..e4c362f5 100644 --- a/net/cert/internal/trust_store_mac.cc +++ b/net/cert/internal/trust_store_mac.cc
@@ -756,8 +756,9 @@ DISALLOW_COPY_AND_ASSIGN(TrustImplMRUCache); }; -TrustStoreMac::TrustStoreMac(CFStringRef policy_oid, TrustImplType impl) { - constexpr size_t cache_size = 512; // TODO(mattm): make this a param. +TrustStoreMac::TrustStoreMac(CFStringRef policy_oid, + TrustImplType impl, + size_t cache_size) { switch (impl) { case TrustImplType::kUnknown: DCHECK(false);
diff --git a/net/cert/internal/trust_store_mac.h b/net/cert/internal/trust_store_mac.h index 92a2386..69e4a8e 100644 --- a/net/cert/internal/trust_store_mac.h +++ b/net/cert/internal/trust_store_mac.h
@@ -105,8 +105,9 @@ // |policy_oid|. For list of possible policy values, see: // https://developer.apple.com/reference/security/1667150-certificate_key_and_trust_servic/1670151-standard_policies_for_specific_c?language=objc // |impl| selects which internal implementation is used for checking trust - // settings. - TrustStoreMac(CFStringRef policy_oid, TrustImplType impl); + // settings, and the interpretation of |cache_size| varies depending on + // |impl|. + TrustStoreMac(CFStringRef policy_oid, TrustImplType impl, size_t cache_size); ~TrustStoreMac() override; // Initializes the trust cache, if it isn't already initialized.
diff --git a/net/cert/internal/trust_store_mac_unittest.cc b/net/cert/internal/trust_store_mac_unittest.cc index a62b85a..bfe7301 100644 --- a/net/cert/internal/trust_store_mac_unittest.cc +++ b/net/cert/internal/trust_store_mac_unittest.cc
@@ -30,6 +30,8 @@ namespace { +constexpr size_t kDefaultCacheSize = 512; + // The PEM block header used for DER certificates const char kCertificateHeader[] = "CERTIFICATE"; @@ -120,7 +122,8 @@ test_keychain_search_list->AddKeychain(keychain); TrustStoreMac trust_store(kSecPolicyAppleSSL, - TrustStoreMac::TrustImplType::kDomainCache); + TrustStoreMac::TrustImplType::kDomainCache, + kDefaultCacheSize); scoped_refptr<ParsedCertificate> a_by_b, b_by_c, b_by_f, c_by_d, c_by_e, f_by_e, d_by_d, e_by_e; @@ -237,7 +240,8 @@ "/System/Library/Keychains/SystemRootCertificates.keychain"}, &find_certificate_system_roots_output)); - TrustStoreMac trust_store(kSecPolicyAppleX509Basic, GetParam()); + TrustStoreMac trust_store(kSecPolicyAppleX509Basic, GetParam(), + kDefaultCacheSize); base::ScopedCFTypeRef<SecPolicyRef> sec_policy(SecPolicyCreateBasicX509()); ASSERT_TRUE(sec_policy);
diff --git a/net/spdy/alps_decoder.cc b/net/spdy/alps_decoder.cc index 24e07973..56d8305 100644 --- a/net/spdy/alps_decoder.cc +++ b/net/spdy/alps_decoder.cc
@@ -44,6 +44,10 @@ return Error::kForbiddenFrame; } + if (settings_parser_.settings_ack_received()) { + return Error::kSettingsWithAck; + } + if (decoder_adapter_.state() != http2::Http2DecoderAdapter::SPDY_READY_FOR_FRAME) { return Error::kNotOnFrameBoundary; @@ -85,6 +89,10 @@ settings_[id] = value; } +void AlpsDecoder::SettingsParser::OnSettingsAck() { + settings_ack_received_ = true; +} + AlpsDecoder::AcceptChParser::AcceptChParser() = default; AlpsDecoder::AcceptChParser::~AcceptChParser() = default;
diff --git a/net/spdy/alps_decoder.h b/net/spdy/alps_decoder.h index 4332fd6..fd62d3a 100644 --- a/net/spdy/alps_decoder.h +++ b/net/spdy/alps_decoder.h
@@ -72,6 +72,7 @@ ~SettingsParser() override; bool forbidden_frame_received() const { return forbidden_frame_received_; } + bool settings_ack_received() const { return settings_ack_received_; } int settings_frame_count() const { return settings_frame_count_; } // Number of SETTINGS frames received. const spdy::SettingsMap& GetSettings() const { return settings_; } @@ -83,10 +84,13 @@ uint8_t flags) override; void OnSettings() override; void OnSetting(spdy::SpdySettingsId id, uint32_t value) override; + void OnSettingsAck() override; private: // True if a forbidden HTTP/2 frame has been received. bool forbidden_frame_received_ = false; + // True if a SETTINGS frame with ACK flag has been received. + bool settings_ack_received_ = false; // Number of SETTINGS frames received. int settings_frame_count_ = 0; // Accumulated setting parameters.
diff --git a/net/spdy/alps_decoder_test.cc b/net/spdy/alps_decoder_test.cc index c0ad4af..1c30710 100644 --- a/net/spdy/alps_decoder_test.cc +++ b/net/spdy/alps_decoder_test.cc
@@ -189,6 +189,17 @@ EXPECT_EQ(AlpsDecoder::Error::kFramingError, error); } +TEST(AlpsDecoderTest, SettingsAck) { + AlpsDecoder decoder; + AlpsDecoder::Error error = + decoder.Decode(HexDecode("000000" // length + "04" // type SETTINGS + "01" // ACK flag + "00000000")); // stream ID + + EXPECT_EQ(AlpsDecoder::Error::kSettingsWithAck, error); +} + // According to https://httpwg.org/specs/rfc7540.html#FrameHeader: // "Flags that have no defined semantics for a particular frame type MUST be // ignored [...]"
diff --git a/remoting/host/BUILD.gn b/remoting/host/BUILD.gn index b986097..ec797a60 100644 --- a/remoting/host/BUILD.gn +++ b/remoting/host/BUILD.gn
@@ -26,7 +26,7 @@ deps += [ ":remoting_host_branded" ] } - if (!is_chromeos_ash && !is_chromeos_lacros && !is_android && !is_ios) { + if (!is_chromeos_ash && !is_android && !is_ios) { deps += [ "//remoting/host:remoting_native_messaging_host", "//remoting/host:remoting_native_messaging_manifests",
diff --git a/remoting/host/clipboard_x11.cc b/remoting/host/clipboard_x11.cc index e997fcb..d237d30 100644 --- a/remoting/host/clipboard_x11.cc +++ b/remoting/host/clipboard_x11.cc
@@ -42,9 +42,9 @@ // Underlying X11 clipboard implementation. XServerClipboard x_server_clipboard_; - // Connection to the X server, used by |x_server_clipboard_|. This is created - // and owned by this class. - std::unique_ptr<x11::Connection> connection_; + // Connection to the X server, used by |x_server_clipboard_|. This must only + // be accessed on the input thread. + x11::Connection* connection_; // Watcher used to handle X11 events from |display_|. std::unique_ptr<base::FileDescriptorWatcher::Controller> @@ -64,19 +64,13 @@ void ClipboardX11::Start( std::unique_ptr<protocol::ClipboardStub> client_clipboard) { - // TODO(lambroslambrou): Share the X connection with InputInjector. - DCHECK(!connection_); - connection_ = std::make_unique<x11::Connection>(); - if (!connection_->Ready()) { - LOG(ERROR) << "Couldn't open X display"; - return; - } + connection_ = x11::Connection::Get(); connection_->AddEventObserver(this); client_clipboard_.swap(client_clipboard); x_server_clipboard_.Init( - connection_.get(), base::BindRepeating(&ClipboardX11::OnClipboardChanged, - base::Unretained(this))); + connection_, base::BindRepeating(&ClipboardX11::OnClipboardChanged, + base::Unretained(this))); x_connection_watch_controller_ = base::FileDescriptorWatcher::WatchReadable( connection_->GetFd(),
diff --git a/remoting/host/input_injector_x11.cc b/remoting/host/input_injector_x11.cc index 9524616d..d6d7e691 100644 --- a/remoting/host/input_injector_x11.cc +++ b/remoting/host/input_injector_x11.cc
@@ -89,7 +89,7 @@ scoped_refptr<base::SingleThreadTaskRunner> task_runner); ~InputInjectorX11() override; - bool Init(); + void Init(); // Clipboard stub interface. void InjectClipboardEvent(const ClipboardEvent& event) override; @@ -110,7 +110,7 @@ public: explicit Core(scoped_refptr<base::SingleThreadTaskRunner> task_runner); - bool Init(); + void Init(); // Mirrors the ClipboardStub interface. void InjectClipboardEvent(const ClipboardEvent& event); @@ -167,8 +167,8 @@ // "tick" being injected. ScrollDirection latest_tick_y_direction_ = ScrollDirection::NONE; - // X11 graphics context. - x11::Connection connection_; + // X11 graphics context. Must only be accessed on the input thread. + x11::Connection* connection_; // Number of buttons we support. // Left, Right, Middle, VScroll Up/Down, HScroll Left/Right, back, forward. @@ -203,8 +203,8 @@ core_->Stop(); } -bool InputInjectorX11::Init() { - return core_->Init(); +void InputInjectorX11::Init() { + core_->Init(); } void InputInjectorX11::InjectClipboardEvent(const ClipboardEvent& event) { @@ -236,19 +236,17 @@ scoped_refptr<base::SingleThreadTaskRunner> task_runner) : task_runner_(task_runner) {} -bool InputInjectorX11::Core::Init() { - CHECK(connection_.Ready()); - - if (!task_runner_->BelongsToCurrentThread()) - task_runner_->PostTask(FROM_HERE, - base::BindOnce(&Core::InitClipboard, this)); - - if (!IgnoreXServerGrabs(&connection_, true)) { - LOG(ERROR) << "Server does not support XTest."; - return false; +void InputInjectorX11::Core::Init() { + if (!task_runner_->BelongsToCurrentThread()) { + task_runner_->PostTask(FROM_HERE, base::BindOnce(&Core::Init, this)); + return; } - InitMouseButtonMap(); - return true; + + connection_ = x11::Connection::Get(); + if (!IgnoreXServerGrabs(connection_, true)) { + LOG(ERROR) << "XTEST not supported, cannot inject key/mouse events."; + } + InitClipboard(); } void InputInjectorX11::Core::InjectClipboardEvent(const ClipboardEvent& event) { @@ -273,6 +271,10 @@ return; } + if (!connection_->xtest().present()) { + return; + } + int keycode = ui::KeycodeConverter::UsbKeycodeToNativeKeycode(event.usb_keycode()); @@ -290,7 +292,7 @@ return; // Key is already held down, so lift the key up to ensure this repeated // press takes effect. - connection_.xtest().FakeInput({x11::KeyEvent::Release, keycode}); + connection_->xtest().FakeInput({x11::KeyEvent::Release, keycode}); } if (!IsLockKey(static_cast<x11::KeyCode>(keycode))) { @@ -328,8 +330,8 @@ } auto opcode = event.pressed() ? x11::KeyEvent::Press : x11::KeyEvent::Release; - connection_.xtest().FakeInput({opcode, keycode}); - connection_.Flush(); + connection_->xtest().FakeInput({opcode, keycode}); + connection_->Flush(); } void InputInjectorX11::Core::InjectTextEvent(const TextEvent& event) { @@ -339,11 +341,15 @@ return; } + if (!connection_->xtest().present()) { + return; + } + // Release all keys before injecting text event. This is necessary to avoid // any interference with the currently pressed keys. E.g. if Shift is pressed // when TextEvent is received. for (int key : pressed_keys_) - connection_.xtest().FakeInput({x11::KeyEvent::Release, key}); + connection_->xtest().FakeInput({x11::KeyEvent::Release, key}); pressed_keys_.clear(); const std::string text = event.text(); @@ -367,26 +373,26 @@ } bool InputInjectorX11::Core::IsAutoRepeatEnabled() { - if (auto reply = connection_.GetKeyboardControl().Sync()) + if (auto reply = connection_->GetKeyboardControl().Sync()) return reply->global_auto_repeat == x11::AutoRepeatMode::On; LOG(ERROR) << "Failed to get keyboard auto-repeat status, assuming ON."; return true; } void InputInjectorX11::Core::SetAutoRepeatEnabled(bool mode) { - connection_.ChangeKeyboardControl( + connection_->ChangeKeyboardControl( {.auto_repeat_mode = mode ? x11::AutoRepeatMode::On : x11::AutoRepeatMode::Off}); - connection_.Flush(); + connection_->Flush(); } bool InputInjectorX11::Core::IsLockKey(x11::KeyCode keycode) { - auto state = connection_.xkb().GetState().Sync(); + auto state = connection_->xkb().GetState().Sync(); if (!state) return false; auto mods = state->baseMods | state->latchedMods | state->lockedMods; auto keysym = - connection_.KeycodeToKeysym(keycode, static_cast<unsigned>(mods)); + connection_->KeycodeToKeysym(keycode, static_cast<unsigned>(mods)); if (state && keysym) return keysym == XK_Caps_Lock || keysym == XK_Num_Lock; else @@ -417,7 +423,7 @@ } if (update_mask) { - connection_.xkb().LatchLockState( + connection_->xkb().LatchLockState( {static_cast<x11::Xkb::DeviceSpec>(x11::Xkb::Id::UseCoreKbd), static_cast<x11::ModMask>(update_mask), static_cast<x11::ModMask>(lock_values)}); @@ -425,14 +431,20 @@ } void InputInjectorX11::Core::InjectScrollWheelClicks(int button, int count) { + DCHECK(task_runner_->BelongsToCurrentThread()); + + if (!connection_->xtest().present()) { + return; + } + if (button < 0) { LOG(WARNING) << "Ignoring unmapped scroll wheel button"; return; } for (int i = 0; i < count; i++) { // Generate a button-down and a button-up to simulate a wheel click. - connection_.xtest().FakeInput({x11::ButtonEvent::Press, button}); - connection_.xtest().FakeInput({x11::ButtonEvent::Release, button}); + connection_->xtest().FakeInput({x11::ButtonEvent::Press, button}); + connection_->xtest().FakeInput({x11::ButtonEvent::Release, button}); } } @@ -443,11 +455,15 @@ return; } + if (!connection_->xtest().present()) { + return; + } + if (event.has_delta_x() && event.has_delta_y() && (event.delta_x() != 0 || event.delta_y() != 0)) { latest_mouse_position_.set(-1, -1); VLOG(3) << "Moving mouse by " << event.delta_x() << "," << event.delta_y(); - connection_.xtest().FakeInput({ + connection_->xtest().FakeInput({ .type = x11::MotionNotifyEvent::opcode, .detail = true, .rootX = event.delta_x(), @@ -478,10 +494,10 @@ VLOG(3) << "Moving mouse to " << latest_mouse_position_.x() << "," << latest_mouse_position_.y(); - connection_.xtest().FakeInput({ + connection_->xtest().FakeInput({ .type = x11::MotionNotifyEvent::opcode, .detail = false, - .root = connection_.default_root(), + .root = connection_->default_root(), .rootX = latest_mouse_position_.x(), .rootY = latest_mouse_position_.y(), }); @@ -500,7 +516,7 @@ << (event.button_down() ? "down " : "up ") << button_number; auto opcode = event.button_down() ? x11::ButtonEvent::Press : x11::ButtonEvent::Release; - connection_.xtest().FakeInput({opcode, button_number}); + connection_->xtest().FakeInput({opcode, button_number}); } // remotedesktop.google.com currently sends scroll events in pixels, which @@ -569,10 +585,12 @@ abs(ticks_x)); } - connection_.Flush(); + connection_->Flush(); } void InputInjectorX11::Core::InitMouseButtonMap() { + DCHECK(task_runner_->BelongsToCurrentThread()); + // TODO(rmsousa): Run this on global/device mapping change events. // Do not touch global pointer mapping, since this may affect the local user. @@ -580,7 +598,7 @@ // Note that if a user has a global mapping that completely disables a button // (by assigning 0 to it), we won't be able to inject it. std::vector<uint8_t> pointer_mapping; - if (auto reply = connection_.GetPointerMapping().Sync()) + if (auto reply = connection_->GetPointerMapping().Sync()) pointer_mapping = std::move(reply->map); for (int& i : pointer_button_map_) i = -1; @@ -594,7 +612,7 @@ LOG(ERROR) << "Global pointer mapping does not support button " << i + 1; } - if (!connection_.QueryExtension("XInputExtension").Sync()) { + if (!connection_->QueryExtension("XInputExtension").Sync()) { // If XInput is not available, we're done. But it would be very unusual to // have a server that supports XTest but not XInput, so log it as an error. LOG(ERROR) << "X Input extension not available"; @@ -607,7 +625,7 @@ // may have mistakenly applied left-handed preferences to the XTEST device. uint8_t device_id = 0; bool device_found = false; - if (auto devices = connection_.xinput().ListInputDevices().Sync()) { + if (auto devices = connection_->xinput().ListInputDevices().Sync()) { for (size_t i = 0; i < devices->devices.size(); i++) { const auto& device_info = devices->devices[i]; const std::string& name = devices->names[i].name; @@ -626,27 +644,27 @@ return; } - auto device = connection_.xinput().OpenDevice({device_id}).Sync(); + auto device = connection_->xinput().OpenDevice({device_id}).Sync(); if (!device) { LOG(ERROR) << "Cannot open XTest device."; return; } if (auto mapping = - connection_.xinput().GetDeviceButtonMapping({device_id}).Sync()) { + connection_->xinput().GetDeviceButtonMapping({device_id}).Sync()) { size_t num_device_buttons = mapping->map.size(); std::vector<uint8_t> new_mapping; for (size_t i = 0; i < num_device_buttons; i++) new_mapping.push_back(i + 1); - if (!connection_.xinput() + if (!connection_->xinput() .SetDeviceButtonMapping({device_id, new_mapping}) .Sync()) { LOG(ERROR) << "Failed to set XTest device button mapping"; } } - connection_.xinput().CloseDevice({device_id}); - connection_.Flush(); + connection_->xinput().CloseDevice({device_id}); + connection_->Flush(); } int InputInjectorX11::Core::MouseButtonToX11ButtonNumber( @@ -692,7 +710,7 @@ clipboard_->Start(std::move(client_clipboard)); character_injector_ = std::make_unique<X11CharacterInjector>( - std::make_unique<X11KeyboardImpl>(&connection_)); + std::make_unique<X11KeyboardImpl>(connection_)); // Disable auto-repeat, if necessary, to avoid triggering auto-repeat // if network congestion delays the key-up event from the client. This is @@ -722,10 +740,8 @@ std::unique_ptr<InputInjector> InputInjector::Create( scoped_refptr<base::SingleThreadTaskRunner> main_task_runner, scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner) { - std::unique_ptr<InputInjectorX11> injector( - new InputInjectorX11(main_task_runner)); - if (!injector->Init()) - return nullptr; + auto injector = std::make_unique<InputInjectorX11>(main_task_runner); + injector->Init(); return std::move(injector); }
diff --git a/remoting/host/it2me/BUILD.gn b/remoting/host/it2me/BUILD.gn index 1a82d84..f252c618 100644 --- a/remoting/host/it2me/BUILD.gn +++ b/remoting/host/it2me/BUILD.gn
@@ -72,32 +72,20 @@ } } -if (is_chromeos_ash || is_chromeos_lacros) { +if (is_chromeos_ash) { source_set("chrome_os_host") { sources = [ - "it2me_native_messaging_host_allowed_origins.cc", - "it2me_native_messaging_host_allowed_origins.h", + "it2me_native_messaging_host_chromeos.cc", + "it2me_native_messaging_host_chromeos.h", ] - if (is_chromeos_lacros) { - sources += [ - "it2me_native_messaging_host_lacros.cc", - "it2me_native_messaging_host_lacros.h", - ] - } else { - sources += [ - "it2me_native_messaging_host_chromeos.cc", - "it2me_native_messaging_host_chromeos.h", - ] + deps = [ + ":common", + "//skia", + ] - deps = [ - ":common", - "//skia", - ] - - if (use_ozone) { - deps += [ "//ui/ozone" ] - } + if (use_ozone) { + deps += [ "//ui/ozone" ] } } }
diff --git a/remoting/host/it2me/it2me_native_messaging_host_allowed_origins.cc b/remoting/host/it2me/it2me_native_messaging_host_allowed_origins.cc deleted file mode 100644 index aa33552f8..0000000 --- a/remoting/host/it2me/it2me_native_messaging_host_allowed_origins.cc +++ /dev/null
@@ -1,23 +0,0 @@ -// Copyright 2021 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 "remoting/host/it2me/it2me_native_messaging_host_allowed_origins.h" - -#include "base/stl_util.h" - -namespace remoting { - -// If you modify the list of allowed_origins, don't forget to update -// remoting/host/it2me/com.google.chrome.remote_assistance.json.jinja2 -// to keep the two lists in sync. -const char* const kIt2MeOrigins[] = { - "chrome-extension://inomeogfingihgjfjlpeplalcfajhgai/", - "chrome-extension://hpodccmdligbeohchckkeajbfohibipg/"}; - -const size_t kIt2MeOriginsSize = base::size(kIt2MeOrigins); - -const char kIt2MeNativeMessageHostName[] = - "com.google.chrome.remote_assistance"; - -} // namespace remoting
diff --git a/remoting/host/it2me/it2me_native_messaging_host_allowed_origins.h b/remoting/host/it2me/it2me_native_messaging_host_allowed_origins.h deleted file mode 100644 index 6d02878..0000000 --- a/remoting/host/it2me/it2me_native_messaging_host_allowed_origins.h +++ /dev/null
@@ -1,22 +0,0 @@ -// Copyright 2021 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 REMOTING_HOST_IT2ME_IT2ME_NATIVE_MESSAGING_HOST_ALLOWED_ORIGINS_H_ -#define REMOTING_HOST_IT2ME_IT2ME_NATIVE_MESSAGING_HOST_ALLOWED_ORIGINS_H_ - -#include <stddef.h> - -namespace remoting { - -// The set of origins which are allowed to instantiate an It2Me host. -extern const char* const kIt2MeOrigins[]; - -// The number of entries defined in |kIt2MeOrigins|. -extern const size_t kIt2MeOriginsSize; - -// The name used to register the It2Me native message host. -extern const char kIt2MeNativeMessageHostName[]; - -} // namespace remoting - -#endif // REMOTING_HOST_IT2ME_IT2ME_NATIVE_MESSAGING_HOST_ALLOWED_ORIGINS_H_
diff --git a/remoting/host/it2me/it2me_native_messaging_host_chromeos.cc b/remoting/host/it2me/it2me_native_messaging_host_chromeos.cc index 0316368..6f261b0 100644 --- a/remoting/host/it2me/it2me_native_messaging_host_chromeos.cc +++ b/remoting/host/it2me/it2me_native_messaging_host_chromeos.cc
@@ -7,7 +7,6 @@ #include <memory> #include "base/lazy_instance.h" -#include "base/stl_util.h" #include "base/task/post_task.h" #include "base/task/task_traits.h" #include "base/task/thread_pool.h" @@ -38,13 +37,4 @@ return host; } -// If you modify the list of allowed_origins, don't forget to update -// remoting/host/it2me/com.google.chrome.remote_assistance.json.jinja2 -// to keep the two lists in sync. -const char* const kRemotingIt2MeOrigins[] = { - "chrome-extension://inomeogfingihgjfjlpeplalcfajhgai/", - "chrome-extension://hpodccmdligbeohchckkeajbfohibipg/"}; - -const size_t kRemotingIt2MeOriginsCount = base::size(kRemotingIt2MeOrigins); - } // namespace remoting
diff --git a/remoting/host/it2me/it2me_native_messaging_host_chromeos.h b/remoting/host/it2me/it2me_native_messaging_host_chromeos.h index 63b88cc..c371e43 100644 --- a/remoting/host/it2me/it2me_native_messaging_host_chromeos.h +++ b/remoting/host/it2me/it2me_native_messaging_host_chromeos.h
@@ -22,18 +22,13 @@ // Creates native messaging host on ChromeOS. Must be called on the UI thread // of the browser process. + std::unique_ptr<extensions::NativeMessageHost> CreateIt2MeNativeMessagingHostForChromeOS( scoped_refptr<base::SingleThreadTaskRunner> io_runnner, scoped_refptr<base::SingleThreadTaskRunner> ui_runnner, policy::PolicyService* policy_service); -// The set of origins which are allowed to instantiate an It2Me host. -extern const char* const kRemotingIt2MeOrigins[]; - -// The number of entries defined in |kRemotingIt2MeOrigins|. -extern const size_t kRemotingIt2MeOriginsCount; - } // namespace remoting #endif // REMOTING_HOST_IT2ME_IT2ME_NATIVE_MESSAGING_HOST_CHROMEOS_H_
diff --git a/remoting/host/it2me/it2me_native_messaging_host_lacros.cc b/remoting/host/it2me/it2me_native_messaging_host_lacros.cc deleted file mode 100644 index a242356..0000000 --- a/remoting/host/it2me/it2me_native_messaging_host_lacros.cc +++ /dev/null
@@ -1,22 +0,0 @@ -// Copyright 2021 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 "remoting/host/it2me/it2me_native_messaging_host_lacros.h" - -#include <memory> - -#include "base/notreached.h" - -namespace remoting { - -std::unique_ptr<extensions::NativeMessageHost> -CreateIt2MeNativeMessagingHostForLacros( - scoped_refptr<base::SingleThreadTaskRunner> io_runnner, - scoped_refptr<base::SingleThreadTaskRunner> ui_runnner) { - // TODO(joedow): Implement a remote support host for LaCrOS. - NOTIMPLEMENTED(); - return nullptr; -} - -} // namespace remoting
diff --git a/remoting/host/it2me/it2me_native_messaging_host_lacros.h b/remoting/host/it2me/it2me_native_messaging_host_lacros.h deleted file mode 100644 index 93feeeb..0000000 --- a/remoting/host/it2me/it2me_native_messaging_host_lacros.h +++ /dev/null
@@ -1,28 +0,0 @@ -// Copyright 2021 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 REMOTING_HOST_IT2ME_IT2ME_NATIVE_MESSAGING_HOST_LACROS_H_ -#define REMOTING_HOST_IT2ME_IT2ME_NATIVE_MESSAGING_HOST_LACROS_H_ - -#include <memory> - -#include "base/memory/scoped_refptr.h" -#include "extensions/browser/api/messaging/native_message_host.h" - -namespace base { -class SingleThreadTaskRunner; -} // namespace base - -namespace remoting { - -// Creates native messaging host on Lacros. Must be called on the UI thread of -// the browser process. - -std::unique_ptr<extensions::NativeMessageHost> -CreateIt2MeNativeMessagingHostForLacros( - scoped_refptr<base::SingleThreadTaskRunner> io_runnner, - scoped_refptr<base::SingleThreadTaskRunner> ui_runnner); - -} // namespace remoting - -#endif // REMOTING_HOST_IT2ME_IT2ME_NATIVE_MESSAGING_HOST_LACROS_H_
diff --git a/services/media_session/public/cpp/test/mock_media_session.cc b/services/media_session/public/cpp/test/mock_media_session.cc index 572cf447..980f146 100644 --- a/services/media_session/public/cpp/test/mock_media_session.cc +++ b/services/media_session/public/cpp/test/mock_media_session.cc
@@ -70,6 +70,8 @@ } else { if (wanted_state_ == session_info_->state || session_info_->playback_state == wanted_playback_state_ || + session_info_->microphone_state == wanted_microphone_state_ || + session_info_->camera_state == wanted_camera_state_ || (wanted_audio_video_states_ && base::ranges::is_permutation(*session_info_->audio_video_states, *wanted_audio_video_states_))) { @@ -156,6 +158,26 @@ StartWaiting(); } +void MockMediaSessionMojoObserver::WaitForMicrophoneState( + mojom::MicrophoneState wanted_state) { + if (session_info_ && session_info_->microphone_state == wanted_state) + return; + + wanted_microphone_state_ = wanted_state; + StartWaiting(); + wanted_microphone_state_.reset(); +} + +void MockMediaSessionMojoObserver::WaitForCameraState( + mojom::CameraState wanted_state) { + if (session_info_ && session_info_->camera_state == wanted_state) + return; + + wanted_camera_state_ = wanted_state; + StartWaiting(); + wanted_camera_state_.reset(); +} + void MockMediaSessionMojoObserver::WaitForAudioVideoStates( const std::vector<mojom::MediaAudioVideoState>& wanted_states) { if (session_info_ && base::ranges::is_permutation(
diff --git a/services/media_session/public/cpp/test/mock_media_session.h b/services/media_session/public/cpp/test/mock_media_session.h index a81e339e..3af3ec6 100644 --- a/services/media_session/public/cpp/test/mock_media_session.h +++ b/services/media_session/public/cpp/test/mock_media_session.h
@@ -49,6 +49,8 @@ void WaitForState(mojom::MediaSessionInfo::SessionState wanted_state); void WaitForPlaybackState(mojom::MediaPlaybackState wanted_state); + void WaitForMicrophoneState(mojom::MicrophoneState wanted_state); + void WaitForCameraState(mojom::CameraState wanted_state); // Blocks until the set of audio/video states for the players in the media // session matches |wanted_states|. The order is not important. @@ -120,6 +122,8 @@ base::Optional<mojom::MediaSessionInfo::SessionState> wanted_state_; base::Optional<mojom::MediaPlaybackState> wanted_playback_state_; + base::Optional<mojom::MicrophoneState> wanted_microphone_state_; + base::Optional<mojom::CameraState> wanted_camera_state_; base::Optional<std::vector<mojom::MediaAudioVideoState>> wanted_audio_video_states_; std::unique_ptr<base::RunLoop> run_loop_;
diff --git a/services/network/url_loader.cc b/services/network/url_loader.cc index 6bcaca9..436ee8ea 100644 --- a/services/network/url_loader.cc +++ b/services/network/url_loader.cc
@@ -495,17 +495,14 @@ mojo::SimpleWatcher::ArmingPolicy::MANUAL, base::SequencedTaskRunnerHandle::Get()), want_raw_headers_(request.report_raw_headers), - report_raw_headers_(false), devtools_request_id_(request.devtools_request_id), - has_user_activation_(false), + request_mode_(request.mode), request_destination_(request.destination), resource_scheduler_client_(std::move(resource_scheduler_client)), keepalive_statistics_recorder_(std::move(keepalive_statistics_recorder)), - first_auth_attempt_(true), custom_proxy_pre_cache_headers_(request.custom_proxy_pre_cache_headers), custom_proxy_post_cache_headers_(request.custom_proxy_post_cache_headers), fetch_window_id_(request.fetch_window_id), - origin_policy_manager_(nullptr), trust_token_helper_factory_(std::move(trust_token_helper_factory)), origin_access_list_(origin_access_list), cookie_observer_(std::move(cookie_observer)), @@ -591,8 +588,6 @@ url_request_->SetUserData(kUserDataKey, std::make_unique<UnownedPointer>(this)); - request_mode_ = request.mode; - if (request.trusted_params) { has_user_activation_ = request.trusted_params->has_user_activation;
diff --git a/services/network/url_loader.h b/services/network/url_loader.h index e8f8e720..89d3ffe 100644 --- a/services/network/url_loader.h +++ b/services/network/url_loader.h
@@ -402,9 +402,9 @@ DeleteCallback delete_callback_; int32_t options_; - bool corb_detachable_; - int resource_type_; - bool is_load_timing_enabled_; + const bool corb_detachable_; + const int resource_type_; + const bool is_load_timing_enabled_; bool has_received_response_ = false; // URLLoaderFactory is guaranteed to outlive URLLoader, so it is safe to @@ -413,7 +413,7 @@ // This also belongs to URLLoaderFactory and outlives this loader. mojom::CrossOriginEmbedderPolicyReporter* const coep_reporter_; - uint32_t request_id_; + const uint32_t request_id_; const int keepalive_request_size_; const bool keepalive_; const bool do_not_prompt_for_login_; @@ -455,7 +455,7 @@ // Whether client requested raw headers. const bool want_raw_headers_; // Whether we actually should report them. - bool report_raw_headers_; + bool report_raw_headers_ = false; net::HttpRawRequestHeaders raw_request_headers_; scoped_refptr<const net::HttpResponseHeaders> raw_response_headers_; @@ -492,9 +492,9 @@ // encoded body size was reported to the client. int64_t reported_total_encoded_bytes_ = 0; - mojom::RequestMode request_mode_; + const mojom::RequestMode request_mode_; - bool has_user_activation_; + bool has_user_activation_ = false; mojom::RequestDestination request_destination_ = mojom::RequestDestination::kEmpty; @@ -503,7 +503,7 @@ base::WeakPtr<KeepaliveStatisticsRecorder> keepalive_statistics_recorder_; - bool first_auth_attempt_; + bool first_auth_attempt_ = true; std::unique_ptr<ScopedThrottlingToken> throttling_token_; @@ -519,7 +519,7 @@ std::unique_ptr<FileOpenerForUpload> file_opener_for_upload_; // Will only be set for requests that have |obey_origin_policy| set. - mojom::OriginPolicyManager* origin_policy_manager_; + mojom::OriginPolicyManager* origin_policy_manager_ = nullptr; // If the request is configured for Trust Tokens // (https://github.com/WICG/trust-token-api) protocol operations, annotates
diff --git a/testing/buildbot/chromium.perf.calibration.json b/testing/buildbot/chromium.perf.calibration.json index 8515823d9..6c764861 100644 --- a/testing/buildbot/chromium.perf.calibration.json +++ b/testing/buildbot/chromium.perf.calibration.json
@@ -39,7 +39,8 @@ "trigger_script": { "args": [ "--multiple-dimension-script-verbose", - "True" + "True", + "--use-dynamic-shards" ], "requires_simultaneous_shard_dispatch": true, "script": "//testing/trigger_scripts/perf_device_trigger.py"
diff --git a/testing/buildbot/chromium.perf.fyi.json b/testing/buildbot/chromium.perf.fyi.json index 373033fc..34b5a961 100644 --- a/testing/buildbot/chromium.perf.fyi.json +++ b/testing/buildbot/chromium.perf.fyi.json
@@ -235,22 +235,23 @@ "isolated_scripts": [ { "args": [ - "hardware_accelerated_feature", - "--show-stdout", - "--browser=web-engine-shell", - "--passthrough", "-v", - "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc", + "--browser=web-engine-shell", + "--upload-results", + "--test-shard-map-filename=fuchsia-perf-fyi_map.json", + "--output-format=histograms", + "--experimental-tbmv3-metrics", "--device=custom", - "--custom-device-target=internal.astro_target" + "--custom-device-target=internal.astro_target", + "--story-filter=load:chrome:blank" ], - "isolate_name": "fuchsia_telemetry_gpu_integration_test", + "isolate_name": "performance_web_engine_test_suite", "merge": { "script": "//tools/perf/process_perf_results.py" }, - "name": "fuchsia_telemetry_gpu_integration_test", + "name": "performance_web_engine_test_suite", "override_compile_targets": [ - "fuchsia_telemetry_gpu_integration_test" + "performance_web_engine_test_suite" ], "swarming": { "can_use_on_swarming_builders": true, @@ -266,7 +267,8 @@ "hard_timeout": 21600, "ignore_task_failure": false, "io_timeout": 21600, - "service_account": "chrome-tester@chops-service-accounts.iam.gserviceaccount.com" + "service_account": "chrome-tester@chops-service-accounts.iam.gserviceaccount.com", + "shards": 1 }, "trigger_script": { "args": [
diff --git a/testing/buildbot/chromium.perf.json b/testing/buildbot/chromium.perf.json index 90dcd3ab..b84194e 100644 --- a/testing/buildbot/chromium.perf.json +++ b/testing/buildbot/chromium.perf.json
@@ -1118,6 +1118,7 @@ "can_use_on_swarming_builders": true, "dimension_sets": [ { + "device_status": "available", "device_type": "eve", "gpu": null, "os": "ChromeOS",
diff --git a/testing/buildbot/filters/android.emulator_p.chrome_public_test_apk.filter b/testing/buildbot/filters/android.emulator_p.chrome_public_test_apk.filter index 955151e..2febd24c 100644 --- a/testing/buildbot/filters/android.emulator_p.chrome_public_test_apk.filter +++ b/testing/buildbot/filters/android.emulator_p.chrome_public_test_apk.filter
@@ -124,3 +124,6 @@ # crbug.com/1163913 -org.chromium.chrome.browser.autofill_assistant.AutofillAssistantTriggerScriptIntegrationTest.dontShowOnboardingIfAcceptedInDifferentTab + +# crbug.com/1187536 +-org.chromium.chrome.browser.customtabs.CustomTabExternalNavigationTest.testIntentPickerNotShownForNormalUrl
diff --git a/testing/buildbot/filters/android.pie_arm64_rel.chrome_public_test_apk.filter b/testing/buildbot/filters/android.pie_arm64_rel.chrome_public_test_apk.filter index 0e57593d..5aeeee8 100644 --- a/testing/buildbot/filters/android.pie_arm64_rel.chrome_public_test_apk.filter +++ b/testing/buildbot/filters/android.pie_arm64_rel.chrome_public_test_apk.filter
@@ -93,3 +93,5 @@ # crbug.com/1169250 -org.chromium.chrome.browser.omnibox.LocationBarTest.testFocusLogic_keyboardVisibility +# crbug.com/1183540: Re-enable once the tests do not cause timeout. +-org.chromium.chrome.browser.contextualsearch.ContextualSearchManagerTest.*
diff --git a/testing/buildbot/gn_isolate_map.pyl b/testing/buildbot/gn_isolate_map.pyl index 96946b77..4d0980f3 100644 --- a/testing/buildbot/gn_isolate_map.pyl +++ b/testing/buildbot/gn_isolate_map.pyl
@@ -1449,6 +1449,14 @@ "label": "//chrome/test:performance_test_suite_eve", "type": "generated_script", }, + "performance_web_engine_test_suite": { + "args": [ + "../../content/test/run_gpu_integration_test_fuchsia.py", + ], + "label": "//content/test:performance_web_engine_test_suite", + "script": "//testing/scripts/run_performance_tests.py", + "type": "script", + }, "performance_webview_test_suite": { "args": [ "remove",
diff --git a/testing/trigger_scripts/perf_device_trigger.py b/testing/trigger_scripts/perf_device_trigger.py index d09bbe27..46043f4 100755 --- a/testing/trigger_scripts/perf_device_trigger.py +++ b/testing/trigger_scripts/perf_device_trigger.py
@@ -165,8 +165,9 @@ selected_config.sort() if verbose: - for shard_index, bot_id in selected_config: - print('Shard %d\n\tBot: %s' % (shard_index, bot_id)) + for shard_index, bot_index in selected_config: + print('Shard %d\n\tBot: %s' % + (shard_index, self._bot_configs[bot_index]['id'])) return selected_config
diff --git a/third_party/blink/common/unique_name/unique_name_helper_unittest.cc b/third_party/blink/common/unique_name/unique_name_helper_unittest.cc index 0b8d4ac20..1afb041e 100644 --- a/third_party/blink/common/unique_name/unique_name_helper_unittest.cc +++ b/third_party/blink/common/unique_name/unique_name_helper_unittest.cc
@@ -10,7 +10,6 @@ #include "base/auto_reset.h" #include "base/optional.h" -#include "base/strings/nullable_string16.h" #include "base/strings/string_piece.h" #include "base/strings/utf_string_conversions.h" #include "testing/gtest/include/gtest/gtest.h"
diff --git a/third_party/blink/public/mojom/web_feature/web_feature.mojom b/third_party/blink/public/mojom/web_feature/web_feature.mojom index e4048506..de4becd 100644 --- a/third_party/blink/public/mojom/web_feature/web_feature.mojom +++ b/third_party/blink/public/mojom/web_feature/web_feature.mojom
@@ -3160,6 +3160,8 @@ kPermissionsPolicyHeader = 3850, kWebAppManifestUrlHandlers = 3851, kLaxAllowingUnsafeCookies = 3852, + kV8MediaSession_SetMicrophoneActive_Method = 3853, + kV8MediaSession_SetCameraActive_Method = 3854, // Add new features immediately above this line. Don't change assigned // numbers of any item, and don't reuse removed slots.
diff --git a/third_party/blink/public/platform/web_runtime_features.h b/third_party/blink/public/platform/web_runtime_features.h index bf2f6b3..d0ad622 100644 --- a/third_party/blink/public/platform/web_runtime_features.h +++ b/third_party/blink/public/platform/web_runtime_features.h
@@ -124,6 +124,7 @@ BLINK_PLATFORM_EXPORT static void EnableLazyImageVisibleLoadTimeMetrics(bool); BLINK_PLATFORM_EXPORT static void EnableMediaFeeds(bool); BLINK_PLATFORM_EXPORT static void EnableMediaSession(bool); + BLINK_PLATFORM_EXPORT static void EnableMediaSessionWebRTC(bool); BLINK_PLATFORM_EXPORT static void EnableNetInfoDownlinkMax(bool); BLINK_PLATFORM_EXPORT static void EnableNeverSlowMode(bool); BLINK_PLATFORM_EXPORT static void EnableNotificationContentImage(bool);
diff --git a/third_party/blink/public/web/web_local_frame_client.h b/third_party/blink/public/web/web_local_frame_client.h index a51b59c..883f833 100644 --- a/third_party/blink/public/web/web_local_frame_client.h +++ b/third_party/blink/public/web/web_local_frame_client.h
@@ -364,7 +364,6 @@ virtual void DidCommitNavigation( WebHistoryCommitType commit_type, bool should_reset_browser_interface_broker, - network::mojom::WebSandboxFlags sandbox_flags, const ParsedPermissionsPolicy& permissions_policy_header, const DocumentPolicyFeatureState& document_policy_header) {}
diff --git a/third_party/blink/renderer/bindings/core/v8/serialization/serialization_tag.h b/third_party/blink/renderer/bindings/core/v8/serialization/serialization_tag.h index 1eb157a3..f105535 100644 --- a/third_party/blink/renderer/bindings/core/v8/serialization/serialization_tag.h +++ b/third_party/blink/renderer/bindings/core/v8/serialization/serialization_tag.h
@@ -104,6 +104,7 @@ kRTCEncodedAudioFrameTag = 'A', // uint32_t -> transferred audio frame ID kRTCEncodedVideoFrameTag = 'V', // uint32_t -> transferred video frame ID + kAudioFrameTag = 'a', // uint32_t -> transferred audio frame data kVideoFrameTag = 'v', // uint32_t -> transferred video frame ID // The following tags were used by the Shape Detection API implementation
diff --git a/third_party/blink/renderer/bindings/modules/v8/serialization/v8_script_value_deserializer_for_modules.cc b/third_party/blink/renderer/bindings/modules/v8/serialization/v8_script_value_deserializer_for_modules.cc index 327c0e8..59b5e37 100644 --- a/third_party/blink/renderer/bindings/modules/v8/serialization/v8_script_value_deserializer_for_modules.cc +++ b/third_party/blink/renderer/bindings/modules/v8/serialization/v8_script_value_deserializer_for_modules.cc
@@ -22,6 +22,9 @@ #include "third_party/blink/renderer/modules/peerconnection/rtc_encoded_audio_frame_delegate.h" #include "third_party/blink/renderer/modules/peerconnection/rtc_encoded_video_frame.h" #include "third_party/blink/renderer/modules/peerconnection/rtc_encoded_video_frame_delegate.h" +#include "third_party/blink/renderer/modules/webcodecs/audio_frame.h" +#include "third_party/blink/renderer/modules/webcodecs/audio_frame_attachment.h" +#include "third_party/blink/renderer/modules/webcodecs/audio_frame_serialization_data.h" #include "third_party/blink/renderer/modules/webcodecs/video_frame.h" #include "third_party/blink/renderer/modules/webcodecs/video_frame_attachment.h" @@ -75,6 +78,8 @@ return ReadRTCEncodedAudioFrame(); case kRTCEncodedVideoFrameTag: return ReadRTCEncodedVideoFrame(); + case kAudioFrameTag: + return ReadAudioFrame(); case kVideoFrameTag: return ReadVideoFrame(); default: @@ -418,6 +423,28 @@ return MakeGarbageCollected<RTCEncodedVideoFrame>(frames[index]); } +AudioFrame* V8ScriptValueDeserializerForModules::ReadAudioFrame() { + if (!RuntimeEnabledFeatures::WebCodecsEnabled( + ExecutionContext::From(GetScriptState()))) { + return nullptr; + } + + uint32_t index; + if (!ReadUint32(&index)) + return nullptr; + + const auto* attachment = + GetSerializedScriptValue()->GetAttachmentIfExists<AudioFrameAttachment>(); + if (!attachment) + return nullptr; + + const auto& serialization_data = attachment->SerializationData(); + if (index >= attachment->size()) + return nullptr; + + return MakeGarbageCollected<AudioFrame>(serialization_data[index].get()); +} + VideoFrame* V8ScriptValueDeserializerForModules::ReadVideoFrame() { if (!RuntimeEnabledFeatures::WebCodecsEnabled( ExecutionContext::From(GetScriptState()))) {
diff --git a/third_party/blink/renderer/bindings/modules/v8/serialization/v8_script_value_deserializer_for_modules.h b/third_party/blink/renderer/bindings/modules/v8/serialization/v8_script_value_deserializer_for_modules.h index 90408ee..9a01275 100644 --- a/third_party/blink/renderer/bindings/modules/v8/serialization/v8_script_value_deserializer_for_modules.h +++ b/third_party/blink/renderer/bindings/modules/v8/serialization/v8_script_value_deserializer_for_modules.h
@@ -10,6 +10,7 @@ namespace blink { +class AudioFrame; class CryptoKey; class FileSystemHandle; class RTCEncodedAudioFrame; @@ -49,6 +50,7 @@ FileSystemHandle* ReadFileSystemHandle(SerializationTag tag); RTCEncodedAudioFrame* ReadRTCEncodedAudioFrame(); RTCEncodedVideoFrame* ReadRTCEncodedVideoFrame(); + AudioFrame* ReadAudioFrame(); VideoFrame* ReadVideoFrame(); };
diff --git a/third_party/blink/renderer/bindings/modules/v8/serialization/v8_script_value_serializer_for_modules.cc b/third_party/blink/renderer/bindings/modules/v8/serialization/v8_script_value_serializer_for_modules.cc index c60809e..0deeb89 100644 --- a/third_party/blink/renderer/bindings/modules/v8/serialization/v8_script_value_serializer_for_modules.cc +++ b/third_party/blink/renderer/bindings/modules/v8/serialization/v8_script_value_serializer_for_modules.cc
@@ -11,6 +11,7 @@ #include "third_party/blink/renderer/bindings/core/v8/serialization/v8_script_value_serializer.h" #include "third_party/blink/renderer/bindings/core/v8/v8_dom_rect_read_only.h" #include "third_party/blink/renderer/bindings/modules/v8/serialization/web_crypto_sub_tags.h" +#include "third_party/blink/renderer/bindings/modules/v8/v8_audio_frame.h" #include "third_party/blink/renderer/bindings/modules/v8/v8_crypto_key.h" #include "third_party/blink/renderer/bindings/modules/v8/v8_dom_file_system.h" #include "third_party/blink/renderer/bindings/modules/v8/v8_file_system_directory_handle.h" @@ -25,6 +26,9 @@ #include "third_party/blink/renderer/modules/peerconnection/rtc_encoded_audio_frame_delegate.h" #include "third_party/blink/renderer/modules/peerconnection/rtc_encoded_video_frame.h" #include "third_party/blink/renderer/modules/peerconnection/rtc_encoded_video_frame_delegate.h" +#include "third_party/blink/renderer/modules/webcodecs/audio_frame.h" +#include "third_party/blink/renderer/modules/webcodecs/audio_frame_attachment.h" +#include "third_party/blink/renderer/modules/webcodecs/audio_frame_serialization_data.h" #include "third_party/blink/renderer/modules/webcodecs/video_frame.h" #include "third_party/blink/renderer/modules/webcodecs/video_frame_attachment.h" #include "third_party/blink/renderer/platform/bindings/script_wrappable.h" @@ -118,6 +122,26 @@ } return WriteVideoFrameHandle(std::move(handle)); } + if (wrapper_type_info == V8AudioFrame::GetWrapperTypeInfo() && + RuntimeEnabledFeatures::WebCodecsEnabled( + ExecutionContext::From(GetScriptState()))) { + if (IsForStorage()) { + exception_state.ThrowDOMException( + DOMExceptionCode::kDataCloneError, + "An AudioFrame cannot be serialized for " + "storage."); + return false; + } + std::unique_ptr<AudioFrameSerializationData> data = + wrappable->ToImpl<AudioFrame>()->GetSerializationData(); + if (!data) { + exception_state.ThrowDOMException(DOMExceptionCode::kDataCloneError, + "An AudioFrame could not be cloned " + "because it was closed."); + return false; + } + return WriteAudioFrameSerializationData(std::move(data)); + } return false; } @@ -358,4 +382,18 @@ return true; } +bool V8ScriptValueSerializerForModules::WriteAudioFrameSerializationData( + std::unique_ptr<AudioFrameSerializationData> audio_data) { + auto* attachment = + GetSerializedScriptValue()->GetOrCreateAttachment<AudioFrameAttachment>(); + auto& serialization_data = attachment->SerializationData(); + serialization_data.push_back(std::move(audio_data)); + const uint32_t index = static_cast<uint32_t>(serialization_data.size() - 1); + + WriteTag(kAudioFrameTag); + WriteUint32(index); + + return true; +} + } // namespace blink
diff --git a/third_party/blink/renderer/bindings/modules/v8/serialization/v8_script_value_serializer_for_modules.h b/third_party/blink/renderer/bindings/modules/v8/serialization/v8_script_value_serializer_for_modules.h index 3ea3283c..013d608 100644 --- a/third_party/blink/renderer/bindings/modules/v8/serialization/v8_script_value_serializer_for_modules.h +++ b/third_party/blink/renderer/bindings/modules/v8/serialization/v8_script_value_serializer_for_modules.h
@@ -12,6 +12,7 @@ namespace blink { +class AudioFrameSerializationData; class FileSystemHandle; class RTCEncodedAudioFrame; class RTCEncodedVideoFrame; @@ -38,6 +39,8 @@ bool WriteRTCEncodedAudioFrame(RTCEncodedAudioFrame*); bool WriteRTCEncodedVideoFrame(RTCEncodedVideoFrame*); bool WriteVideoFrameHandle(scoped_refptr<VideoFrameHandle>); + bool WriteAudioFrameSerializationData( + std::unique_ptr<AudioFrameSerializationData>); }; } // namespace blink
diff --git a/third_party/blink/renderer/bindings/modules/v8/serialization/v8_script_value_serializer_for_modules_test.cc b/third_party/blink/renderer/bindings/modules/v8/serialization/v8_script_value_serializer_for_modules_test.cc index 170fa8a..c35aa36c 100644 --- a/third_party/blink/renderer/bindings/modules/v8/serialization/v8_script_value_serializer_for_modules_test.cc +++ b/third_party/blink/renderer/bindings/modules/v8/serialization/v8_script_value_serializer_for_modules_test.cc
@@ -19,6 +19,7 @@ #include "third_party/blink/renderer/bindings/core/v8/v8_dom_exception.h" #include "third_party/blink/renderer/bindings/core/v8/v8_dom_rect_read_only.h" #include "third_party/blink/renderer/bindings/modules/v8/serialization/v8_script_value_deserializer_for_modules.h" +#include "third_party/blink/renderer/bindings/modules/v8/v8_audio_frame.h" #include "third_party/blink/renderer/bindings/modules/v8/v8_crypto_key.h" #include "third_party/blink/renderer/bindings/modules/v8/v8_dom_file_system.h" #include "third_party/blink/renderer/bindings/modules/v8/v8_rtc_certificate.h" @@ -28,6 +29,8 @@ #include "third_party/blink/renderer/modules/filesystem/dom_file_system.h" #include "third_party/blink/renderer/modules/peerconnection/rtc_certificate.h" #include "third_party/blink/renderer/modules/peerconnection/rtc_certificate_generator.h" +#include "third_party/blink/renderer/modules/webcodecs/audio_frame.h" +#include "third_party/blink/renderer/modules/webcodecs/audio_frame_serialization_data.h" #include "third_party/blink/renderer/modules/webcodecs/video_frame.h" #include "third_party/blink/renderer/platform/bindings/exception_state.h" #include "third_party/blink/renderer/platform/testing/unit_test_helpers.h" @@ -1022,7 +1025,7 @@ scoped_refptr<media::VideoFrame> media_frame = media::VideoFrame::CreateBlackFrame(kFrameSize); - // Create and destroy the frame. + // Create and close the frame. auto* blink_frame = MakeGarbageCollected<VideoFrame>( media_frame, scope.GetExecutionContext()); blink_frame->close(); @@ -1035,5 +1038,84 @@ "DataCloneError", scope.GetScriptState(), exception_state)); } +TEST(V8ScriptValueSerializerForModulesTest, RoundTripAudioFrame) { + V8TestingScope scope; + + const unsigned kChannels = 2; + const unsigned kSampleRate = 8000; + const unsigned kFrames = 500; + constexpr base::TimeDelta kTimestamp = base::TimeDelta::FromMilliseconds(314); + + auto audio_bus = media::AudioBus::Create(kChannels, kFrames); + + // Populate each frame with a unique value. + const unsigned kTotalFrames = (kFrames * kChannels); + const float kFramesMultiplier = 1.0 / kTotalFrames; + for (unsigned ch = 0; ch < kChannels; ++ch) { + float* data = audio_bus->channel(ch); + for (unsigned i = 0; i < kFrames; ++i) + data[i] = (i + ch * kFrames) * kFramesMultiplier; + } + + auto audio_serialization_data = AudioFrameSerializationData::Wrap( + std::move(audio_bus), kSampleRate, kTimestamp); + + auto* audio_frame = + MakeGarbageCollected<AudioFrame>(std::move(audio_serialization_data)); + + // Round trip the frame and make sure the size is the same. + v8::Local<v8::Value> wrapper = ToV8(audio_frame, scope.GetScriptState()); + v8::Local<v8::Value> result = RoundTripForModules(wrapper, scope); + + // The data should have been copied, not transferred. + EXPECT_TRUE(audio_frame->buffer()); + + ASSERT_TRUE(V8AudioFrame::HasInstance(result, scope.GetIsolate())); + + AudioFrame* new_frame = V8AudioFrame::ToImpl(result.As<v8::Object>()); + EXPECT_EQ(base::TimeDelta::FromMicroseconds(new_frame->timestamp()), + kTimestamp); + EXPECT_EQ(new_frame->buffer()->numberOfChannels(), kChannels); + EXPECT_EQ(new_frame->buffer()->sampleRate(), kSampleRate); + EXPECT_EQ(new_frame->buffer()->length(), kFrames); + + // Make sure the data wasn't changed during the transfer. + for (unsigned ch = 0; ch < kChannels; ++ch) { + float* src_data = audio_frame->buffer()->getChannelData(ch)->Data(); + float* dst_data = new_frame->buffer()->getChannelData(ch)->Data(); + for (unsigned i = 0; i < kFrames; ++i) { + EXPECT_EQ(src_data[i], dst_data[i]); + } + } + + // Closing the original |audio_frame| should not affect |new_frame|. + audio_frame->close(); + EXPECT_TRUE(new_frame->buffer()); +} + +TEST(V8ScriptValueSerializerForModulesTest, ClosedAudioFrameThrows) { + V8TestingScope scope; + ExceptionState exception_state(scope.GetIsolate(), + ExceptionState::kExecutionContext, "Window", + "postMessage"); + + auto audio_bus = media::AudioBus::Create(/*channels=*/2, /*frames=*/500); + auto audio_serialization_data = AudioFrameSerializationData::Wrap( + std::move(audio_bus), /*sample_rate=*/8000, + base::TimeDelta::FromMilliseconds(314)); + + // Create and close the frame. + auto* audio_frame = + MakeGarbageCollected<AudioFrame>(std::move(audio_serialization_data)); + audio_frame->close(); + + // Serializing the closed frame should throw an error. + v8::Local<v8::Value> wrapper = ToV8(audio_frame, scope.GetScriptState()); + EXPECT_FALSE(V8ScriptValueSerializer(scope.GetScriptState()) + .Serialize(wrapper, exception_state)); + EXPECT_TRUE(HadDOMExceptionInModulesTest( + "DataCloneError", scope.GetScriptState(), exception_state)); +} + } // namespace } // namespace blink
diff --git a/third_party/blink/renderer/core/exported/web_document_subresource_filter_test.cc b/third_party/blink/renderer/core/exported/web_document_subresource_filter_test.cc index 6340e3b..58e63c2 100644 --- a/third_party/blink/renderer/core/exported/web_document_subresource_filter_test.cc +++ b/third_party/blink/renderer/core/exported/web_document_subresource_filter_test.cc
@@ -73,7 +73,6 @@ void DidCommitNavigation( WebHistoryCommitType commit_type, bool should_reset_browser_interface_broker, - network::mojom::WebSandboxFlags sandbox_flags, const ParsedPermissionsPolicy& permissions_policy_header, const DocumentPolicyFeatureState& document_policy_header) override { subresource_filter_ =
diff --git a/third_party/blink/renderer/core/frame/local_frame_client.h b/third_party/blink/renderer/core/frame/local_frame_client.h index 20a509a..2344c10 100644 --- a/third_party/blink/renderer/core/frame/local_frame_client.h +++ b/third_party/blink/renderer/core/frame/local_frame_client.h
@@ -138,7 +138,6 @@ HistoryItem* item, WebHistoryCommitType commit_type, bool should_reset_browser_interface_broker, - network::mojom::WebSandboxFlags sandbox_flags, const blink::ParsedPermissionsPolicy& permissions_policy_header, const blink::DocumentPolicyFeatureState& document_policy_header) = 0; virtual void DispatchDidFailLoad(const ResourceError&,
diff --git a/third_party/blink/renderer/core/frame/local_frame_client_impl.cc b/third_party/blink/renderer/core/frame/local_frame_client_impl.cc index 76a5edba..cbddca0 100644 --- a/third_party/blink/renderer/core/frame/local_frame_client_impl.cc +++ b/third_party/blink/renderer/core/frame/local_frame_client_impl.cc
@@ -404,7 +404,6 @@ HistoryItem* item, WebHistoryCommitType commit_type, bool should_reset_browser_interface_broker, - network::mojom::WebSandboxFlags sandbox_flags, const blink::ParsedPermissionsPolicy& permissions_policy_header, const blink::DocumentPolicyFeatureState& document_policy_header) { if (!web_frame_->Parent()) { @@ -414,7 +413,7 @@ if (web_frame_->Client()) { web_frame_->Client()->DidCommitNavigation( - commit_type, should_reset_browser_interface_broker, sandbox_flags, + commit_type, should_reset_browser_interface_broker, permissions_policy_header, document_policy_header); // With local to local swap it's possible for the frame to be deleted as a
diff --git a/third_party/blink/renderer/core/frame/local_frame_client_impl.h b/third_party/blink/renderer/core/frame/local_frame_client_impl.h index 244a0028..76643c8 100644 --- a/third_party/blink/renderer/core/frame/local_frame_client_impl.h +++ b/third_party/blink/renderer/core/frame/local_frame_client_impl.h
@@ -100,7 +100,6 @@ HistoryItem*, WebHistoryCommitType, bool should_reset_browser_interface_broker, - network::mojom::WebSandboxFlags sandbox_flags, const blink::ParsedPermissionsPolicy& permissions_policy_header, const blink::DocumentPolicyFeatureState& document_policy_header) override; void DispatchDidFailLoad(const ResourceError&, WebHistoryCommitType) override;
diff --git a/third_party/blink/renderer/core/frame/web_frame_test.cc b/third_party/blink/renderer/core/frame/web_frame_test.cc index f09cb07..80d2d66 100644 --- a/third_party/blink/renderer/core/frame/web_frame_test.cc +++ b/third_party/blink/renderer/core/frame/web_frame_test.cc
@@ -4235,7 +4235,6 @@ void DidCommitNavigation( WebHistoryCommitType commit_type, bool should_reset_browser_interface_broker, - network::mojom::WebSandboxFlags sandbox_flags, const ParsedPermissionsPolicy& permissions_policy_header, const DocumentPolicyFeatureState& document_policy_header) override { Frame()->View()->ResetScrollAndScaleState(); @@ -6502,7 +6501,6 @@ void DidCommitNavigation( WebHistoryCommitType commit_type, bool should_reset_browser_interface_broker, - network::mojom::WebSandboxFlags sandbox_flags, const ParsedPermissionsPolicy& permissions_policy_header, const DocumentPolicyFeatureState& document_policy_header) final { did_load_ = true; @@ -9642,7 +9640,6 @@ void DidCommitNavigation( WebHistoryCommitType history_commit_type, bool should_reset_browser_interface_broker, - network::mojom::WebSandboxFlags sandbox_flags, const ParsedPermissionsPolicy& permissions_policy_header, const DocumentPolicyFeatureState& document_policy_header) override { history_commit_type_ = history_commit_type; @@ -9909,7 +9906,6 @@ void DidCommitNavigation( WebHistoryCommitType history_commit_type, bool should_reset_browser_interface_broker, - network::mojom::WebSandboxFlags sandbox_flags, const ParsedPermissionsPolicy& permissions_policy_header, const DocumentPolicyFeatureState& document_policy_header) final { history_commit_type_ = history_commit_type;
diff --git a/third_party/blink/renderer/core/frame/web_local_frame_client_test.cc b/third_party/blink/renderer/core/frame/web_local_frame_client_test.cc index 23d5f08..b726aa2d 100644 --- a/third_party/blink/renderer/core/frame/web_local_frame_client_test.cc +++ b/third_party/blink/renderer/core/frame/web_local_frame_client_test.cc
@@ -34,12 +34,11 @@ void DidCommitNavigation( WebHistoryCommitType commit_type, bool should_reset_browser_interface_broker, - network::mojom::WebSandboxFlags sandbox_flags, const ParsedPermissionsPolicy& permissions_policy_header, const DocumentPolicyFeatureState& document_policy_header) override { calls_.push_back("DidCommitNavigation"); TestWebFrameClient::DidCommitNavigation( - commit_type, should_reset_browser_interface_broker, sandbox_flags, + commit_type, should_reset_browser_interface_broker, permissions_policy_header, document_policy_header); }
diff --git a/third_party/blink/renderer/core/inspector/inspector_trace_events.h b/third_party/blink/renderer/core/inspector/inspector_trace_events.h index 2ec962a..e46bb392 100644 --- a/third_party/blink/renderer/core/inspector/inspector_trace_events.h +++ b/third_party/blink/renderer/core/inspector/inspector_trace_events.h
@@ -519,6 +519,7 @@ CORE_EXPORT String ToHexString(const void* p); CORE_EXPORT void SetCallStack(TracedValue*); +CORE_EXPORT void SetCallStack(perfetto::TracedDictionary&); } // namespace blink
diff --git a/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm_helpers.cc b/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm_helpers.cc index 58ce313..cea1314 100644 --- a/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm_helpers.cc +++ b/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm_helpers.cc
@@ -771,7 +771,7 @@ if (distributable_block_size <= LayoutUnit()) return; - // Step 1: percentage rows grow to their percentage size. + // Step 1: percentage rows grow to no more than their percentage size. if (percent_rows_with_deficit_count > 0) { float ratio = std::min( distributable_block_size.ToFloat() / percentage_block_size_deficit, @@ -792,6 +792,9 @@ } last_row->block_size += remaining_deficit; distributed_block_size += remaining_deficit; + // Rounding errors might cause us to distribute more than available length. + distributed_block_size = + std::min(distributed_block_size, distributable_block_size); distributable_block_size -= distributed_block_size; } DCHECK_GE(distributable_block_size, LayoutUnit());
diff --git a/third_party/blink/renderer/core/loader/document_loader.cc b/third_party/blink/renderer/core/loader/document_loader.cc index ea436a6a..ed8dcb31 100644 --- a/third_party/blink/renderer/core/loader/document_loader.cc +++ b/third_party/blink/renderer/core/loader/document_loader.cc
@@ -2199,7 +2199,6 @@ GetLocalFrameClient().DispatchDidCommitLoad( history_item_.Get(), LoadTypeToCommitType(load_type_), previous_window != frame_->DomWindow(), - frame_->DomWindow()->GetSandboxFlags(), security_init.PermissionsPolicyHeader(), document_policy_.feature_state); }
diff --git a/third_party/blink/renderer/core/loader/empty_clients.h b/third_party/blink/renderer/core/loader/empty_clients.h index 10a75b72..9f9e6c6 100644 --- a/third_party/blink/renderer/core/loader/empty_clients.h +++ b/third_party/blink/renderer/core/loader/empty_clients.h
@@ -258,7 +258,6 @@ HistoryItem* item, WebHistoryCommitType commit_type, bool should_reset_browser_interface_broker, - network::mojom::WebSandboxFlags sandbox_flags, const blink::ParsedPermissionsPolicy& permissions_policy_header, const blink::DocumentPolicyFeatureState& document_policy_header) override {}
diff --git a/third_party/blink/renderer/core/paint/svg_foreign_object_painter.cc b/third_party/blink/renderer/core/paint/svg_foreign_object_painter.cc index 5d0f5254..81b649f 100644 --- a/third_party/blink/renderer/core/paint/svg_foreign_object_painter.cc +++ b/third_party/blink/renderer/core/paint/svg_foreign_object_painter.cc
@@ -54,8 +54,8 @@ // ScopedSVGPaintState only applies masks (and clips-within-clips) here. ScopedSVGPaintState paint_state(layout_svg_foreign_object_, paint_info); - PaintTiming& timing = PaintTiming::From( - layout_svg_foreign_object_.GetElement()->GetDocument().TopDocument()); + PaintTiming& timing = + PaintTiming::From(layout_svg_foreign_object_.GetDocument()); timing.MarkFirstContentfulPaint(); BlockPainter(layout_svg_foreign_object_).Paint(paint_info);
diff --git a/third_party/blink/renderer/core/paint/svg_image_painter.cc b/third_party/blink/renderer/core/paint/svg_image_painter.cc index af754d84..9afdc91 100644 --- a/third_party/blink/renderer/core/paint/svg_image_painter.cc +++ b/third_party/blink/renderer/core/paint/svg_image_painter.cc
@@ -107,8 +107,7 @@ layout_svg_image_, image->Size(), *image_content, paint_info.context.GetPaintController().CurrentPaintChunkProperties(), EnclosingIntRect(dest_rect)); - PaintTiming& timing = PaintTiming::From( - layout_svg_image_.GetElement()->GetDocument().TopDocument()); + PaintTiming& timing = PaintTiming::From(layout_svg_image_.GetDocument()); timing.MarkFirstContentfulPaint(); }
diff --git a/third_party/blink/renderer/core/paint/svg_inline_text_box_painter.cc b/third_party/blink/renderer/core/paint/svg_inline_text_box_painter.cc index dbbcc44b..59f116d 100644 --- a/third_party/blink/renderer/core/paint/svg_inline_text_box_painter.cc +++ b/third_party/blink/renderer/core/paint/svg_inline_text_box_painter.cc
@@ -504,8 +504,7 @@ context.GetPaintController().SetTextPainted(); if (!scaled_font.ShouldSkipDrawing()) { - PaintTiming& timing = PaintTiming::From( - text_layout_object.GetNode()->GetDocument().TopDocument()); + PaintTiming& timing = PaintTiming::From(text_layout_object.GetDocument()); timing.MarkFirstContentfulPaint(); PaintTimingDetector::NotifyTextPaint(EnclosingIntRect( InlineLayoutObject().VisualRectInLocalSVGCoordinates()));
diff --git a/third_party/blink/renderer/core/paint/svg_shape_painter.cc b/third_party/blink/renderer/core/paint/svg_shape_painter.cc index 1f593a8..3c2ced9 100644 --- a/third_party/blink/renderer/core/paint/svg_shape_painter.cc +++ b/third_party/blink/renderer/core/paint/svg_shape_painter.cc
@@ -162,8 +162,7 @@ DarkModeFilter::ElementRole::kSVG); } } - PaintTiming& timing = PaintTiming::From( - layout_svg_shape_.GetElement()->GetDocument().TopDocument()); + PaintTiming& timing = PaintTiming::From(layout_svg_shape_.GetDocument()); timing.MarkFirstContentfulPaint(); } @@ -188,8 +187,7 @@ context.DrawPath(use_path->GetSkPath(), flags, DarkModeFilter::ElementRole::kSVG); } - PaintTiming& timing = PaintTiming::From( - layout_svg_shape_.GetElement()->GetDocument().TopDocument()); + PaintTiming& timing = PaintTiming::From(layout_svg_shape_.GetDocument()); timing.MarkFirstContentfulPaint(); }
diff --git a/third_party/blink/renderer/modules/mediasession/media_session.cc b/third_party/blink/renderer/modules/mediasession/media_session.cc index 7cbfdaf..07a978f0c 100644 --- a/third_party/blink/renderer/modules/mediasession/media_session.cc +++ b/third_party/blink/renderer/modules/mediasession/media_session.cc
@@ -44,6 +44,11 @@ DEFINE_STATIC_LOCAL(const AtomicString, skip_ad_action_name, ("skipad")); DEFINE_STATIC_LOCAL(const AtomicString, stop_action_name, ("stop")); DEFINE_STATIC_LOCAL(const AtomicString, seek_to_action_name, ("seekto")); + DEFINE_STATIC_LOCAL(const AtomicString, toggle_microphone_action_name, + ("togglemicrophone")); + DEFINE_STATIC_LOCAL(const AtomicString, toggle_camera_action_name, + ("togglecamera")); + DEFINE_STATIC_LOCAL(const AtomicString, hang_up_action_name, ("hangup")); switch (action) { case MediaSessionAction::kPlay: @@ -64,6 +69,12 @@ return stop_action_name; case MediaSessionAction::kSeekTo: return seek_to_action_name; + case MediaSessionAction::kToggleMicrophone: + return toggle_microphone_action_name; + case MediaSessionAction::kToggleCamera: + return toggle_camera_action_name; + case MediaSessionAction::kHangUp: + return hang_up_action_name; default: NOTREACHED(); } @@ -90,6 +101,12 @@ return MediaSessionAction::kStop; if ("seekto" == action_name) return MediaSessionAction::kSeekTo; + if ("togglemicrophone" == action_name) + return MediaSessionAction::kToggleMicrophone; + if ("togglecamera" == action_name) + return MediaSessionAction::kToggleCamera; + if ("hangup" == action_name) + return MediaSessionAction::kHangUp; NOTREACHED(); return base::nullopt; @@ -196,6 +213,16 @@ UseCounter::Count(window, WebFeature::kMediaSessionSkipAd); } + if (!RuntimeEnabledFeatures::MediaSessionWebRTCEnabled()) { + if ("togglemicrophone" == action || "togglecamera" == action || + "hangup" == action) { + exception_state.ThrowTypeError("The provided value '" + action + + "' is not a valid enum " + "value of type MediaSessionAction."); + return; + } + } + if (handler) { auto add_result = action_handlers_.Set(action, handler); @@ -272,6 +299,31 @@ RecalculatePositionState(/*was_set=*/true); } +void MediaSession::setMicrophoneActive(bool active) { + auto* service = GetService(); + if (!service) + return; + + if (active) { + service->SetMicrophoneState( + media_session::mojom::MicrophoneState::kUnmuted); + } else { + service->SetMicrophoneState(media_session::mojom::MicrophoneState::kMuted); + } +} + +void MediaSession::setCameraActive(bool active) { + auto* service = GetService(); + if (!service) + return; + + if (active) { + service->SetCameraState(media_session::mojom::CameraState::kTurnedOn); + } else { + service->SetCameraState(media_session::mojom::CameraState::kTurnedOff); + } +} + void MediaSession::NotifyActionChange(const String& action, ActionChangeType type) { mojom::blink::MediaSessionService* service = GetService();
diff --git a/third_party/blink/renderer/modules/mediasession/media_session.h b/third_party/blink/renderer/modules/mediasession/media_session.h index 767ebea..1bc16f9 100644 --- a/third_party/blink/renderer/modules/mediasession/media_session.h +++ b/third_party/blink/renderer/modules/mediasession/media_session.h
@@ -51,6 +51,10 @@ void setPositionState(MediaPositionState*, ExceptionState&); + void setMicrophoneActive(bool active); + + void setCameraActive(bool active); + // Called by the MediaMetadata owned by |this| when it has updates. Also used // internally when a new MediaMetadata object is set. void OnMetadataChanged();
diff --git a/third_party/blink/renderer/modules/mediasession/media_session.idl b/third_party/blink/renderer/modules/mediasession/media_session.idl index 3f9e8bafb..7bf2fe1c 100644 --- a/third_party/blink/renderer/modules/mediasession/media_session.idl +++ b/third_party/blink/renderer/modules/mediasession/media_session.idl
@@ -21,7 +21,10 @@ "seekforward", "skipad", "stop", - "seekto" + "seekto", + "togglemicrophone", + "togglecamera", + "hangup" }; callback MediaSessionActionHandler = void (MediaSessionActionDetails details); @@ -38,4 +41,10 @@ [Measure, RaisesException, RuntimeEnabled=MediaSessionPosition] void setPositionState(optional MediaPositionState state = {}); + + [Measure, RuntimeEnabled=MediaSessionWebRTC] + void setMicrophoneActive(boolean active); + + [Measure, RuntimeEnabled=MediaSessionWebRTC] + void setCameraActive(boolean active); };
diff --git a/third_party/blink/renderer/modules/peerconnection/peer_connection_tracker.cc b/third_party/blink/renderer/modules/peerconnection/peer_connection_tracker.cc index 58df4c85..d7b6b34 100644 --- a/third_party/blink/renderer/modules/peerconnection/peer_connection_tracker.cc +++ b/third_party/blink/renderer/modules/peerconnection/peer_connection_tracker.cc
@@ -323,11 +323,12 @@ default: NOTREACHED(); } - return sdp_semantics_str; + return "\"" + sdp_semantics_str + "\""; } String SerializeConfiguration( - const webrtc::PeerConnectionInterface::RTCConfiguration& config) { + const webrtc::PeerConnectionInterface::RTCConfiguration& config, + bool usesInsertableStreams) { StringBuilder result; // TODO(hbos): Add serialization of certificate. result.Append("{ iceServers: "); @@ -340,9 +341,12 @@ result.Append(SerializeRtcpMuxPolicy(config.rtcp_mux_policy)); result.Append(", iceCandidatePoolSize: "); result.AppendNumber(config.ice_candidate_pool_size); - result.Append(", sdpSemantics: \""); + result.Append(", sdpSemantics: "); result.Append(SerializeSdpSemantics(config.sdp_semantics)); - result.Append("\" }"); + if (usesInsertableStreams) { + result.Append(", encodedInsertableStreams: true"); + } + result.Append(" }"); return result.ToString(); } @@ -834,7 +838,11 @@ auto info = blink::mojom::blink::PeerConnectionInfo::New(); info->lid = GetNextLocalID(); - info->rtc_configuration = SerializeConfiguration(config); + bool usesInsertableStreams = + pc_handler->force_encoded_audio_insertable_streams() && + pc_handler->force_encoded_video_insertable_streams(); + info->rtc_configuration = + SerializeConfiguration(config, usesInsertableStreams); info->constraints = SerializeMediaConstraints(constraints); if (frame) @@ -950,8 +958,12 @@ if (id == -1) return; - SendPeerConnectionUpdate(id, "setConfiguration", - SerializeConfiguration(config)); + bool usesInsertableStreams = + pc_handler->force_encoded_audio_insertable_streams() && + pc_handler->force_encoded_video_insertable_streams(); + SendPeerConnectionUpdate( + id, "setConfiguration", + SerializeConfiguration(config, usesInsertableStreams)); } void PeerConnectionTracker::TrackAddIceCandidate(
diff --git a/third_party/blink/renderer/modules/peerconnection/rtc_peer_connection.idl b/third_party/blink/renderer/modules/peerconnection/rtc_peer_connection.idl index d88ca64..74c28ea 100644 --- a/third_party/blink/renderer/modules/peerconnection/rtc_peer_connection.idl +++ b/third_party/blink/renderer/modules/peerconnection/rtc_peer_connection.idl
@@ -70,8 +70,8 @@ ] interface RTCPeerConnection : EventTarget { // TODO(guidou): There should only be one constructor argument. [CallWith=ExecutionContext, RaisesException] constructor(optional RTCConfiguration configuration = {}, optional any mediaConstraints); - [CallWith=ScriptState, RaisesException] Promise<RTCSessionDescription> createOffer(optional RTCOfferOptions options = {}); - [CallWith=ScriptState, RaisesException] Promise<RTCSessionDescription> createAnswer(optional RTCAnswerOptions options = {}); + [CallWith=ScriptState, RaisesException] Promise<RTCSessionDescriptionInit> createOffer(optional RTCOfferOptions options = {}); + [CallWith=ScriptState, RaisesException] Promise<RTCSessionDescriptionInit> createAnswer(optional RTCAnswerOptions options = {}); [CallWith=ScriptState, RaisesException] Promise<void> setLocalDescription(optional RTCSessionDescriptionInit description = {}); readonly attribute RTCSessionDescription? localDescription; readonly attribute RTCSessionDescription? currentLocalDescription;
diff --git a/third_party/blink/renderer/modules/webcodecs/BUILD.gn b/third_party/blink/renderer/modules/webcodecs/BUILD.gn index 3c5a5489..a575126 100644 --- a/third_party/blink/renderer/modules/webcodecs/BUILD.gn +++ b/third_party/blink/renderer/modules/webcodecs/BUILD.gn
@@ -17,6 +17,8 @@ "audio_encoder.h", "audio_frame.cc", "audio_frame.h", + "audio_frame_attachment.cc", + "audio_frame_attachment.h", "audio_frame_serialization_data.cc", "audio_frame_serialization_data.h", "codec_config_eval.h",
diff --git a/third_party/blink/renderer/modules/webcodecs/audio_frame.cc b/third_party/blink/renderer/modules/webcodecs/audio_frame.cc index d9ab76d..3f4d5c5b 100644 --- a/third_party/blink/renderer/modules/webcodecs/audio_frame.cc +++ b/third_party/blink/renderer/modules/webcodecs/audio_frame.cc
@@ -47,31 +47,10 @@ buffer->ReadAllFrames(wrapped_channels); } -std::unique_ptr<AudioFrameSerializationData> -AudioFrame::GetSerializationData() { - DCHECK(buffer_); - - // Copy buffer unaligned memory into media::AudioBus' aligned memory. - // TODO(https://crbug.com/1168418): reevaluate if this copy is necessary after - // our changes. E.g. If we can ever guarantee AudioBuffer's memory alignment, - // we could save this copy here, by using buffer_->GetSharedAudioBuffer() and - // wrapping it directly. - auto data_copy = - media::AudioBus::Create(buffer_->numberOfChannels(), buffer_->length()); - - for (int i = 0; i < data_copy->channels(); ++i) { - size_t byte_length = buffer_->getChannelData(i)->byteLength(); - DCHECK_EQ(byte_length, data_copy->frames() * sizeof(float)); - float* buffer_data_src = buffer_->getChannelData(i)->Data(); - memcpy(data_copy->channel(i), buffer_data_src, byte_length); - } - - return AudioFrameSerializationData::Wrap( - std::move(data_copy), buffer_->sampleRate(), - base::TimeDelta::FromMicroseconds(timestamp_)); -} - AudioFrame::AudioFrame(std::unique_ptr<AudioFrameSerializationData> data) + : AudioFrame(data.get()) {} + +AudioFrame::AudioFrame(AudioFrameSerializationData* data) : timestamp_(data->timestamp().InMicroseconds()) { media::AudioBus* data_bus = data->data(); @@ -100,6 +79,31 @@ } } +std::unique_ptr<AudioFrameSerializationData> +AudioFrame::GetSerializationData() { + if (!buffer_) + return nullptr; + + // Copy buffer unaligned memory into media::AudioBus' aligned memory. + // TODO(https://crbug.com/1168418): reevaluate if this copy is necessary after + // our changes. E.g. If we can ever guarantee AudioBuffer's memory alignment, + // we could save this copy here, by using buffer_->GetSharedAudioBuffer() and + // wrapping it directly. + auto data_copy = + media::AudioBus::Create(buffer_->numberOfChannels(), buffer_->length()); + + for (int i = 0; i < data_copy->channels(); ++i) { + size_t byte_length = buffer_->getChannelData(i)->byteLength(); + DCHECK_EQ(byte_length, data_copy->frames() * sizeof(float)); + float* buffer_data_src = buffer_->getChannelData(i)->Data(); + memcpy(data_copy->channel(i), buffer_data_src, byte_length); + } + + return AudioFrameSerializationData::Wrap( + std::move(data_copy), buffer_->sampleRate(), + base::TimeDelta::FromMicroseconds(timestamp_)); +} + void AudioFrame::close() { buffer_.Clear(); }
diff --git a/third_party/blink/renderer/modules/webcodecs/audio_frame.h b/third_party/blink/renderer/modules/webcodecs/audio_frame.h index ab27cdda..647f1d52 100644 --- a/third_party/blink/renderer/modules/webcodecs/audio_frame.h +++ b/third_party/blink/renderer/modules/webcodecs/audio_frame.h
@@ -26,6 +26,9 @@ explicit AudioFrame(scoped_refptr<media::AudioBuffer>); explicit AudioFrame(std::unique_ptr<AudioFrameSerializationData> data); + // Makes an internal copy of the data. + explicit AudioFrame(AudioFrameSerializationData* data); + // audio_frame.idl implementation. explicit AudioFrame(AudioFrameInit*); void close();
diff --git a/third_party/blink/renderer/modules/webcodecs/audio_frame_attachment.cc b/third_party/blink/renderer/modules/webcodecs/audio_frame_attachment.cc new file mode 100644 index 0000000..1b2892a --- /dev/null +++ b/third_party/blink/renderer/modules/webcodecs/audio_frame_attachment.cc
@@ -0,0 +1,11 @@ +// Copyright 2021 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "third_party/blink/renderer/modules/webcodecs/audio_frame_attachment.h" + +namespace blink { + +const void* const AudioFrameAttachment::kAttachmentKey = nullptr; + +} // namespace blink
diff --git a/third_party/blink/renderer/modules/webcodecs/audio_frame_attachment.h b/third_party/blink/renderer/modules/webcodecs/audio_frame_attachment.h new file mode 100644 index 0000000..5364c597 --- /dev/null +++ b/third_party/blink/renderer/modules/webcodecs/audio_frame_attachment.h
@@ -0,0 +1,47 @@ +// Copyright 2021 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_WEBCODECS_AUDIO_FRAME_ATTACHMENT_H_ +#define THIRD_PARTY_BLINK_RENDERER_MODULES_WEBCODECS_AUDIO_FRAME_ATTACHMENT_H_ + +#include "base/optional.h" +#include "third_party/blink/renderer/bindings/core/v8/serialization/serialized_script_value.h" +#include "third_party/blink/renderer/modules/modules_export.h" + +namespace blink { + +class AudioFrameSerializationData; + +// Used to serialize audio frames. +class MODULES_EXPORT AudioFrameAttachment + : public SerializedScriptValue::Attachment { + public: + using SerializationDataVector = + Vector<std::unique_ptr<AudioFrameSerializationData>>; + + static const void* const kAttachmentKey; + AudioFrameAttachment() = default; + ~AudioFrameAttachment() override = default; + + bool IsLockedToAgentCluster() const override { + return !audio_serialization_data_.IsEmpty(); + } + + size_t size() const { return audio_serialization_data_.size(); } + + SerializationDataVector& SerializationData() { + return audio_serialization_data_; + } + + const SerializationDataVector& SerializationData() const { + return audio_serialization_data_; + } + + private: + SerializationDataVector audio_serialization_data_; +}; + +} // namespace blink + +#endif // THIRD_PARTY_BLINK_RENDERER_MODULES_WEBCODECS_VIDEO_FRAME_ATTACHMENT_H_
diff --git a/third_party/blink/renderer/modules/webcodecs/audio_frame_serialization_data.cc b/third_party/blink/renderer/modules/webcodecs/audio_frame_serialization_data.cc index d0ec790..09a679e 100644 --- a/third_party/blink/renderer/modules/webcodecs/audio_frame_serialization_data.cc +++ b/third_party/blink/renderer/modules/webcodecs/audio_frame_serialization_data.cc
@@ -37,4 +37,5 @@ return std::make_unique<BasicAudioFrameSerializationData>( std::move(data), sample_rate, timestamp); } + } // namespace blink
diff --git a/third_party/blink/renderer/modules/websockets/inspector_websocket_events.cc b/third_party/blink/renderer/modules/websockets/inspector_websocket_events.cc index 409f11ae..fa73c0d 100644 --- a/third_party/blink/renderer/modules/websockets/inspector_websocket_events.cc +++ b/third_party/blink/renderer/modules/websockets/inspector_websocket_events.cc
@@ -5,6 +5,7 @@ #include "third_party/blink/renderer/modules/websockets/inspector_websocket_events.h" #include <memory> +#include "base/trace_event/trace_event.h" #include "third_party/blink/renderer/core/execution_context/execution_context.h" #include "third_party/blink/renderer/core/frame/local_dom_window.h" #include "third_party/blink/renderer/core/frame/local_frame.h" @@ -15,49 +16,45 @@ namespace blink { -std::unique_ptr<TracedValue> InspectorWebSocketCreateEvent::Data( - ExecutionContext* execution_context, - uint64_t identifier, - const KURL& url, - const String& protocol) { +void InspectorWebSocketCreateEvent::Data(perfetto::TracedValue context, + ExecutionContext* execution_context, + uint64_t identifier, + const KURL& url, + const String& protocol) { + auto dict = std::move(context).WriteDictionary(); DCHECK(execution_context->IsContextThread()); - auto value = std::make_unique<TracedValue>(); - value->SetInteger("identifier", static_cast<int>(identifier)); - value->SetString("url", url.GetString()); + dict.Add("identifier", identifier); + dict.Add("url", url.GetString()); if (auto* window = DynamicTo<LocalDOMWindow>(execution_context)) { - value->SetString("frame", IdentifiersFactory::FrameId(window->GetFrame())); + dict.Add("frame", IdentifiersFactory::FrameId(window->GetFrame())); } else if (auto* scope = DynamicTo<WorkerGlobalScope>(execution_context)) { - value->SetString("workerId", - IdentifiersFactory::IdFromToken( - scope->GetThread()->GetDevToolsWorkerToken())); + dict.Add("workerId", IdentifiersFactory::IdFromToken( + scope->GetThread()->GetDevToolsWorkerToken())); } else { NOTREACHED() << "WebSocket is available only in Window and WorkerGlobalScope"; } if (!protocol.IsNull()) - value->SetString("webSocketProtocol", protocol); - SetCallStack(value.get()); - return value; + dict.Add("webSocketProtocol", protocol); + SetCallStack(dict); } -std::unique_ptr<TracedValue> InspectorWebSocketEvent::Data( - ExecutionContext* execution_context, - uint64_t identifier) { +void InspectorWebSocketEvent::Data(perfetto::TracedValue context, + ExecutionContext* execution_context, + uint64_t identifier) { DCHECK(execution_context->IsContextThread()); - auto value = std::make_unique<TracedValue>(); - value->SetInteger("identifier", static_cast<int>(identifier)); + auto dict = std::move(context).WriteDictionary(); + dict.Add("identifier", identifier); if (auto* window = DynamicTo<LocalDOMWindow>(execution_context)) { - value->SetString("frame", IdentifiersFactory::FrameId(window->GetFrame())); + dict.Add("frame", IdentifiersFactory::FrameId(window->GetFrame())); } else if (auto* scope = DynamicTo<WorkerGlobalScope>(execution_context)) { - value->SetString("workerId", - IdentifiersFactory::IdFromToken( - scope->GetThread()->GetDevToolsWorkerToken())); + dict.Add("workerId", IdentifiersFactory::IdFromToken( + scope->GetThread()->GetDevToolsWorkerToken())); } else { NOTREACHED() << "WebSocket is available only in Window and WorkerGlobalScope"; } - SetCallStack(value.get()); - return value; + SetCallStack(dict); } } // namespace blink
diff --git a/third_party/blink/renderer/modules/websockets/inspector_websocket_events.h b/third_party/blink/renderer/modules/websockets/inspector_websocket_events.h index bb49b2e..22ce6dbe 100644 --- a/third_party/blink/renderer/modules/websockets/inspector_websocket_events.h +++ b/third_party/blink/renderer/modules/websockets/inspector_websocket_events.h
@@ -8,10 +8,9 @@ #include <memory> #include "third_party/blink/renderer/core/inspector/inspector_trace_events.h" #include "third_party/blink/renderer/platform/heap/handle.h" -#include "third_party/blink/renderer/platform/instrumentation/tracing/trace_event.h" -#include "third_party/blink/renderer/platform/instrumentation/tracing/traced_value.h" #include "third_party/blink/renderer/platform/wtf/forward.h" #include "third_party/blink/renderer/platform/wtf/functional.h" +#include "third_party/perfetto/include/perfetto/tracing/traced_value_forward.h" namespace blink { @@ -22,18 +21,20 @@ STATIC_ONLY(InspectorWebSocketCreateEvent); public: - static std::unique_ptr<TracedValue> Data(ExecutionContext*, - uint64_t identifier, - const KURL&, - const String& protocol); + static void Data(perfetto::TracedValue context, + ExecutionContext*, + uint64_t identifier, + const KURL&, + const String& protocol); }; class InspectorWebSocketEvent { STATIC_ONLY(InspectorWebSocketEvent); public: - static std::unique_ptr<TracedValue> Data(ExecutionContext*, - uint64_t identifier); + static void Data(perfetto::TracedValue context, + ExecutionContext*, + uint64_t identifier); }; } // namespace blink
diff --git a/third_party/blink/renderer/modules/websockets/websocket_channel_impl.cc b/third_party/blink/renderer/modules/websockets/websocket_channel_impl.cc index 12ed1eb..6eef0e8 100644 --- a/third_party/blink/renderer/modules/websockets/websocket_channel_impl.cc +++ b/third_party/blink/renderer/modules/websockets/websocket_channel_impl.cc
@@ -279,10 +279,9 @@ throttle_passed_ = true; } - TRACE_EVENT_INSTANT1("devtools.timeline", "WebSocketCreate", - TRACE_EVENT_SCOPE_THREAD, "data", - InspectorWebSocketCreateEvent::Data( - execution_context_, identifier_, url, protocol)); + DEVTOOLS_TIMELINE_TRACE_EVENT_INSTANT( + "WebSocketCreate", InspectorWebSocketCreateEvent::Data, + execution_context_, identifier_, url, protocol); probe::DidCreateWebSocket(execution_context_, identifier_, url, protocol); return true; } @@ -413,9 +412,9 @@ void WebSocketChannelImpl::Disconnect() { DVLOG(1) << this << " disconnect()"; if (identifier_) { - TRACE_EVENT_INSTANT1( - "devtools.timeline", "WebSocketDestroy", TRACE_EVENT_SCOPE_THREAD, - "data", InspectorWebSocketEvent::Data(execution_context_, identifier_)); + DEVTOOLS_TIMELINE_TRACE_EVENT_INSTANT("WebSocketDestroy", + InspectorWebSocketEvent::Data, + execution_context_, identifier_); probe::DidCloseWebSocket(execution_context_, identifier_); } @@ -454,10 +453,9 @@ DVLOG(1) << this << " OnOpeningHandshakeStarted(" << request->url.GetString() << ")"; - TRACE_EVENT_INSTANT1( - "devtools.timeline", "WebSocketSendHandshakeRequest", - TRACE_EVENT_SCOPE_THREAD, "data", - InspectorWebSocketEvent::Data(execution_context_, identifier_)); + DEVTOOLS_TIMELINE_TRACE_EVENT_INSTANT("WebSocketSendHandshakeRequest", + InspectorWebSocketEvent::Data, + execution_context_, identifier_); probe::WillSendWebSocketHandshakeRequest(execution_context_, identifier_, request.get()); handshake_request_ = std::move(request); @@ -483,10 +481,9 @@ const String& extensions = response->extensions; DVLOG(1) << this << " OnConnectionEstablished(" << protocol << ", " << extensions << ")"; - TRACE_EVENT_INSTANT1( - "devtools.timeline", "WebSocketReceiveHandshakeResponse", - TRACE_EVENT_SCOPE_THREAD, "data", - InspectorWebSocketEvent::Data(execution_context_, identifier_)); + DEVTOOLS_TIMELINE_TRACE_EVENT_INSTANT("WebSocketReceiveHandshakeResponse", + InspectorWebSocketEvent::Data, + execution_context_, identifier_); probe::DidReceiveWebSocketHandshakeResponse(execution_context_, identifier_, handshake_request_.get(), response.get()); @@ -553,9 +550,9 @@ << reason << ")"; if (identifier_) { - TRACE_EVENT_INSTANT1( - "devtools.timeline", "WebSocketDestroy", TRACE_EVENT_SCOPE_THREAD, - "data", InspectorWebSocketEvent::Data(execution_context_, identifier_)); + DEVTOOLS_TIMELINE_TRACE_EVENT_INSTANT("WebSocketDestroy", + InspectorWebSocketEvent::Data, + execution_context_, identifier_); probe::DidCloseWebSocket(execution_context_, identifier_); identifier_ = 0; }
diff --git a/third_party/blink/renderer/platform/BUILD.gn b/third_party/blink/renderer/platform/BUILD.gn index a946609..e04ec75 100644 --- a/third_party/blink/renderer/platform/BUILD.gn +++ b/third_party/blink/renderer/platform/BUILD.gn
@@ -1470,6 +1470,8 @@ "weborigin/security_origin_hash.h", "weborigin/security_policy.cc", "weborigin/security_policy.h", + "webrtc/convert_to_webrtc_video_frame_buffer.cc", + "webrtc/convert_to_webrtc_video_frame_buffer.h", "webrtc/legacy_webrtc_video_frame_adapter.cc", "webrtc/legacy_webrtc_video_frame_adapter.h", "webrtc/peer_connection_remote_audio_source.cc", @@ -1478,6 +1480,8 @@ "webrtc/track_observer.h", "webrtc/webrtc_logging.cc", "webrtc/webrtc_source.h", + "webrtc/webrtc_video_frame_adapter.cc", + "webrtc/webrtc_video_frame_adapter.h", "webrtc/webrtc_video_utils.cc", "webrtc/webrtc_video_utils.h", "widget/compositing/layer_tree_settings.cc", @@ -2145,7 +2149,9 @@ "weborigin/scheme_registry_test.cc", "weborigin/security_origin_test.cc", "weborigin/security_policy_test.cc", + "webrtc/convert_to_webrtc_video_frame_buffer_test.cc", "webrtc/legacy_webrtc_video_frame_adapter_test.cc", + "webrtc/webrtc_video_frame_adapter_test.cc", "widget/compositing/layer_tree_settings_unittest.cc", "widget/compositing/layer_tree_view_unittest.cc", "widget/compositing/render_frame_metadata_observer_impl_unittest.cc",
diff --git a/third_party/blink/renderer/platform/exported/DEPS b/third_party/blink/renderer/platform/exported/DEPS index 25a72a15..8dc689b 100644 --- a/third_party/blink/renderer/platform/exported/DEPS +++ b/third_party/blink/renderer/platform/exported/DEPS
@@ -8,7 +8,6 @@ specific_include_rules = { "notification_data_conversions_test.cc": [ - "+base/strings/nullable_string16.h", "+base/strings/utf_string_conversions.h", ], }
diff --git a/third_party/blink/renderer/platform/exported/web_runtime_features.cc b/third_party/blink/renderer/platform/exported/web_runtime_features.cc index 2d8d8c7..998b4cae 100644 --- a/third_party/blink/renderer/platform/exported/web_runtime_features.cc +++ b/third_party/blink/renderer/platform/exported/web_runtime_features.cc
@@ -251,6 +251,10 @@ RuntimeEnabledFeatures::SetMediaSessionEnabled(enable); } +void WebRuntimeFeatures::EnableMediaSessionWebRTC(bool enable) { + RuntimeEnabledFeatures::SetMediaSessionWebRTCEnabled(enable); +} + void WebRuntimeFeatures::EnableNotificationContentImage(bool enable) { RuntimeEnabledFeatures::SetNotificationContentImageEnabled(enable); }
diff --git a/third_party/blink/renderer/platform/peerconnection/rtc_video_decoder_adapter.cc b/third_party/blink/renderer/platform/peerconnection/rtc_video_decoder_adapter.cc index b382c416..ae3221d 100644 --- a/third_party/blink/renderer/platform/peerconnection/rtc_video_decoder_adapter.cc +++ b/third_party/blink/renderer/platform/peerconnection/rtc_video_decoder_adapter.cc
@@ -332,12 +332,19 @@ GetDecoderCounter()->IncrementCount(); } +#if defined(OS_ANDROID) && !BUILDFLAG(ENABLE_FFMPEG_VIDEO_DECODERS) + const bool has_software_fallback = + video_codec_type_ != webrtc::kVideoCodecH264; +#else + const bool has_software_fallback = true; +#endif + // Don't allow hardware decode for small videos if there are too many // decoder instances. This includes the case where our resolution drops while // too many decoders exist. { base::AutoLock auto_lock(lock_); - if (current_resolution_ < kMinResolution && + if (has_software_fallback && current_resolution_ < kMinResolution && GetDecoderCounter()->Count() > kMaxDecoderInstances) { // Decrement the count and clear the flag, so that other decoders don't // fall back also. @@ -428,7 +435,8 @@ return WEBRTC_VIDEO_CODEC_FALLBACK_SOFTWARE; } - if (pending_buffers_.size() >= kMaxPendingBuffers) { + if (has_software_fallback && + pending_buffers_.size() >= kMaxPendingBuffers) { // We are severely behind. Drop pending buffers and request a keyframe to // catch up as quickly as possible. DVLOG(2) << "Pending buffers overflow";
diff --git a/third_party/blink/renderer/platform/peerconnection/rtc_video_decoder_stream_adapter.cc b/third_party/blink/renderer/platform/peerconnection/rtc_video_decoder_stream_adapter.cc index 8d5db14..252c463 100644 --- a/third_party/blink/renderer/platform/peerconnection/rtc_video_decoder_stream_adapter.cc +++ b/third_party/blink/renderer/platform/peerconnection/rtc_video_decoder_stream_adapter.cc
@@ -438,8 +438,15 @@ // drop any other non-key frame. key_frame_required_ = true; +#if defined(OS_ANDROID) && !BUILDFLAG(ENABLE_FFMPEG_VIDEO_DECODERS) + const bool has_software_fallback = + video_codec_type_ != webrtc::kVideoCodecH264; +#else + const bool has_software_fallback = true; +#endif // If we hit the absolute limit, then give up. - if (pending_buffer_count_ >= kAbsoluteMaxPendingBuffers) { + if (has_software_fallback && + pending_buffer_count_ >= kAbsoluteMaxPendingBuffers) { has_error_ = true; PostCrossThreadTask( *media_task_runner_.get(), FROM_HERE,
diff --git a/third_party/blink/renderer/platform/runtime_enabled_features.json5 b/third_party/blink/renderer/platform/runtime_enabled_features.json5 index fc61624..87bdbd85 100644 --- a/third_party/blink/renderer/platform/runtime_enabled_features.json5 +++ b/third_party/blink/renderer/platform/runtime_enabled_features.json5
@@ -1291,6 +1291,9 @@ status: "stable", }, { + name: "MediaSessionWebRTC", + }, + { name: "MediaSourceExperimental", status: "experimental", },
diff --git a/third_party/blink/renderer/platform/webrtc/DEPS b/third_party/blink/renderer/platform/webrtc/DEPS index 53af1825..d15aba99 100644 --- a/third_party/blink/renderer/platform/webrtc/DEPS +++ b/third_party/blink/renderer/platform/webrtc/DEPS
@@ -4,11 +4,20 @@ ] specific_include_rules = { + "webrtc_video_frame_adapter\.cc": [ + "+base/dcheck_is_on.h", + "+gpu/command_buffer/client/raster_interface.h", + ], + "webrtc_video_frame_adapter\.h": [ + "+components/viz/common/gpu/raster_context_provider.h", + "+media/video/gpu_video_accelerator_factories.h", + ], "legacy_webrtc_video_frame_adapter\.cc": [ "+media/video/gpu_video_accelerator_factories.h", "+gpu/command_buffer/client/raster_interface.h", ], "legacy_webrtc_video_frame_adapter\.h": [ "+components/viz/common/gpu/raster_context_provider.h", + "+media/video/gpu_video_accelerator_factories.h", ], }
diff --git a/third_party/blink/renderer/platform/webrtc/convert_to_webrtc_video_frame_buffer.cc b/third_party/blink/renderer/platform/webrtc/convert_to_webrtc_video_frame_buffer.cc new file mode 100644 index 0000000..a2ec18b --- /dev/null +++ b/third_party/blink/renderer/platform/webrtc/convert_to_webrtc_video_frame_buffer.cc
@@ -0,0 +1,433 @@ +// Copyright 2015 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "third_party/blink/renderer/platform/webrtc/convert_to_webrtc_video_frame_buffer.h" + +#include "base/callback_helpers.h" +#include "base/containers/span.h" +#include "base/logging.h" +#include "base/strings/stringprintf.h" +#include "media/base/video_util.h" +#include "third_party/blink/public/common/features.h" +#include "third_party/blink/public/platform/modules/webrtc/webrtc_logging.h" +#include "third_party/blink/public/platform/platform.h" +#include "third_party/blink/renderer/platform/scheduler/public/post_cross_thread_task.h" +#include "third_party/blink/renderer/platform/scheduler/public/thread.h" +#include "third_party/blink/renderer/platform/webrtc/webrtc_video_frame_adapter.h" +#include "third_party/blink/renderer/platform/wtf/cross_thread_functional.h" +#include "third_party/blink/renderer/platform/wtf/functional.h" +#include "third_party/libyuv/include/libyuv/convert.h" +#include "third_party/libyuv/include/libyuv/convert_from_argb.h" +#include "third_party/libyuv/include/libyuv/scale.h" +#include "third_party/webrtc/api/video/i420_buffer.h" +#include "third_party/webrtc/common_video/include/video_frame_buffer.h" +#include "third_party/webrtc/common_video/libyuv/include/webrtc_libyuv.h" +#include "third_party/webrtc/rtc_base/ref_counted_object.h" +#include "ui/gfx/gpu_memory_buffer.h" + +namespace { + +class I420FrameAdapter : public webrtc::I420BufferInterface { + public: + explicit I420FrameAdapter(scoped_refptr<media::VideoFrame> frame) + : frame_(std::move(frame)) { + DCHECK_EQ(frame_->format(), media::PIXEL_FORMAT_I420); + DCHECK_EQ(frame_->visible_rect().size(), frame_->natural_size()); + } + + int width() const override { return frame_->visible_rect().width(); } + int height() const override { return frame_->visible_rect().height(); } + + const uint8_t* DataY() const override { + return frame_->visible_data(media::VideoFrame::kYPlane); + } + + const uint8_t* DataU() const override { + return frame_->visible_data(media::VideoFrame::kUPlane); + } + + const uint8_t* DataV() const override { + return frame_->visible_data(media::VideoFrame::kVPlane); + } + + int StrideY() const override { + return frame_->stride(media::VideoFrame::kYPlane); + } + + int StrideU() const override { + return frame_->stride(media::VideoFrame::kUPlane); + } + + int StrideV() const override { + return frame_->stride(media::VideoFrame::kVPlane); + } + + protected: + scoped_refptr<media::VideoFrame> frame_; +}; + +class I420AFrameAdapter : public webrtc::I420ABufferInterface { + public: + explicit I420AFrameAdapter(scoped_refptr<media::VideoFrame> frame) + : frame_(std::move(frame)) { + DCHECK_EQ(frame_->format(), media::PIXEL_FORMAT_I420A); + DCHECK_EQ(frame_->visible_rect().size(), frame_->natural_size()); + } + + int width() const override { return frame_->visible_rect().width(); } + int height() const override { return frame_->visible_rect().height(); } + + const uint8_t* DataY() const override { + return frame_->visible_data(media::VideoFrame::kYPlane); + } + + const uint8_t* DataU() const override { + return frame_->visible_data(media::VideoFrame::kUPlane); + } + + const uint8_t* DataV() const override { + return frame_->visible_data(media::VideoFrame::kVPlane); + } + + const uint8_t* DataA() const override { + return frame_->visible_data(media::VideoFrame::kAPlane); + } + + int StrideY() const override { + return frame_->stride(media::VideoFrame::kYPlane); + } + + int StrideU() const override { + return frame_->stride(media::VideoFrame::kUPlane); + } + + int StrideV() const override { + return frame_->stride(media::VideoFrame::kVPlane); + } + + int StrideA() const override { + return frame_->stride(media::VideoFrame::kAPlane); + } + + protected: + scoped_refptr<media::VideoFrame> frame_; +}; + +class NV12FrameAdapter : public webrtc::NV12BufferInterface { + public: + explicit NV12FrameAdapter(scoped_refptr<media::VideoFrame> frame) + : frame_(std::move(frame)) { + DCHECK_EQ(frame_->format(), media::PIXEL_FORMAT_NV12); + DCHECK_EQ(frame_->visible_rect().size(), frame_->natural_size()); + } + + int width() const override { return frame_->visible_rect().width(); } + int height() const override { return frame_->visible_rect().height(); } + + const uint8_t* DataY() const override { + return frame_->visible_data(media::VideoFrame::kYPlane); + } + + const uint8_t* DataUV() const override { + return frame_->visible_data(media::VideoFrame::kUVPlane); + } + + int StrideY() const override { + return frame_->stride(media::VideoFrame::kYPlane); + } + + int StrideUV() const override { + return frame_->stride(media::VideoFrame::kUVPlane); + } + + rtc::scoped_refptr<webrtc::I420BufferInterface> ToI420() override { + rtc::scoped_refptr<webrtc::I420Buffer> i420_buffer; + i420_buffer = webrtc::I420Buffer::Create(width(), height()); + libyuv::NV12ToI420(DataY(), StrideY(), DataUV(), StrideUV(), + i420_buffer->MutableDataY(), i420_buffer->StrideY(), + i420_buffer->MutableDataU(), i420_buffer->StrideU(), + i420_buffer->MutableDataV(), i420_buffer->StrideV(), + width(), height()); + return i420_buffer; + } + + protected: + scoped_refptr<media::VideoFrame> frame_; +}; + +rtc::scoped_refptr<webrtc::VideoFrameBuffer> MakeFrameAdapter( + scoped_refptr<media::VideoFrame> video_frame) { + switch (video_frame->format()) { + case media::PIXEL_FORMAT_I420: + return new rtc::RefCountedObject<I420FrameAdapter>( + std::move(video_frame)); + case media::PIXEL_FORMAT_I420A: + return new rtc::RefCountedObject<I420AFrameAdapter>( + std::move(video_frame)); + case media::PIXEL_FORMAT_NV12: + return new rtc::RefCountedObject<NV12FrameAdapter>( + std::move(video_frame)); + default: + NOTREACHED(); + return nullptr; + } +} + +scoped_refptr<media::VideoFrame> MakeScaledI420VideoFrame( + scoped_refptr<media::VideoFrame> source_frame, + scoped_refptr<blink::WebRtcVideoFrameAdapter::SharedResources> + shared_resources) { + // ARGB pixel format may be produced by readback of texture backed frames. + DCHECK(source_frame->format() == media::PIXEL_FORMAT_NV12 || + source_frame->format() == media::PIXEL_FORMAT_I420 || + source_frame->format() == media::PIXEL_FORMAT_I420A || + source_frame->format() == media::PIXEL_FORMAT_ARGB); + const bool has_alpha = source_frame->format() == media::PIXEL_FORMAT_I420A; + // Convert to I420 and scale to the natural size specified in + // |source_frame|. + auto dst_frame = shared_resources->CreateFrame( + has_alpha ? media::PIXEL_FORMAT_I420A : media::PIXEL_FORMAT_I420, + source_frame->natural_size(), gfx::Rect(source_frame->natural_size()), + source_frame->natural_size(), source_frame->timestamp()); + if (!dst_frame) { + LOG(ERROR) << "Failed to create I420 frame from pool."; + return nullptr; + } + dst_frame->metadata().MergeMetadataFrom(source_frame->metadata()); + + switch (source_frame->format()) { + case media::PIXEL_FORMAT_I420A: + libyuv::ScalePlane(source_frame->visible_data(media::VideoFrame::kAPlane), + source_frame->stride(media::VideoFrame::kAPlane), + source_frame->visible_rect().width(), + source_frame->visible_rect().height(), + dst_frame->data(media::VideoFrame::kAPlane), + dst_frame->stride(media::VideoFrame::kAPlane), + dst_frame->coded_size().width(), + dst_frame->coded_size().height(), + libyuv::kFilterBilinear); + // Fallthrough to I420 in order to scale the YUV planes as well. + ABSL_FALLTHROUGH_INTENDED; + case media::PIXEL_FORMAT_I420: + libyuv::I420Scale(source_frame->visible_data(media::VideoFrame::kYPlane), + source_frame->stride(media::VideoFrame::kYPlane), + source_frame->visible_data(media::VideoFrame::kUPlane), + source_frame->stride(media::VideoFrame::kUPlane), + source_frame->visible_data(media::VideoFrame::kVPlane), + source_frame->stride(media::VideoFrame::kVPlane), + source_frame->visible_rect().width(), + source_frame->visible_rect().height(), + dst_frame->data(media::VideoFrame::kYPlane), + dst_frame->stride(media::VideoFrame::kYPlane), + dst_frame->data(media::VideoFrame::kUPlane), + dst_frame->stride(media::VideoFrame::kUPlane), + dst_frame->data(media::VideoFrame::kVPlane), + dst_frame->stride(media::VideoFrame::kVPlane), + dst_frame->coded_size().width(), + dst_frame->coded_size().height(), + libyuv::kFilterBilinear); + break; + case media::PIXEL_FORMAT_NV12: { + webrtc::NV12ToI420Scaler scaler; + scaler.NV12ToI420Scale( + source_frame->visible_data(media::VideoFrame::kYPlane), + source_frame->stride(media::VideoFrame::kYPlane), + source_frame->visible_data(media::VideoFrame::kUVPlane), + source_frame->stride(media::VideoFrame::kUVPlane), + source_frame->visible_rect().width(), + source_frame->visible_rect().height(), + dst_frame->data(media::VideoFrame::kYPlane), + dst_frame->stride(media::VideoFrame::kYPlane), + dst_frame->data(media::VideoFrame::kUPlane), + dst_frame->stride(media::VideoFrame::kUPlane), + dst_frame->data(media::VideoFrame::kVPlane), + dst_frame->stride(media::VideoFrame::kVPlane), + dst_frame->coded_size().width(), dst_frame->coded_size().height()); + } break; + case media::PIXEL_FORMAT_ARGB: { + auto visible_size = source_frame->visible_rect().size(); + if (visible_size == dst_frame->coded_size()) { + // Direct conversion to dst_frame with no scaling. + libyuv::ARGBToI420( + source_frame->visible_data(media::VideoFrame::kARGBPlane), + source_frame->stride(media::VideoFrame::kARGBPlane), + dst_frame->data(media::VideoFrame::kYPlane), + dst_frame->stride(media::VideoFrame::kYPlane), + dst_frame->data(media::VideoFrame::kUPlane), + dst_frame->stride(media::VideoFrame::kUPlane), + dst_frame->data(media::VideoFrame::kVPlane), + dst_frame->stride(media::VideoFrame::kVPlane), visible_size.width(), + visible_size.height()); + } else { + // Convert to I420 tmp image and then scale to the dst_frame. + auto tmp_frame = shared_resources->CreateTemporaryFrame( + media::PIXEL_FORMAT_I420, visible_size, gfx::Rect(visible_size), + visible_size, source_frame->timestamp()); + libyuv::ARGBToI420( + source_frame->visible_data(media::VideoFrame::kARGBPlane), + source_frame->stride(media::VideoFrame::kARGBPlane), + tmp_frame->data(media::VideoFrame::kYPlane), + tmp_frame->stride(media::VideoFrame::kYPlane), + tmp_frame->data(media::VideoFrame::kUPlane), + tmp_frame->stride(media::VideoFrame::kUPlane), + tmp_frame->data(media::VideoFrame::kVPlane), + tmp_frame->stride(media::VideoFrame::kVPlane), visible_size.width(), + visible_size.height()); + libyuv::I420Scale( + tmp_frame->data(media::VideoFrame::kYPlane), + tmp_frame->stride(media::VideoFrame::kYPlane), + tmp_frame->data(media::VideoFrame::kUPlane), + tmp_frame->stride(media::VideoFrame::kUPlane), + tmp_frame->data(media::VideoFrame::kVPlane), + tmp_frame->stride(media::VideoFrame::kVPlane), visible_size.width(), + visible_size.height(), dst_frame->data(media::VideoFrame::kYPlane), + dst_frame->stride(media::VideoFrame::kYPlane), + dst_frame->data(media::VideoFrame::kUPlane), + dst_frame->stride(media::VideoFrame::kUPlane), + dst_frame->data(media::VideoFrame::kVPlane), + dst_frame->stride(media::VideoFrame::kVPlane), + dst_frame->coded_size().width(), dst_frame->coded_size().height(), + libyuv::kFilterBilinear); + } + } break; + default: + NOTREACHED(); + } + return dst_frame; +} + +scoped_refptr<media::VideoFrame> MakeScaledNV12VideoFrame( + scoped_refptr<media::VideoFrame> source_frame, + scoped_refptr<blink::WebRtcVideoFrameAdapter::SharedResources> + shared_resources) { + DCHECK_EQ(source_frame->format(), media::PIXEL_FORMAT_NV12); + auto dst_frame = shared_resources->CreateFrame( + media::PIXEL_FORMAT_NV12, source_frame->natural_size(), + gfx::Rect(source_frame->natural_size()), source_frame->natural_size(), + source_frame->timestamp()); + dst_frame->metadata().MergeMetadataFrom(source_frame->metadata()); + const auto& nv12_planes = dst_frame->layout().planes(); + libyuv::NV12Scale(source_frame->visible_data(media::VideoFrame::kYPlane), + source_frame->stride(media::VideoFrame::kYPlane), + source_frame->visible_data(media::VideoFrame::kUVPlane), + source_frame->stride(media::VideoFrame::kUVPlane), + source_frame->visible_rect().width(), + source_frame->visible_rect().height(), + dst_frame->data(media::VideoFrame::kYPlane), + nv12_planes[media::VideoFrame::kYPlane].stride, + dst_frame->data(media::VideoFrame::kUVPlane), + nv12_planes[media::VideoFrame::kUVPlane].stride, + dst_frame->coded_size().width(), + dst_frame->coded_size().height(), libyuv::kFilterBox); + return dst_frame; +} + +scoped_refptr<media::VideoFrame> MaybeConvertAndScaleFrame( + scoped_refptr<media::VideoFrame> source_frame, + scoped_refptr<blink::WebRtcVideoFrameAdapter::SharedResources> + shared_resources) { + if (!source_frame) + return nullptr; + // Texture frames may be readback in ARGB format. + RTC_DCHECK(source_frame->format() == media::PIXEL_FORMAT_I420 || + source_frame->format() == media::PIXEL_FORMAT_I420A || + source_frame->format() == media::PIXEL_FORMAT_NV12 || + source_frame->format() == media::PIXEL_FORMAT_ARGB); + RTC_DCHECK(shared_resources); + + const bool allow_nv12_output = + base::FeatureList::IsEnabled(blink::features::kWebRtcLibvpxEncodeNV12); + const bool source_is_i420 = + source_frame->format() == media::PIXEL_FORMAT_I420 || + source_frame->format() == media::PIXEL_FORMAT_I420A; + const bool source_is_nv12 = + source_frame->format() == media::PIXEL_FORMAT_NV12; + const bool no_scaling_needed = + source_frame->natural_size() == source_frame->visible_rect().size(); + + if (((source_is_nv12 && allow_nv12_output) || source_is_i420) && + no_scaling_needed) { + // |source_frame| already has correct pixel format and resolution. + return source_frame; + } else if (source_is_nv12 && allow_nv12_output) { + // Output NV12 only if it is allowed and no conversion is needed. + return MakeScaledNV12VideoFrame(std::move(source_frame), + std::move(shared_resources)); + } else { + return MakeScaledI420VideoFrame(std::move(source_frame), + std::move(shared_resources)); + } +} + +} // anonymous namespace + +namespace blink { + +// static +bool CanConvertToWebRtcVideoFrameBuffer(const media::VideoFrame* frame) { + // Currently accept I420, I420A, NV12 formats in a mapped frame, + // or a texture or GPU memory buffer frame. + return (frame->IsMappable() && + base::Contains(GetPixelFormatsMappableToWebRtcVideoFrameBuffer(), + frame->format())) || + frame->storage_type() == + media::VideoFrame::STORAGE_GPU_MEMORY_BUFFER || + frame->HasTextures(); +} + +// static +base::span<const media::VideoPixelFormat> +GetPixelFormatsMappableToWebRtcVideoFrameBuffer() { + static constexpr const media::VideoPixelFormat + kGetPixelFormatsMappableToWebRtcVideoFrameBuffer[] = { + media::PIXEL_FORMAT_I420, media::PIXEL_FORMAT_I420A, + media::PIXEL_FORMAT_NV12}; + return base::make_span(kGetPixelFormatsMappableToWebRtcVideoFrameBuffer); +} + +rtc::scoped_refptr<webrtc::VideoFrameBuffer> ConvertToWebRtcVideoFrameBuffer( + scoped_refptr<media::VideoFrame> video_frame, + scoped_refptr<WebRtcVideoFrameAdapter::SharedResources> shared_resources) { + DCHECK(CanConvertToWebRtcVideoFrameBuffer(video_frame.get())) + << "Can not create WebRTC frame buffer for frame " + << video_frame->AsHumanReadableString(); + + if (video_frame->storage_type() == + media::VideoFrame::StorageType::STORAGE_GPU_MEMORY_BUFFER) { + auto converted_frame = + shared_resources + ? shared_resources->ConstructVideoFrameFromGpu(video_frame) + : nullptr; + converted_frame = + MaybeConvertAndScaleFrame(converted_frame, shared_resources); + if (!converted_frame) { + return MakeFrameAdapter(media::VideoFrame::CreateColorFrame( + video_frame->natural_size(), 0u, 0x80, 0x80, + video_frame->timestamp())); + } + return MakeFrameAdapter(std::move(converted_frame)); + } else if (video_frame->HasTextures()) { + auto converted_frame = + shared_resources + ? shared_resources->ConstructVideoFrameFromTexture(video_frame) + : nullptr; + converted_frame = + MaybeConvertAndScaleFrame(converted_frame, shared_resources); + if (!converted_frame) { + DLOG(ERROR) << "Texture backed frame cannot be accessed."; + return MakeFrameAdapter(media::VideoFrame::CreateColorFrame( + video_frame->natural_size(), 0u, 0x80, 0x80, + video_frame->timestamp())); + } + return MakeFrameAdapter(std::move(converted_frame)); + } + + // Since scaling is required, hard-apply both the cropping and scaling + // before we hand the frame over to WebRTC. + scoped_refptr<media::VideoFrame> scaled_frame = + MaybeConvertAndScaleFrame(video_frame, shared_resources); + return MakeFrameAdapter(std::move(scaled_frame)); +} + +} // namespace blink
diff --git a/third_party/blink/renderer/platform/webrtc/convert_to_webrtc_video_frame_buffer.h b/third_party/blink/renderer/platform/webrtc/convert_to_webrtc_video_frame_buffer.h new file mode 100644 index 0000000..9c421dee --- /dev/null +++ b/third_party/blink/renderer/platform/webrtc/convert_to_webrtc_video_frame_buffer.h
@@ -0,0 +1,30 @@ +// Copyright 2015 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_WEBRTC_CONVERT_TO_WEBRTC_VIDEO_FRAME_BUFFER_H_ +#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_WEBRTC_CONVERT_TO_WEBRTC_VIDEO_FRAME_BUFFER_H_ + +#include "base/containers/span.h" +#include "base/memory/ref_counted.h" +#include "media/base/video_frame.h" +#include "third_party/blink/renderer/platform/platform_export.h" +#include "third_party/blink/renderer/platform/webrtc/webrtc_video_frame_adapter.h" +#include "third_party/webrtc/api/video/video_frame_buffer.h" + +namespace blink { + +PLATFORM_EXPORT bool CanConvertToWebRtcVideoFrameBuffer( + const media::VideoFrame* frame); + +PLATFORM_EXPORT base::span<const media::VideoPixelFormat> +GetPixelFormatsMappableToWebRtcVideoFrameBuffer(); + +PLATFORM_EXPORT rtc::scoped_refptr<webrtc::VideoFrameBuffer> +ConvertToWebRtcVideoFrameBuffer( + scoped_refptr<media::VideoFrame> video_frame, + scoped_refptr<WebRtcVideoFrameAdapter::SharedResources> shared_resources); + +} // namespace blink + +#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_WEBRTC_CONVERT_TO_WEBRTC_VIDEO_FRAME_BUFFER_H_
diff --git a/third_party/blink/renderer/platform/webrtc/convert_to_webrtc_video_frame_buffer_test.cc b/third_party/blink/renderer/platform/webrtc/convert_to_webrtc_video_frame_buffer_test.cc new file mode 100644 index 0000000..789be57 --- /dev/null +++ b/third_party/blink/renderer/platform/webrtc/convert_to_webrtc_video_frame_buffer_test.cc
@@ -0,0 +1,335 @@ +// Copyright 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "third_party/blink/renderer/platform/webrtc/convert_to_webrtc_video_frame_buffer.h" + +#include "base/strings/strcat.h" +#include "base/test/scoped_feature_list.h" +#include "media/base/video_frame.h" +#include "testing/gmock/include/gmock/gmock.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "third_party/blink/public/common/features.h" +#include "third_party/blink/renderer/platform/testing/video_frame_utils.h" +#include "third_party/blink/renderer/platform/webrtc/testing/mock_webrtc_video_frame_adapter_shared_resources.h" +#include "third_party/blink/renderer/platform/webrtc/webrtc_video_frame_adapter.h" +#include "third_party/webrtc/api/video/video_frame_buffer.h" +#include "third_party/webrtc/rtc_base/ref_counted_object.h" +#include "ui/gfx/gpu_memory_buffer.h" + +using ::testing::_; +using ::testing::Return; + +namespace blink { + +class ConvertToWebRtcVideoFrameBufferParamTest + : public ::testing::TestWithParam< + std::tuple<media::VideoFrame::StorageType, media::VideoPixelFormat>> { + public: + ConvertToWebRtcVideoFrameBufferParamTest() + : resources_(new WebRtcVideoFrameAdapter::SharedResources(nullptr)) {} + + protected: + scoped_refptr<WebRtcVideoFrameAdapter::SharedResources> resources_; +}; + +namespace { +std::vector<ConvertToWebRtcVideoFrameBufferParamTest::ParamType> TestParams() { + std::vector<ConvertToWebRtcVideoFrameBufferParamTest::ParamType> test_params; + // All formats for owned memory. + for (media::VideoPixelFormat format : + GetPixelFormatsMappableToWebRtcVideoFrameBuffer()) { + test_params.emplace_back( + media::VideoFrame::StorageType::STORAGE_OWNED_MEMORY, format); + } + test_params.emplace_back( + media::VideoFrame::StorageType::STORAGE_GPU_MEMORY_BUFFER, + media::VideoPixelFormat::PIXEL_FORMAT_NV12); + return test_params; +} +} // namespace + +TEST_P(ConvertToWebRtcVideoFrameBufferParamTest, ToI420) { + const gfx::Size kCodedSize(1280, 960); + const gfx::Rect kVisibleRect(0, 120, 1280, 720); + const gfx::Size kNaturalSize(640, 360); + + media::VideoFrame::StorageType storage_type = std::get<0>(GetParam()); + media::VideoPixelFormat pixel_format = std::get<1>(GetParam()); + scoped_refptr<media::VideoFrame> frame = CreateTestFrame( + kCodedSize, kVisibleRect, kNaturalSize, storage_type, pixel_format); + rtc::scoped_refptr<webrtc::VideoFrameBuffer> frame_buffer = + ConvertToWebRtcVideoFrameBuffer(std::move(frame), resources_); + + // The I420 frame should have the same size as the natural size. + auto i420_frame = frame_buffer->ToI420(); + EXPECT_EQ(i420_frame->width(), kNaturalSize.width()); + EXPECT_EQ(i420_frame->height(), kNaturalSize.height()); +} + +INSTANTIATE_TEST_CASE_P( + ConvertToWebRtcVideoFrameBufferParamTest, + ConvertToWebRtcVideoFrameBufferParamTest, + ::testing::ValuesIn(TestParams()), + [](const auto& info) { + return base::StrCat( + {media::VideoFrame::StorageTypeToString(std::get<0>(info.param)), "_", + media::VideoPixelFormatToString(std::get<1>(info.param))}); + }); + +TEST(ConvertToWebRtcVideoFrameBufferTest, ToI420DownScaleGmb) { + base::test::ScopedFeatureList scoped_feautre_list; + scoped_feautre_list.InitAndDisableFeature( + blink::features::kWebRtcLibvpxEncodeNV12); + const gfx::Size kCodedSize(1280, 960); + const gfx::Rect kVisibleRect(0, 120, 1280, 720); + const gfx::Size kNaturalSize(640, 360); + scoped_refptr<WebRtcVideoFrameAdapter::SharedResources> resources = + new WebRtcVideoFrameAdapter::SharedResources(nullptr); + auto gmb_frame = + CreateTestFrame(kCodedSize, kVisibleRect, kNaturalSize, + media::VideoFrame::STORAGE_GPU_MEMORY_BUFFER); + + // The adapter should report width and height from the natural size for + // VideoFrame backed by GpuMemoryBuffer. + rtc::scoped_refptr<webrtc::VideoFrameBuffer> gmb_frame_buffer = + ConvertToWebRtcVideoFrameBuffer(std::move(gmb_frame), resources); + EXPECT_EQ(gmb_frame_buffer->width(), kNaturalSize.width()); + EXPECT_EQ(gmb_frame_buffer->height(), kNaturalSize.height()); + + // The I420 frame should have the same size as the natural size + auto i420_frame = gmb_frame_buffer->ToI420(); + ASSERT_TRUE(i420_frame); + EXPECT_EQ(i420_frame->width(), kNaturalSize.width()); + EXPECT_EQ(i420_frame->height(), kNaturalSize.height()); + auto* get_i420_frame = gmb_frame_buffer->GetI420(); + ASSERT_TRUE(get_i420_frame); + EXPECT_EQ(get_i420_frame->width(), kNaturalSize.width()); + EXPECT_EQ(get_i420_frame->height(), kNaturalSize.height()); +} + +TEST(ConvertToWebRtcVideoFrameBufferTest, ToI420ADownScale) { + const gfx::Size kCodedSize(1280, 960); + const gfx::Rect kVisibleRect(0, 120, 1280, 720); + const gfx::Size kNaturalSize(640, 360); + scoped_refptr<WebRtcVideoFrameAdapter::SharedResources> resources = + new WebRtcVideoFrameAdapter::SharedResources(nullptr); + + // The adapter should report width and height from the natural size for + // VideoFrame backed by owned memory. + auto owned_memory_frame = + CreateTestFrame(kCodedSize, kVisibleRect, kNaturalSize, + media::VideoFrame::STORAGE_OWNED_MEMORY, + media::VideoPixelFormat::PIXEL_FORMAT_I420A); + rtc::scoped_refptr<webrtc::VideoFrameBuffer> owned_memory_frame_buffer = + ConvertToWebRtcVideoFrameBuffer(std::move(owned_memory_frame), resources); + EXPECT_EQ(owned_memory_frame_buffer->width(), kNaturalSize.width()); + EXPECT_EQ(owned_memory_frame_buffer->height(), kNaturalSize.height()); + + // The I420A frame should have the same size as the natural size + auto i420a_frame = owned_memory_frame_buffer->ToI420(); + ASSERT_TRUE(i420a_frame); + EXPECT_EQ(webrtc::VideoFrameBuffer::Type::kI420A, i420a_frame->type()); + EXPECT_EQ(i420a_frame->width(), kNaturalSize.width()); + EXPECT_EQ(i420a_frame->height(), kNaturalSize.height()); +} + +TEST(ConvertToWebRtcVideoFrameBufferTest, + Nv12WrapsGmbWhenNoScalingNeeededWithFeature) { + base::test::ScopedFeatureList scoped_feautre_list; + scoped_feautre_list.InitAndEnableFeature( + blink::features::kWebRtcLibvpxEncodeNV12); + const gfx::Size kCodedSize(1280, 960); + const gfx::Rect kVisibleRect(0, 120, 1280, 720); + // Same size as visible rect so no scaling. + const gfx::Size kNaturalSize = kVisibleRect.size(); + scoped_refptr<WebRtcVideoFrameAdapter::SharedResources> resources = + new WebRtcVideoFrameAdapter::SharedResources(nullptr); + + auto gmb_frame = + CreateTestFrame(kCodedSize, kVisibleRect, kNaturalSize, + media::VideoFrame::STORAGE_GPU_MEMORY_BUFFER); + + // The adapter should report width and height from the natural size for + // VideoFrame backed by GpuMemoryBuffer. + rtc::scoped_refptr<webrtc::VideoFrameBuffer> gmb_frame_buffer = + ConvertToWebRtcVideoFrameBuffer(std::move(gmb_frame), resources); + EXPECT_EQ(gmb_frame_buffer->width(), kNaturalSize.width()); + EXPECT_EQ(gmb_frame_buffer->height(), kNaturalSize.height()); + + // Under feature, expect that the adapted frame is NV12 with frame should + // have the same size as the natural size. + auto* nv12_frame = gmb_frame_buffer->GetNV12(); + ASSERT_TRUE(nv12_frame); + EXPECT_EQ(webrtc::VideoFrameBuffer::Type::kNV12, nv12_frame->type()); + EXPECT_EQ(nv12_frame->width(), kNaturalSize.width()); + EXPECT_EQ(nv12_frame->height(), kNaturalSize.height()); + + // Even though we have an NV12 frame, ToI420 should return an I420 frame. + EXPECT_FALSE(gmb_frame_buffer->GetI420()); + auto i420_frame = gmb_frame_buffer->ToI420(); + ASSERT_TRUE(i420_frame); + EXPECT_EQ(i420_frame->width(), kNaturalSize.width()); + EXPECT_EQ(i420_frame->height(), kNaturalSize.height()); +} + +TEST(ConvertToWebRtcVideoFrameBufferTest, Nv12ScalesGmbWithFeature) { + base::test::ScopedFeatureList scoped_feautre_list; + scoped_feautre_list.InitAndEnableFeature( + blink::features::kWebRtcLibvpxEncodeNV12); + const gfx::Size kCodedSize(1280, 960); + const gfx::Rect kVisibleRect(0, 120, 1280, 720); + const gfx::Size kNaturalSize(640, 360); + scoped_refptr<WebRtcVideoFrameAdapter::SharedResources> resources = + new WebRtcVideoFrameAdapter::SharedResources(nullptr); + + auto gmb_frame = + CreateTestFrame(kCodedSize, kVisibleRect, kNaturalSize, + media::VideoFrame::STORAGE_GPU_MEMORY_BUFFER); + + // The adapter should report width and height from the natural size for + // VideoFrame backed by GpuMemoryBuffer. + rtc::scoped_refptr<webrtc::VideoFrameBuffer> gmb_frame_buffer = + ConvertToWebRtcVideoFrameBuffer(gmb_frame, resources); + EXPECT_EQ(gmb_frame_buffer->width(), kNaturalSize.width()); + EXPECT_EQ(gmb_frame_buffer->height(), kNaturalSize.height()); + + // Under feature, expect that the adapted frame is NV12 with frame should + // have the same size as the natural size. + auto* nv12_frame = gmb_frame_buffer->GetNV12(); + ASSERT_TRUE(nv12_frame); + EXPECT_EQ(webrtc::VideoFrameBuffer::Type::kNV12, nv12_frame->type()); + EXPECT_EQ(nv12_frame->width(), kNaturalSize.width()); + EXPECT_EQ(nv12_frame->height(), kNaturalSize.height()); + + // Even though we have an NV12 frame, ToI420 should return an I420 frame. + EXPECT_FALSE(gmb_frame_buffer->GetI420()); + auto i420_frame = gmb_frame_buffer->ToI420(); + ASSERT_TRUE(i420_frame); + EXPECT_EQ(i420_frame->width(), kNaturalSize.width()); + EXPECT_EQ(i420_frame->height(), kNaturalSize.height()); +} + +TEST(ConvertToWebRtcVideoFrameBufferTest, Nv12OwnedMemoryFrame) { + const gfx::Size kCodedSize(1280, 960); + const gfx::Rect kVisibleRect(0, 120, 1280, 720); + const gfx::Size kNaturalSize = kVisibleRect.size(); + scoped_refptr<WebRtcVideoFrameAdapter::SharedResources> resources = + new WebRtcVideoFrameAdapter::SharedResources(nullptr); + + // The adapter should report width and height from the natural size for + // VideoFrame backed by owned memory. + auto owned_memory_frame = + CreateTestFrame(kCodedSize, kVisibleRect, kNaturalSize, + media::VideoFrame::STORAGE_OWNED_MEMORY, + media::VideoPixelFormat::PIXEL_FORMAT_NV12); + rtc::scoped_refptr<webrtc::VideoFrameBuffer> owned_memory_frame_buffer = + ConvertToWebRtcVideoFrameBuffer(std::move(owned_memory_frame), resources); + EXPECT_EQ(owned_memory_frame_buffer->width(), kNaturalSize.width()); + EXPECT_EQ(owned_memory_frame_buffer->height(), kNaturalSize.height()); + + // The NV12 frame should have the same size as the visible rect size + auto* nv12_frame = owned_memory_frame_buffer->GetNV12(); + ASSERT_TRUE(nv12_frame); + EXPECT_EQ(webrtc::VideoFrameBuffer::Type::kNV12, nv12_frame->type()); + EXPECT_EQ(nv12_frame->width(), kVisibleRect.size().width()); + EXPECT_EQ(nv12_frame->height(), kVisibleRect.size().height()); +} + +TEST(ConvertToWebRtcVideoFrameBufferTest, Nv12ScaleOwnedMemoryFrame) { + const gfx::Size kCodedSize(1280, 960); + const gfx::Rect kVisibleRect(0, 120, 1280, 720); + const gfx::Size kNaturalSize(640, 360); + scoped_refptr<WebRtcVideoFrameAdapter::SharedResources> resources = + new WebRtcVideoFrameAdapter::SharedResources(nullptr); + + // The adapter should report width and height from the natural size for + // VideoFrame backed by owned memory. + auto owned_memory_frame = + CreateTestFrame(kCodedSize, kVisibleRect, kNaturalSize, + media::VideoFrame::STORAGE_OWNED_MEMORY, + media::VideoPixelFormat::PIXEL_FORMAT_NV12); + rtc::scoped_refptr<webrtc::VideoFrameBuffer> owned_memory_frame_buffer = + ConvertToWebRtcVideoFrameBuffer(std::move(owned_memory_frame), resources); + EXPECT_EQ(owned_memory_frame_buffer->width(), kNaturalSize.width()); + EXPECT_EQ(owned_memory_frame_buffer->height(), kNaturalSize.height()); + + // The NV12 frame should have the same size as the natural size. + auto* nv12_frame = owned_memory_frame_buffer->GetNV12(); + ASSERT_TRUE(nv12_frame); + EXPECT_EQ(webrtc::VideoFrameBuffer::Type::kNV12, nv12_frame->type()); + EXPECT_EQ(nv12_frame->width(), kNaturalSize.width()); + EXPECT_EQ(nv12_frame->height(), kNaturalSize.height()); +} + +TEST(ConvertToWebRtcVideoFrameBufferTest, + TextureFrameIsBlackWithNoSharedResources) { + const gfx::Size kCodedSize(1280, 960); + const gfx::Rect kVisibleRect(0, 120, 1280, 720); + const gfx::Size kNaturalSize(640, 360); + + // The adapter should report width and height from the natural size for + // VideoFrame backed by owned memory. + auto owned_memory_frame = CreateTestFrame( + kCodedSize, kVisibleRect, kNaturalSize, media::VideoFrame::STORAGE_OPAQUE, + media::VideoPixelFormat::PIXEL_FORMAT_NV12); + rtc::scoped_refptr<webrtc::VideoFrameBuffer> frame_buffer = + ConvertToWebRtcVideoFrameBuffer(std::move(owned_memory_frame), nullptr); + EXPECT_EQ(frame_buffer->width(), kNaturalSize.width()); + EXPECT_EQ(frame_buffer->height(), kNaturalSize.height()); + + // The NV12 frame should have the same size as the natural size, but be black + // since we can't handle the texture with no shared resources. + auto i420_frame = frame_buffer->ToI420(); + ASSERT_TRUE(i420_frame); + EXPECT_EQ(i420_frame->width(), kNaturalSize.width()); + EXPECT_EQ(i420_frame->height(), kNaturalSize.height()); + EXPECT_EQ(0x0, i420_frame->DataY()[0]); + EXPECT_EQ(0x80, i420_frame->DataU()[0]); + EXPECT_EQ(0x80, i420_frame->DataV()[0]); +} + +TEST(ConvertToWebRtcVideoFrameBufferTest, + ConvertsTextureFrameWithSharedResources) { + const gfx::Size kCodedSize(1280, 960); + const gfx::Rect kVisibleRect(0, 120, 1280, 720); + const gfx::Size kNaturalSize(640, 360); + + scoped_refptr<MockSharedResources> resources = + base::MakeRefCounted<MockSharedResources>(); + + // The adapter should report width and height from the natural size for + // VideoFrame backed by owned memory. + auto owned_memory_frame = CreateTestFrame( + kCodedSize, kVisibleRect, kNaturalSize, media::VideoFrame::STORAGE_OPAQUE, + media::VideoPixelFormat::PIXEL_FORMAT_NV12); + + scoped_refptr<media::VideoFrame> memory_frame = + CreateTestFrame(kCodedSize, kVisibleRect, kNaturalSize, + media::VideoFrame::STORAGE_OWNED_MEMORY, + media::VideoPixelFormat::PIXEL_FORMAT_ARGB); + // fill mock image with whilte color. + memset(memory_frame->data(media::VideoFrame::kARGBPlane), 0xFF, + kCodedSize.GetArea() * 4); + + // Should call texture conversion. + resources->ExpectCreateFrameWithRealImplementation(); + resources->ExpectCreateTemporaryFrameWithRealImplementation(); + EXPECT_CALL(*resources, ConstructVideoFrameFromTexture(_)) + .WillOnce(Return(memory_frame)); + + rtc::scoped_refptr<webrtc::VideoFrameBuffer> frame_buffer = + ConvertToWebRtcVideoFrameBuffer(std::move(owned_memory_frame), resources); + EXPECT_EQ(frame_buffer->width(), kNaturalSize.width()); + EXPECT_EQ(frame_buffer->height(), kNaturalSize.height()); + + // The NV12 frame should have the same size as the natural size, but be black + // since we can't handle the texture with no shared resources. + auto i420_frame = frame_buffer->ToI420(); + ASSERT_TRUE(i420_frame); + EXPECT_EQ(i420_frame->width(), kNaturalSize.width()); + EXPECT_EQ(i420_frame->height(), kNaturalSize.height()); + // Returned memory frame should not be replaced by a black frame. + EXPECT_NE(0x0, i420_frame->DataY()[0]); +} +} // namespace blink
diff --git a/third_party/blink/renderer/platform/webrtc/legacy_webrtc_video_frame_adapter_test.cc b/third_party/blink/renderer/platform/webrtc/legacy_webrtc_video_frame_adapter_test.cc index c747960..10ffd6a 100644 --- a/third_party/blink/renderer/platform/webrtc/legacy_webrtc_video_frame_adapter_test.cc +++ b/third_party/blink/renderer/platform/webrtc/legacy_webrtc_video_frame_adapter_test.cc
@@ -331,8 +331,8 @@ const gfx::Rect kVisibleRect(0, 120, 1280, 720); const gfx::Size kNaturalSize(640, 360); - scoped_refptr<MockSharedResources> resources = - base::MakeRefCounted<MockSharedResources>(); + scoped_refptr<MockLegacySharedResources> resources = + base::MakeRefCounted<MockLegacySharedResources>(); // The adapter should report width and height from the natural size for // VideoFrame backed by owned memory.
diff --git a/third_party/blink/renderer/platform/webrtc/testing/mock_webrtc_video_frame_adapter_shared_resources.h b/third_party/blink/renderer/platform/webrtc/testing/mock_webrtc_video_frame_adapter_shared_resources.h index d637106..58652f1 100644 --- a/third_party/blink/renderer/platform/webrtc/testing/mock_webrtc_video_frame_adapter_shared_resources.h +++ b/third_party/blink/renderer/platform/webrtc/testing/mock_webrtc_video_frame_adapter_shared_resources.h
@@ -6,15 +6,75 @@ #define THIRD_PARTY_BLINK_RENDERER_PLATFORM_WEBRTC_TESTING_MOCK_WEBRTC_VIDEO_FRAME_ADAPTER_SHARED_RESOURCES_H_ #include "third_party/blink/renderer/platform/webrtc/legacy_webrtc_video_frame_adapter.h" +#include "third_party/blink/renderer/platform/webrtc/webrtc_video_frame_adapter.h" #include "testing/gmock/include/gmock/gmock.h" namespace blink { -class MockSharedResources +class MockSharedResources : public WebRtcVideoFrameAdapter::SharedResources { + public: + MockSharedResources() : WebRtcVideoFrameAdapter::SharedResources(nullptr) {} + + MOCK_METHOD(scoped_refptr<media::VideoFrame>, + CreateFrame, + (media::VideoPixelFormat format, + const gfx::Size& coded_size, + const gfx::Rect& visible_rect, + const gfx::Size& natural_size, + base::TimeDelta timestamp)); + + MOCK_METHOD(scoped_refptr<media::VideoFrame>, + CreateTemporaryFrame, + (media::VideoPixelFormat format, + const gfx::Size& coded_size, + const gfx::Rect& visible_rect, + const gfx::Size& natural_size, + base::TimeDelta timestamp)); + + MOCK_METHOD(scoped_refptr<viz::RasterContextProvider>, + GetRasterContextProvider, + ()); + + MOCK_METHOD(scoped_refptr<media::VideoFrame>, + ConstructVideoFrameFromTexture, + (scoped_refptr<media::VideoFrame> source_frame)); + + MOCK_METHOD(scoped_refptr<media::VideoFrame>, + ConstructVideoFrameFromGpu, + (scoped_refptr<media::VideoFrame> source_frame)); + + void ExpectCreateFrameWithRealImplementation() { + EXPECT_CALL(*this, CreateFrame) + .WillOnce(testing::Invoke( + [this](media::VideoPixelFormat format, const gfx::Size& coded_size, + const gfx::Rect& visible_rect, const gfx::Size& natural_size, + base::TimeDelta timestamp) { + return WebRtcVideoFrameAdapter::SharedResources::CreateFrame( + format, coded_size, visible_rect, natural_size, timestamp); + })); + } + + void ExpectCreateTemporaryFrameWithRealImplementation() { + EXPECT_CALL(*this, CreateTemporaryFrame) + .WillOnce(testing::Invoke([this](media::VideoPixelFormat format, + const gfx::Size& coded_size, + const gfx::Rect& visible_rect, + const gfx::Size& natural_size, + base::TimeDelta timestamp) { + return WebRtcVideoFrameAdapter::SharedResources::CreateTemporaryFrame( + format, coded_size, visible_rect, natural_size, timestamp); + })); + } + + private: + friend class base::RefCountedThreadSafe<MockSharedResources>; +}; + +class MockLegacySharedResources : public LegacyWebRtcVideoFrameAdapter::SharedResources { public: - MockSharedResources() + MockLegacySharedResources() : LegacyWebRtcVideoFrameAdapter::SharedResources(nullptr) {} MOCK_METHOD(scoped_refptr<media::VideoFrame>, @@ -70,7 +130,7 @@ } private: - friend class base::RefCountedThreadSafe<MockSharedResources>; + friend class base::RefCountedThreadSafe<MockLegacySharedResources>; }; } // namespace blink
diff --git a/third_party/blink/renderer/platform/webrtc/webrtc_video_frame_adapter.cc b/third_party/blink/renderer/platform/webrtc/webrtc_video_frame_adapter.cc new file mode 100644 index 0000000..4388412 --- /dev/null +++ b/third_party/blink/renderer/platform/webrtc/webrtc_video_frame_adapter.cc
@@ -0,0 +1,356 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "third_party/blink/renderer/platform/webrtc/webrtc_video_frame_adapter.h" + +#include <cmath> + +#include "base/dcheck_is_on.h" +#include "base/synchronization/waitable_event.h" +#include "base/threading/thread_restrictions.h" +#include "gpu/command_buffer/client/raster_interface.h" +#include "media/base/video_util.h" +#include "third_party/blink/public/platform/platform.h" +#include "third_party/blink/renderer/platform/scheduler/public/post_cross_thread_task.h" +#include "third_party/blink/renderer/platform/scheduler/public/thread.h" +#include "third_party/blink/renderer/platform/webrtc/convert_to_webrtc_video_frame_buffer.h" +#include "third_party/blink/renderer/platform/wtf/cross_thread_functional.h" +#include "third_party/webrtc/rtc_base/ref_counted_object.h" + +namespace blink { + +namespace { + +bool IsApproxEquals(int a, int b) { + return std::abs(a - b) <= 4; +} + +bool IsApproxEquals(const gfx::Rect& a, const gfx::Rect& b) { + return IsApproxEquals(a.x(), b.x()) && IsApproxEquals(a.y(), b.y()) && + IsApproxEquals(a.width(), b.width()) && + IsApproxEquals(a.height(), b.height()); +} + +static void CreateContextProviderOnMainThread( + scoped_refptr<viz::RasterContextProvider>* result, + base::WaitableEvent* waitable_event) { + *result = blink::Platform::Current()->SharedCompositorWorkerContextProvider(); + waitable_event->Signal(); +} + +} // namespace + +scoped_refptr<media::VideoFrame> +WebRtcVideoFrameAdapter::SharedResources::CreateFrame( + media::VideoPixelFormat format, + const gfx::Size& coded_size, + const gfx::Rect& visible_rect, + const gfx::Size& natural_size, + base::TimeDelta timestamp) { + return pool_.CreateFrame(format, coded_size, visible_rect, natural_size, + timestamp); +} + +scoped_refptr<media::VideoFrame> +WebRtcVideoFrameAdapter::SharedResources::CreateTemporaryFrame( + media::VideoPixelFormat format, + const gfx::Size& coded_size, + const gfx::Rect& visible_rect, + const gfx::Size& natural_size, + base::TimeDelta timestamp) { + return pool_for_tmp_frames_.CreateFrame(format, coded_size, visible_rect, + natural_size, timestamp); +} + +scoped_refptr<viz::RasterContextProvider> +WebRtcVideoFrameAdapter::SharedResources::GetRasterContextProvider() { + base::AutoLock auto_lock(context_provider_lock_); + if (raster_context_provider_) { + // Reuse created context provider if it's alive. + viz::RasterContextProvider::ScopedRasterContextLock lock( + raster_context_provider_.get()); + if (lock.RasterInterface()->GetGraphicsResetStatusKHR() == GL_NO_ERROR) + return raster_context_provider_; + } + + // Recreate the context provider. + base::WaitableEvent waitable_event; + PostCrossThreadTask( + *Thread::MainThread()->GetTaskRunner(), FROM_HERE, + CrossThreadBindOnce(&CreateContextProviderOnMainThread, + CrossThreadUnretained(&raster_context_provider_), + CrossThreadUnretained(&waitable_event))); + + // This wait is necessary because this task is completed via main thread + // asynchronously but WebRTC API is synchronous. + base::ScopedAllowBaseSyncPrimitivesOutsideBlockingScope allow_wait; + waitable_event.Wait(); + + return raster_context_provider_; +} + +scoped_refptr<media::VideoFrame> +WebRtcVideoFrameAdapter::SharedResources::ConstructVideoFrameFromTexture( + scoped_refptr<media::VideoFrame> source_frame) { + RTC_DCHECK(source_frame->HasTextures()); + + scoped_refptr<viz::RasterContextProvider> raster_context_provider = + GetRasterContextProvider(); + if (!raster_context_provider) { + return nullptr; + } + viz::RasterContextProvider::ScopedRasterContextLock scoped_context( + raster_context_provider.get()); + + auto* ri = scoped_context.RasterInterface(); + auto* gr_context = raster_context_provider->GrContext(); + + if (!ri) { + return nullptr; + } + + return media::ReadbackTextureBackedFrameToMemorySync( + *source_frame, ri, gr_context, &pool_for_mapped_frames_); +} + +scoped_refptr<media::VideoFrame> +WebRtcVideoFrameAdapter::SharedResources::ConstructVideoFrameFromGpu( + scoped_refptr<media::VideoFrame> source_frame) { + CHECK(source_frame); + // NV12 is the only supported format. + DCHECK_EQ(source_frame->format(), media::PIXEL_FORMAT_NV12); + DCHECK_EQ(source_frame->storage_type(), + media::VideoFrame::STORAGE_GPU_MEMORY_BUFFER); + + return media::ConvertToMemoryMappedFrame(std::move(source_frame)); +} + +void WebRtcVideoFrameAdapter::SharedResources::SetFeedback( + const media::VideoFrameFeedback& feedback) { + base::AutoLock auto_lock(feedback_lock_); + last_feedback_ = feedback; +} + +media::VideoFrameFeedback +WebRtcVideoFrameAdapter::SharedResources::GetFeedback() { + base::AutoLock auto_lock(feedback_lock_); + return last_feedback_; +} + +WebRtcVideoFrameAdapter::SharedResources::SharedResources( + media::GpuVideoAcceleratorFactories* gpu_factories) + : gpu_factories_(gpu_factories) {} + +WebRtcVideoFrameAdapter::SharedResources::~SharedResources() = default; + +WebRtcVideoFrameAdapter::ScaledBufferSize::ScaledBufferSize( + gfx::Rect visible_rect, + gfx::Size natural_size) + : visible_rect(std::move(visible_rect)), + natural_size(std::move(natural_size)) {} + +bool WebRtcVideoFrameAdapter::ScaledBufferSize::operator==( + const ScaledBufferSize& rhs) const { + return visible_rect == rhs.visible_rect && natural_size == rhs.natural_size; +} + +bool WebRtcVideoFrameAdapter::ScaledBufferSize::operator!=( + const ScaledBufferSize& rhs) const { + return !(*this == rhs); +} + +WebRtcVideoFrameAdapter::ScaledBufferSize +WebRtcVideoFrameAdapter::ScaledBufferSize::CropAndScale( + int offset_x, + int offset_y, + int crop_width, + int crop_height, + int scaled_width, + int scaled_height) const { + DCHECK_LT(offset_x, natural_size.width()); + DCHECK_LT(offset_y, natural_size.height()); + DCHECK_LE(offset_x + crop_width, natural_size.width()); + DCHECK_LE(offset_y + crop_height, natural_size.height()); + DCHECK_LE(scaled_width, crop_width); + DCHECK_LE(scaled_height, crop_height); + // Used to convert requested visible rect to the natural size, i.e. undo + // scaling. + double horizontal_scale = + static_cast<double>(visible_rect.width()) / natural_size.width(); + double vertical_scale = + static_cast<double>(visible_rect.height()) / natural_size.height(); + return ScaledBufferSize( + gfx::Rect(visible_rect.x() + offset_x * horizontal_scale, + visible_rect.y() + offset_y * vertical_scale, + crop_width * horizontal_scale, crop_height * vertical_scale), + gfx::Size(scaled_width, scaled_height)); +} + +WebRtcVideoFrameAdapter::ScaledBuffer::ScaledBuffer( + scoped_refptr<WebRtcVideoFrameAdapter> parent, + ScaledBufferSize size) + : parent_(std::move(parent)), size_(std::move(size)) {} + +rtc::scoped_refptr<webrtc::I420BufferInterface> +WebRtcVideoFrameAdapter::ScaledBuffer::ToI420() { + return parent_->GetOrCreateFrameBufferForSize(size_)->ToI420(); +} + +rtc::scoped_refptr<webrtc::VideoFrameBuffer> +WebRtcVideoFrameAdapter::ScaledBuffer::GetMappedFrameBuffer( + rtc::ArrayView<webrtc::VideoFrameBuffer::Type> types) { + auto frame_buffer = parent_->GetOrCreateFrameBufferForSize(size_); + return base::Contains(types, frame_buffer->type()) ? frame_buffer : nullptr; +} + +rtc::scoped_refptr<webrtc::VideoFrameBuffer> +WebRtcVideoFrameAdapter::ScaledBuffer::CropAndScale(int offset_x, + int offset_y, + int crop_width, + int crop_height, + int scaled_width, + int scaled_height) { + return new rtc::RefCountedObject<ScaledBuffer>( + parent_, size_.CropAndScale(offset_x, offset_y, crop_width, crop_height, + scaled_width, scaled_height)); +} + +WebRtcVideoFrameAdapter::WebRtcVideoFrameAdapter( + scoped_refptr<media::VideoFrame> frame) + : WebRtcVideoFrameAdapter(std::move(frame), {}, nullptr) {} + +WebRtcVideoFrameAdapter::WebRtcVideoFrameAdapter( + scoped_refptr<media::VideoFrame> frame, + std::vector<scoped_refptr<media::VideoFrame>> scaled_frames, + scoped_refptr<SharedResources> shared_resources) + : frame_(std::move(frame)), + scaled_frames_(std::move(scaled_frames)), + shared_resources_(std::move(shared_resources)), + full_size_(frame_->visible_rect(), frame_->natural_size()) { +#if DCHECK_IS_ON() + double frame_aspect_ratio = + static_cast<double>(frame_->coded_size().width()) / + frame_->coded_size().height(); + for (const auto& scaled_frame : scaled_frames_) { + DCHECK_LT(scaled_frame->coded_size().width(), frame_->coded_size().width()); + DCHECK_LT(scaled_frame->coded_size().height(), + frame_->coded_size().height()); + double scaled_frame_aspect_ratio = + static_cast<double>(scaled_frame->coded_size().width()) / + scaled_frame->coded_size().height(); + DCHECK_LE(std::abs(scaled_frame_aspect_ratio - frame_aspect_ratio), 0.05); + } +#endif +} + +WebRtcVideoFrameAdapter::~WebRtcVideoFrameAdapter() { + if (shared_resources_) { + shared_resources_->SetFeedback( + media::VideoFrameFeedback().RequireMapped(!adapted_frames_.empty())); + } +} + +rtc::scoped_refptr<webrtc::I420BufferInterface> +WebRtcVideoFrameAdapter::ToI420() { + return GetOrCreateFrameBufferForSize(full_size_)->ToI420(); +} + +rtc::scoped_refptr<webrtc::VideoFrameBuffer> +WebRtcVideoFrameAdapter::GetMappedFrameBuffer( + rtc::ArrayView<webrtc::VideoFrameBuffer::Type> types) { + auto frame_buffer = GetOrCreateFrameBufferForSize(full_size_); + return base::Contains(types, frame_buffer->type()) ? frame_buffer : nullptr; +} + +// Soft-applies cropping and scaling. The result is a ScaledBuffer. +rtc::scoped_refptr<webrtc::VideoFrameBuffer> +WebRtcVideoFrameAdapter::CropAndScale(int offset_x, + int offset_y, + int crop_width, + int crop_height, + int scaled_width, + int scaled_height) { + return new rtc::RefCountedObject<ScaledBuffer>( + this, full_size_.CropAndScale(offset_x, offset_y, crop_width, crop_height, + scaled_width, scaled_height)); +} + +rtc::scoped_refptr<webrtc::VideoFrameBuffer> +WebRtcVideoFrameAdapter::GetOrCreateFrameBufferForSize( + const ScaledBufferSize& size) { + base::AutoLock auto_lock(adapted_frames_lock_); + // Does this buffer already exist? + for (const auto& adapted_frame : adapted_frames_) { + if (adapted_frame.size == size) + return adapted_frame.frame_buffer; + } + // Adapt the frame for this size. + scoped_refptr<media::VideoFrame> video_frame = GetOrWrapFrameForSize(size); + rtc::scoped_refptr<webrtc::VideoFrameBuffer> frame_buffer = + ConvertToWebRtcVideoFrameBuffer(video_frame, shared_resources_); + adapted_frames_.emplace_back(size, video_frame, frame_buffer); + return frame_buffer; +} + +scoped_refptr<media::VideoFrame> WebRtcVideoFrameAdapter::GetOrWrapFrameForSize( + const ScaledBufferSize& size) const { + if (size == full_size_) + return frame_; + double requested_scale_factor = + static_cast<double>(size.natural_size.width()) / + size.visible_rect.width(); + // Ideally we have a frame that is in the same scale as |size|. Otherwise, the + // best frame is the smallest frame that is greater than |size|. + scoped_refptr<media::VideoFrame> best_frame = frame_; + double best_frame_scale_factor = 1.0; + for (const auto& scaled_frame : scaled_frames_) { + double scale_factor = + static_cast<double>(scaled_frame->coded_size().width()) / + frame_->coded_size().width(); + if (scale_factor >= requested_scale_factor && + scale_factor < best_frame_scale_factor) { + best_frame = scaled_frame; + best_frame_scale_factor = scale_factor; + if (scale_factor == requested_scale_factor) { + break; + } + } + } + // Because |size| is expressed relative to the full size'd frame, we need to + // adjust the visible rect for the scale of the best frame. + gfx::Rect visible_rect(size.visible_rect.x() * best_frame_scale_factor, + size.visible_rect.y() * best_frame_scale_factor, + size.visible_rect.width() * best_frame_scale_factor, + size.visible_rect.height() * best_frame_scale_factor); + if (IsApproxEquals(visible_rect, best_frame->visible_rect())) { + // Due to rounding errors it is possible for |visible_rect| to be slightly + // off, which could either cause unnecessary cropping/scaling or cause + // crashes if |visible_rect| is not contained within + // |best_frame->visible_rect()|, so we adjust it. + visible_rect = best_frame->visible_rect(); + } + CHECK(best_frame->visible_rect().Contains(visible_rect)) + << visible_rect.ToString() << " is not contained within " + << best_frame->visible_rect().ToString(); + // Wrapping is only needed if we need to crop or scale the best frame. + if (best_frame->visible_rect() == visible_rect && + best_frame->natural_size() == size.natural_size) { + return best_frame; + } + return media::VideoFrame::WrapVideoFrame(best_frame, best_frame->format(), + visible_rect, size.natural_size); +} + +scoped_refptr<media::VideoFrame> +WebRtcVideoFrameAdapter::GetAdaptedVideoBufferForTesting( + const ScaledBufferSize& size) { + base::AutoLock auto_lock(adapted_frames_lock_); + for (const auto& adapted_frame : adapted_frames_) { + if (adapted_frame.size == size) + return adapted_frame.video_frame; + } + return nullptr; +} + +} // namespace blink
diff --git a/third_party/blink/renderer/platform/webrtc/webrtc_video_frame_adapter.h b/third_party/blink/renderer/platform/webrtc/webrtc_video_frame_adapter.h new file mode 100644 index 0000000..75c8213 --- /dev/null +++ b/third_party/blink/renderer/platform/webrtc/webrtc_video_frame_adapter.h
@@ -0,0 +1,248 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_WEBRTC_WEBRTC_VIDEO_FRAME_ADAPTER_H_ +#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_WEBRTC_WEBRTC_VIDEO_FRAME_ADAPTER_H_ + +#include <vector> + +#include "base/logging.h" +#include "base/memory/ref_counted.h" +#include "base/synchronization/lock.h" +#include "components/viz/common/gpu/raster_context_provider.h" +#include "media/base/video_frame.h" +#include "media/base/video_frame_pool.h" +#include "media/base/video_types.h" +#include "media/capture/video_frame_feedback.h" +#include "media/video/gpu_video_accelerator_factories.h" +#include "third_party/blink/renderer/platform/platform_export.h" +#include "third_party/webrtc/api/scoped_refptr.h" +#include "third_party/webrtc/api/video/video_frame_buffer.h" +#include "ui/gfx/geometry/rect.h" +#include "ui/gfx/geometry/size.h" + +namespace blink { + +// The WebRtcVideoFrameAdapter implements webrtc::VideoFrameBuffer and is backed +// by one or more media::VideoFrames. +// * Upon CropAndScale(), the crop and scale values are soft-applied. +// * Upon GetMappedFrameBuffer(), any outstanding crop and scale is hard-applied +// before returning the resulting buffer. This also happens on ToI420(). +// +// This eliminates any intermediary downscales by only hard-applying what +// actually needs to be mapped. +// +// When crop and scale is hard-applied, the media::VideoFrame of closest size +// that is greater than or equal the requested size is chosen, minimizing +// scaling costs. If a media::VideoFrame exists in the desired size, no scaling +// is needed. +// +// WebRtcVideoFrameAdapter keeps track of which crops and scales were +// hard-applied during its lifetime. +// TODO(https://crbug.com/webrtc/12469): Expose this information to the caller +// or to the frame feeddback so that we may optionally use this information to +// optimize future captured frames for these sizes. +class PLATFORM_EXPORT WebRtcVideoFrameAdapter + : public webrtc::VideoFrameBuffer { + public: + class PLATFORM_EXPORT SharedResources + : public base::RefCountedThreadSafe<SharedResources> { + public: + explicit SharedResources( + media::GpuVideoAcceleratorFactories* gpu_factories); + + // Create frames for requested output format and resolution. + virtual scoped_refptr<media::VideoFrame> CreateFrame( + media::VideoPixelFormat format, + const gfx::Size& coded_size, + const gfx::Rect& visible_rect, + const gfx::Size& natural_size, + base::TimeDelta timestamp); + + // Temporary frames may have a different format or size than scaled frames. + // However, VideoFramePool doesn't work nicely if the requested frame size + // or format changes on the fly. Therefore a separate pool is used for + // temporary frames. + virtual scoped_refptr<media::VideoFrame> CreateTemporaryFrame( + media::VideoPixelFormat format, + const gfx::Size& coded_size, + const gfx::Rect& visible_rect, + const gfx::Size& natural_size, + base::TimeDelta timestamp); + + virtual scoped_refptr<viz::RasterContextProvider> + GetRasterContextProvider(); + + // Constructs a VideoFrame from a texture by invoking RasterInterface, + // which would perform a blocking call to a GPU process. + // The pixel data is copied and may be in ARGB pixel format in some cases, + // So additional conversion to I420 would be needed. + virtual scoped_refptr<media::VideoFrame> ConstructVideoFrameFromTexture( + scoped_refptr<media::VideoFrame> source_frame); + + // Constructs a VideoFrame from a GMB. Unless it's a Windows DXGI GMB, + // the buffer is mapped to the memory and wrapped by a VideoFrame with no + // copies. For DXGI buffers a blocking call to GPU process is made to + // copy pixel data. + virtual scoped_refptr<media::VideoFrame> ConstructVideoFrameFromGpu( + scoped_refptr<media::VideoFrame> source_frame); + + // Used to report feedback from an adapter upon destruction. + void SetFeedback(const media::VideoFrameFeedback& feedback); + + // Returns the most recently stored feedback. + media::VideoFrameFeedback GetFeedback(); + + protected: + friend class base::RefCountedThreadSafe<SharedResources>; + virtual ~SharedResources(); + + private: + media::VideoFramePool pool_; + media::VideoFramePool pool_for_mapped_frames_; + media::VideoFramePool pool_for_tmp_frames_; + + base::Lock context_provider_lock_; + scoped_refptr<viz::RasterContextProvider> raster_context_provider_ + GUARDED_BY(context_provider_lock_); + + media::GpuVideoAcceleratorFactories* gpu_factories_; + + base::Lock feedback_lock_; + + // Contains feedback from the most recently destroyed Adapter. + media::VideoFrameFeedback last_feedback_ GUARDED_BY(feedback_lock_); + }; + + struct PLATFORM_EXPORT ScaledBufferSize { + ScaledBufferSize(gfx::Rect visible_rect, gfx::Size natural_size); + + bool operator==(const ScaledBufferSize& rhs) const; + bool operator!=(const ScaledBufferSize& rhs) const; + + // Applies crop-and-scale relative to the current natural size. + ScaledBufferSize CropAndScale(int offset_x, + int offset_y, + int crop_width, + int crop_height, + int scaled_width, + int scaled_height) const; + + // Cropping relative to the original image. + gfx::Rect visible_rect; + // The size (after scaling) of the visible rect. + gfx::Size natural_size; + }; + + // Implements a soft-applied "view" of the parent WebRtcVideoFrameAdapter. Its + // size only gets hard-applied if GetMappedFrameBuffer() or ToI420() is + // called, in which case the result is cached inside the parent. + class ScaledBuffer : public webrtc::VideoFrameBuffer { + public: + ScaledBuffer(scoped_refptr<WebRtcVideoFrameAdapter> parent, + ScaledBufferSize size); + + // Regardless of the pixel format used internally, kNative is returned + // indicating that GetMappedFrameBuffer() or ToI420() is required to obtain + // the pixels. + webrtc::VideoFrameBuffer::Type type() const override { + return webrtc::VideoFrameBuffer::Type::kNative; + } + int width() const override { return size_.natural_size.width(); } + int height() const override { return size_.natural_size.height(); } + + // Obtains a mapped I420 buffer with this ScaledBuffer's size hard-applied. + // If I420 is not used internally, a conversion happens. + rtc::scoped_refptr<webrtc::I420BufferInterface> ToI420() override; + + // Obtains a mapped buffer of this ScaledBuffer's size hard-applied. The + // resulting buffer's type is the non-kNative type used internally. + rtc::scoped_refptr<webrtc::VideoFrameBuffer> GetMappedFrameBuffer( + rtc::ArrayView<webrtc::VideoFrameBuffer::Type> types) override; + + // Soft-applies cropping and scaling. The result is another ScaledBuffer. + rtc::scoped_refptr<webrtc::VideoFrameBuffer> CropAndScale( + int offset_x, + int offset_y, + int crop_width, + int crop_height, + int scaled_width, + int scaled_height) override; + + const ScaledBufferSize& size() const { return size_; } + + private: + scoped_refptr<WebRtcVideoFrameAdapter> parent_; + ScaledBufferSize size_; + }; + + explicit WebRtcVideoFrameAdapter(scoped_refptr<media::VideoFrame> frame); + WebRtcVideoFrameAdapter( + scoped_refptr<media::VideoFrame> frame, + std::vector<scoped_refptr<media::VideoFrame>> scaled_frames, + scoped_refptr<SharedResources> shared_resources); + + scoped_refptr<media::VideoFrame> getMediaVideoFrame() const { return frame_; } + + // Regardless of the pixel format used internally, kNative is returned + // indicating that GetMappedFrameBuffer() or ToI420() is required to obtain + // the pixels. + webrtc::VideoFrameBuffer::Type type() const override { + return webrtc::VideoFrameBuffer::Type::kNative; + } + int width() const override { return frame_->natural_size().width(); } + int height() const override { return frame_->natural_size().height(); } + + rtc::scoped_refptr<webrtc::I420BufferInterface> ToI420() override; + rtc::scoped_refptr<webrtc::VideoFrameBuffer> GetMappedFrameBuffer( + rtc::ArrayView<webrtc::VideoFrameBuffer::Type> types) override; + + // Soft-applies cropping and scaling. The result is a ScaledBuffer. + rtc::scoped_refptr<webrtc::VideoFrameBuffer> CropAndScale( + int offset_x, + int offset_y, + int crop_width, + int crop_height, + int scaled_width, + int scaled_height) override; + + // If this exact size has been hard-applied, gets the frame associated with + // that adaptation, otherwise null. + scoped_refptr<media::VideoFrame> GetAdaptedVideoBufferForTesting( + const ScaledBufferSize& size); + + protected: + ~WebRtcVideoFrameAdapter() override; + + private: + struct AdaptedFrame { + AdaptedFrame(ScaledBufferSize size, + scoped_refptr<media::VideoFrame> video_frame, + rtc::scoped_refptr<webrtc::VideoFrameBuffer> frame_buffer) + : size(std::move(size)), + video_frame(std::move(video_frame)), + frame_buffer(std::move(frame_buffer)) {} + + ScaledBufferSize size; + scoped_refptr<media::VideoFrame> video_frame; + rtc::scoped_refptr<webrtc::VideoFrameBuffer> frame_buffer; + }; + + rtc::scoped_refptr<webrtc::VideoFrameBuffer> GetOrCreateFrameBufferForSize( + const ScaledBufferSize& size); + scoped_refptr<media::VideoFrame> GetOrWrapFrameForSize( + const ScaledBufferSize& size) const; + + base::Lock adapted_frames_lock_; + const scoped_refptr<media::VideoFrame> frame_; + const std::vector<scoped_refptr<media::VideoFrame>> scaled_frames_; + const scoped_refptr<SharedResources> shared_resources_; + const ScaledBufferSize full_size_; + // Frames that have been adapted, i.e. that were "hard-applied" and mapped. + std::vector<AdaptedFrame> adapted_frames_ GUARDED_BY(adapted_frames_lock_); +}; + +} // namespace blink + +#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_WEBRTC_WEBRTC_VIDEO_FRAME_ADAPTER_H_
diff --git a/third_party/blink/renderer/platform/webrtc/webrtc_video_frame_adapter_test.cc b/third_party/blink/renderer/platform/webrtc/webrtc_video_frame_adapter_test.cc new file mode 100644 index 0000000..1130071 --- /dev/null +++ b/third_party/blink/renderer/platform/webrtc/webrtc_video_frame_adapter_test.cc
@@ -0,0 +1,421 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "third_party/blink/renderer/platform/webrtc/webrtc_video_frame_adapter.h" + +#include "base/memory/ref_counted.h" +#include "testing/gmock/include/gmock/gmock.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "third_party/blink/renderer/platform/testing/video_frame_utils.h" +#include "third_party/blink/renderer/platform/webrtc/testing/mock_webrtc_video_frame_adapter_shared_resources.h" +#include "third_party/webrtc/api/scoped_refptr.h" +#include "third_party/webrtc/rtc_base/ref_counted_object.h" + +namespace blink { + +TEST(ScaledBufferSizeTest, CroppingIsRelative) { + const WebRtcVideoFrameAdapter::ScaledBufferSize k720p( + gfx::Rect(0, 0, 1280, 720), gfx::Size(1280, 720)); + + // Crop away a 100 pixel border. + const auto cropped_full_scale = + k720p.CropAndScale(100, 100, 1080, 520, 1080, 520); + EXPECT_EQ(cropped_full_scale.visible_rect.x(), 100); + EXPECT_EQ(cropped_full_scale.visible_rect.y(), 100); + EXPECT_EQ(cropped_full_scale.visible_rect.width(), 1080); + EXPECT_EQ(cropped_full_scale.visible_rect.height(), 520); + EXPECT_EQ(cropped_full_scale.natural_size.width(), 1080); + EXPECT_EQ(cropped_full_scale.natural_size.height(), 520); + + // Applying the same size again should be a NO-OP. + const auto cropped_full_scale2 = + cropped_full_scale.CropAndScale(0, 0, 1080, 520, 1080, 520); + EXPECT_TRUE(cropped_full_scale2 == cropped_full_scale); + + // Cropping again is relative to the current crop. Crop on crop. + const auto second_cropped_full_size = + cropped_full_scale.CropAndScale(100, 100, 880, 320, 880, 320); + EXPECT_EQ(second_cropped_full_size.visible_rect.x(), 200); + EXPECT_EQ(second_cropped_full_size.visible_rect.y(), 200); + EXPECT_EQ(second_cropped_full_size.visible_rect.width(), 880); + EXPECT_EQ(second_cropped_full_size.visible_rect.height(), 320); + EXPECT_EQ(second_cropped_full_size.natural_size.width(), 880); + EXPECT_EQ(second_cropped_full_size.natural_size.height(), 320); + + // Applying the same size again should be a NO-OP. + const auto second_cropped_full_size2 = + second_cropped_full_size.CropAndScale(0, 0, 880, 320, 880, 320); + EXPECT_TRUE(second_cropped_full_size2 == second_cropped_full_size); + + // Cropping again is relative to the current crop. Crop on crop on crop. + const auto third_cropped_full_size = + second_cropped_full_size.CropAndScale(100, 100, 680, 120, 680, 120); + EXPECT_EQ(third_cropped_full_size.visible_rect.x(), 300); + EXPECT_EQ(third_cropped_full_size.visible_rect.y(), 300); + EXPECT_EQ(third_cropped_full_size.visible_rect.width(), 680); + EXPECT_EQ(third_cropped_full_size.visible_rect.height(), 120); + EXPECT_EQ(third_cropped_full_size.natural_size.width(), 680); + EXPECT_EQ(third_cropped_full_size.natural_size.height(), 120); +} + +TEST(ScaledBufferSizeTest, ScalingIsRelative) { + const WebRtcVideoFrameAdapter::ScaledBufferSize k720p( + gfx::Rect(0, 0, 1280, 720), gfx::Size(1280, 720)); + + // Scale down by 2x. + const auto no_crop_half_size = k720p.CropAndScale(0, 0, 1280, 720, 640, 360); + EXPECT_EQ(no_crop_half_size.visible_rect.x(), 0); + EXPECT_EQ(no_crop_half_size.visible_rect.y(), 0); + EXPECT_EQ(no_crop_half_size.visible_rect.width(), 1280); + EXPECT_EQ(no_crop_half_size.visible_rect.height(), 720); + EXPECT_EQ(no_crop_half_size.natural_size.width(), 640); + EXPECT_EQ(no_crop_half_size.natural_size.height(), 360); + + // Applying the same size again should be a NO-OP. + const auto no_crop_half_size2 = + no_crop_half_size.CropAndScale(0, 0, 640, 360, 640, 360); + EXPECT_TRUE(no_crop_half_size2 == no_crop_half_size); + + // Scaling again is relative to the current scale. Half-size on half-size. + const auto no_crop_quarter_size = + no_crop_half_size.CropAndScale(0, 0, 640, 360, 320, 180); + EXPECT_EQ(no_crop_quarter_size.visible_rect.x(), 0); + EXPECT_EQ(no_crop_quarter_size.visible_rect.y(), 0); + EXPECT_EQ(no_crop_quarter_size.visible_rect.width(), 1280); + EXPECT_EQ(no_crop_quarter_size.visible_rect.height(), 720); + EXPECT_EQ(no_crop_quarter_size.natural_size.width(), 320); + EXPECT_EQ(no_crop_quarter_size.natural_size.height(), 180); + + // Applying the same size again should be a NO-OP. + const auto no_crop_quarter_size2 = + no_crop_quarter_size.CropAndScale(0, 0, 320, 180, 320, 180); + EXPECT_TRUE(no_crop_quarter_size2 == no_crop_quarter_size); + + // Scaling again is relative to the current scale. + // Half-size on half-size on half-size. + const auto no_crop_eighths_size = + no_crop_quarter_size.CropAndScale(0, 0, 320, 180, 160, 90); + EXPECT_EQ(no_crop_eighths_size.visible_rect.x(), 0); + EXPECT_EQ(no_crop_eighths_size.visible_rect.y(), 0); + EXPECT_EQ(no_crop_eighths_size.visible_rect.width(), 1280); + EXPECT_EQ(no_crop_eighths_size.visible_rect.height(), 720); + EXPECT_EQ(no_crop_eighths_size.natural_size.width(), 160); + EXPECT_EQ(no_crop_eighths_size.natural_size.height(), 90); +} + +TEST(ScaledBufferSizeTest, CroppingAndScalingIsRelative) { + const WebRtcVideoFrameAdapter::ScaledBufferSize k720p( + gfx::Rect(0, 0, 1280, 720), gfx::Size(1280, 720)); + + // Crop away a 100 pixel border and downscale by 2x. + const auto crop_and_scale1 = + k720p.CropAndScale(100, 100, 1080, 520, 540, 260); + EXPECT_EQ(crop_and_scale1.visible_rect.x(), 100); + EXPECT_EQ(crop_and_scale1.visible_rect.y(), 100); + EXPECT_EQ(crop_and_scale1.visible_rect.width(), 1080); + EXPECT_EQ(crop_and_scale1.visible_rect.height(), 520); + EXPECT_EQ(crop_and_scale1.natural_size.width(), 540); + EXPECT_EQ(crop_and_scale1.natural_size.height(), 260); + + // Cropping some more at the new scale without further downscale. + const auto crop_and_scale2 = + crop_and_scale1.CropAndScale(50, 50, 440, 160, 440, 160); + // The delta offset is magnified due to scale. Offset = 100*1 + 50*2. + EXPECT_EQ(crop_and_scale2.visible_rect.x(), 200); + EXPECT_EQ(crop_and_scale2.visible_rect.y(), 200); + EXPECT_EQ(crop_and_scale2.visible_rect.width(), 880); + EXPECT_EQ(crop_and_scale2.visible_rect.height(), 320); + EXPECT_EQ(crop_and_scale2.natural_size.width(), 440); + EXPECT_EQ(crop_and_scale2.natural_size.height(), 160); + + // Scaling some more without further cropping. + const auto crop_and_scale3 = + crop_and_scale2.CropAndScale(0, 0, 440, 160, 220, 80); + EXPECT_EQ(crop_and_scale3.visible_rect.x(), 200); + EXPECT_EQ(crop_and_scale3.visible_rect.y(), 200); + EXPECT_EQ(crop_and_scale3.visible_rect.width(), 880); + EXPECT_EQ(crop_and_scale3.visible_rect.height(), 320); + EXPECT_EQ(crop_and_scale3.natural_size.width(), 220); + EXPECT_EQ(crop_and_scale3.natural_size.height(), 80); +} + +TEST(WebRtcVideoFrameAdapterTest, MapFullFrameIsZeroCopy) { + std::vector<webrtc::VideoFrameBuffer::Type> kNv12 = { + webrtc::VideoFrameBuffer::Type::kNV12}; + const gfx::Size kSize720p(1280, 720); + const gfx::Rect kRect720p(0, 0, 1280, 720); + + // The strictness of the mock ensures zero copy. + scoped_refptr<MockSharedResources> resources = + new testing::StrictMock<MockSharedResources>(); + + auto frame_720p = CreateTestFrame(kSize720p, kRect720p, kSize720p, + media::VideoFrame::STORAGE_OWNED_MEMORY, + media::VideoPixelFormat::PIXEL_FORMAT_NV12); + + rtc::scoped_refptr<WebRtcVideoFrameAdapter> multi_buffer( + new rtc::RefCountedObject<WebRtcVideoFrameAdapter>( + frame_720p, std::vector<scoped_refptr<media::VideoFrame>>(), + resources)); + + // Mapping produces a frame of the correct size. + auto mapped_frame = multi_buffer->GetMappedFrameBuffer(kNv12); + EXPECT_EQ(mapped_frame->width(), kSize720p.width()); + EXPECT_EQ(mapped_frame->height(), kSize720p.height()); + // The mapping above should be backed by |frame_720p|. + auto adapted_frame = multi_buffer->GetAdaptedVideoBufferForTesting( + WebRtcVideoFrameAdapter::ScaledBufferSize(kRect720p, kSize720p)); + EXPECT_EQ(adapted_frame, frame_720p); +} + +TEST(WebRtcVideoFrameAdapterTest, + MapScaledFrameCreatesNewFrameWhenNotPreScaled) { + std::vector<webrtc::VideoFrameBuffer::Type> kNv12 = { + webrtc::VideoFrameBuffer::Type::kNV12}; + const gfx::Size kSize720p(1280, 720); + const gfx::Rect kRect720p(0, 0, 1280, 720); + const gfx::Size kSize360p(640, 360); + + // Because the size we are going to request does not the frame we expect one + // CreateFrame() to happen. + scoped_refptr<MockSharedResources> resources = + new testing::StrictMock<MockSharedResources>(); + EXPECT_CALL(*resources, CreateFrame) + .WillOnce(testing::Invoke( + [](media::VideoPixelFormat format, const gfx::Size& coded_size, + const gfx::Rect& visible_rect, const gfx::Size& natural_size, + base::TimeDelta timestamp) { + return CreateTestFrame(coded_size, visible_rect, natural_size, + media::VideoFrame::STORAGE_OWNED_MEMORY, + format); + })); + + auto frame_720p = CreateTestFrame(kSize720p, kRect720p, kSize720p, + media::VideoFrame::STORAGE_OWNED_MEMORY, + media::VideoPixelFormat::PIXEL_FORMAT_NV12); + + rtc::scoped_refptr<WebRtcVideoFrameAdapter> multi_buffer( + new rtc::RefCountedObject<WebRtcVideoFrameAdapter>( + frame_720p, std::vector<scoped_refptr<media::VideoFrame>>(), + resources)); + + auto scaled_frame = + multi_buffer->Scale(kSize360p.width(), kSize360p.height()); + + // Mapping produces a frame of the correct size. + auto mapped_frame = scaled_frame->GetMappedFrameBuffer(kNv12); + EXPECT_EQ(mapped_frame->width(), kSize360p.width()); + EXPECT_EQ(mapped_frame->height(), kSize360p.height()); + // The mapping above should be backed by a frame that wraps |frame_720p|. We + // can tell by looking at the coded size. + auto adapted_frame = multi_buffer->GetAdaptedVideoBufferForTesting( + WebRtcVideoFrameAdapter::ScaledBufferSize(kRect720p, kSize360p)); + ASSERT_TRUE(adapted_frame); + EXPECT_EQ(adapted_frame->coded_size(), frame_720p->coded_size()); +} + +TEST(WebRtcVideoFrameAdapterTest, MapScaledFrameUsesPreScaling) { + std::vector<webrtc::VideoFrameBuffer::Type> kNv12 = { + webrtc::VideoFrameBuffer::Type::kNV12}; + const gfx::Size kSize720p(1280, 720); + const gfx::Rect kRect720p(0, 0, 1280, 720); + const gfx::Size kSize360p(640, 360); + const gfx::Rect kRect360p(0, 0, 640, 360); + + // The strictness of the mock ensures no additional scaling. + scoped_refptr<MockSharedResources> resources = + new testing::StrictMock<MockSharedResources>(); + + auto frame_720p = CreateTestFrame(kSize720p, kRect720p, kSize720p, + media::VideoFrame::STORAGE_OWNED_MEMORY, + media::VideoPixelFormat::PIXEL_FORMAT_NV12); + auto frame_360p = CreateTestFrame(kSize360p, kRect360p, kSize360p, + media::VideoFrame::STORAGE_OWNED_MEMORY, + media::VideoPixelFormat::PIXEL_FORMAT_NV12); + + rtc::scoped_refptr<WebRtcVideoFrameAdapter> multi_buffer( + new rtc::RefCountedObject<WebRtcVideoFrameAdapter>( + frame_720p, + std::vector<scoped_refptr<media::VideoFrame>>({frame_360p}), + resources)); + + auto scaled_frame = + multi_buffer->Scale(kSize360p.width(), kSize360p.height()); + + // Mapping produces a frame of the correct size. + auto mapped_frame = scaled_frame->GetMappedFrameBuffer(kNv12); + EXPECT_EQ(mapped_frame->width(), kSize360p.width()); + EXPECT_EQ(mapped_frame->height(), kSize360p.height()); + // The mapping above should be backed by |frame_360p|. + auto adapted_frame = multi_buffer->GetAdaptedVideoBufferForTesting( + WebRtcVideoFrameAdapter::ScaledBufferSize(kRect720p, kSize360p)); + EXPECT_EQ(adapted_frame, frame_360p); +} + +TEST(WebRtcVideoFrameAdapterTest, MapScaledFrameScalesFromClosestFrame) { + std::vector<webrtc::VideoFrameBuffer::Type> kNv12 = { + webrtc::VideoFrameBuffer::Type::kNV12}; + const gfx::Size kSize720p(1280, 720); + const gfx::Rect kRect720p(0, 0, 1280, 720); + const gfx::Size kSize480p(853, 480); + const gfx::Rect kRect480p(0, 0, 853, 480); + const gfx::Size kSize360p(640, 360); + const gfx::Rect kRect360p(0, 0, 640, 360); + + // A size in-between 480p and 360p. + const gfx::Size kSize432p(768, 432); + + // Because the size we are going to request does not match any of the frames + // we expect one CreateFrame() to happen. + scoped_refptr<MockSharedResources> resources = + new testing::StrictMock<MockSharedResources>(); + EXPECT_CALL(*resources, CreateFrame) + .WillOnce(testing::Invoke( + [](media::VideoPixelFormat format, const gfx::Size& coded_size, + const gfx::Rect& visible_rect, const gfx::Size& natural_size, + base::TimeDelta timestamp) { + return CreateTestFrame(coded_size, visible_rect, natural_size, + media::VideoFrame::STORAGE_OWNED_MEMORY, + format); + })); + + auto frame_720p = CreateTestFrame(kSize720p, kRect720p, kSize720p, + media::VideoFrame::STORAGE_OWNED_MEMORY, + media::VideoPixelFormat::PIXEL_FORMAT_NV12); + auto frame_480p = CreateTestFrame(kSize480p, kRect480p, kSize480p, + media::VideoFrame::STORAGE_OWNED_MEMORY, + media::VideoPixelFormat::PIXEL_FORMAT_NV12); + auto frame_360p = CreateTestFrame(kSize360p, kRect360p, kSize360p, + media::VideoFrame::STORAGE_OWNED_MEMORY, + media::VideoPixelFormat::PIXEL_FORMAT_NV12); + + rtc::scoped_refptr<WebRtcVideoFrameAdapter> multi_buffer( + new rtc::RefCountedObject<WebRtcVideoFrameAdapter>( + frame_720p, + std::vector<scoped_refptr<media::VideoFrame>>( + {frame_480p, frame_360p}), + resources)); + + auto scaled_frame = + multi_buffer->Scale(kSize432p.width(), kSize432p.height()); + + // Mapping produces a frame of the correct size. + auto mapped_frame = scaled_frame->GetMappedFrameBuffer(kNv12); + EXPECT_EQ(mapped_frame->width(), kSize432p.width()); + EXPECT_EQ(mapped_frame->height(), kSize432p.height()); + // The mapping above should be backed by a frame that wraps |frame_480p|. We + // can tell by looking at the coded size. + auto adapted_frame = multi_buffer->GetAdaptedVideoBufferForTesting( + WebRtcVideoFrameAdapter::ScaledBufferSize(kRect720p, kSize432p)); + ASSERT_TRUE(adapted_frame); + EXPECT_EQ(adapted_frame->coded_size(), frame_480p->coded_size()); +} + +TEST(WebRtcVideoFrameAdapterTest, CanApplyCropAndScale) { + std::vector<webrtc::VideoFrameBuffer::Type> kNv12 = { + webrtc::VideoFrameBuffer::Type::kNV12}; + const gfx::Size kSize720p(1280, 720); + const gfx::Rect kRect720p(0, 0, 1280, 720); + const gfx::Size kSize360p(640, 360); + const gfx::Rect kRect360p(0, 0, 640, 360); + + const gfx::Rect kCroppedRect1(20, 20, 1240, 680); + const gfx::Rect kCroppedRect2(20, 20, 1200, 640); + const gfx::Size kScaledSize2(1200 / 2, 640 / 2); + + // The strictness of the mock ensures zero copy. + scoped_refptr<MockSharedResources> resources = + new testing::StrictMock<MockSharedResources>(); + + auto frame_720p = CreateTestFrame(kSize720p, kRect720p, kSize720p, + media::VideoFrame::STORAGE_OWNED_MEMORY, + media::VideoPixelFormat::PIXEL_FORMAT_NV12); + auto frame_360p = CreateTestFrame(kSize360p, kRect360p, kSize360p, + media::VideoFrame::STORAGE_OWNED_MEMORY, + media::VideoPixelFormat::PIXEL_FORMAT_NV12); + + rtc::scoped_refptr<WebRtcVideoFrameAdapter> multi_buffer( + new rtc::RefCountedObject<WebRtcVideoFrameAdapter>( + frame_720p, + std::vector<scoped_refptr<media::VideoFrame>>({frame_360p}), + resources)); + + // Apply initial cropping, keeping the same scale. + auto cropped_frame1 = multi_buffer->CropAndScale( + kCroppedRect1.x(), kCroppedRect1.y(), kCroppedRect1.width(), + kCroppedRect1.height(), kCroppedRect1.width(), kCroppedRect1.height()); + + // Mapping produces a frame of the correct size. + auto mapped_cropped_frame1 = cropped_frame1->GetMappedFrameBuffer(kNv12); + EXPECT_EQ(mapped_cropped_frame1->width(), kCroppedRect1.width()); + EXPECT_EQ(mapped_cropped_frame1->height(), kCroppedRect1.height()); + // The mapping above should be backed by a frame that wraps |frame_720p|. We + // can tell by looking at the coded size. + auto adapted_frame = multi_buffer->GetAdaptedVideoBufferForTesting( + WebRtcVideoFrameAdapter::ScaledBufferSize( + kCroppedRect1, + gfx::Size(kCroppedRect1.width(), kCroppedRect1.height()))); + ASSERT_TRUE(adapted_frame); + EXPECT_EQ(adapted_frame->coded_size(), frame_720p->coded_size()); + + // Apply further cropping and scaling on the already cropped frame. + auto cropped_frame2 = cropped_frame1->CropAndScale( + kCroppedRect2.x(), kCroppedRect2.y(), kCroppedRect2.width(), + kCroppedRect2.height(), kScaledSize2.width(), kScaledSize2.height()); + + // Mapping produces a frame of the correct size. + auto mapped_cropped_frame2 = cropped_frame2->GetMappedFrameBuffer(kNv12); + EXPECT_EQ(mapped_cropped_frame2->width(), kScaledSize2.width()); + EXPECT_EQ(mapped_cropped_frame2->height(), kScaledSize2.height()); + // The mapping above should be backed by a frame that wraps |frame_360p|. We + // can tell by looking at the coded size. + adapted_frame = multi_buffer->GetAdaptedVideoBufferForTesting( + WebRtcVideoFrameAdapter::ScaledBufferSize( + // The second cropped rectangle is relative to the first one. + gfx::Rect(kCroppedRect1.x() + kCroppedRect2.x(), + kCroppedRect1.y() + kCroppedRect2.y(), + kCroppedRect2.width(), kCroppedRect2.height()), + kScaledSize2)); + ASSERT_TRUE(adapted_frame); + EXPECT_EQ(adapted_frame->coded_size(), frame_360p->coded_size()); +} + +TEST(WebRtcVideoFrameAdapterTest, FrameFeedbackSetsRequireMappedFrame) { + std::vector<webrtc::VideoFrameBuffer::Type> kNv12 = { + webrtc::VideoFrameBuffer::Type::kNV12}; + const gfx::Size kSize720p(1280, 720); + const gfx::Rect kRect720p(0, 0, 1280, 720); + const gfx::Size kSize360p(640, 360); + + scoped_refptr<WebRtcVideoFrameAdapter::SharedResources> resources = + base::MakeRefCounted<WebRtcVideoFrameAdapter::SharedResources>(nullptr); + + auto frame_720p = CreateTestFrame(kSize720p, kRect720p, kSize720p, + media::VideoFrame::STORAGE_OWNED_MEMORY, + media::VideoPixelFormat::PIXEL_FORMAT_NV12); + + // By default, the feedback is not set to require mapping. + EXPECT_FALSE(resources->GetFeedback().require_mapped_frame); + { + // Do some scaling, but don't map it. + rtc::scoped_refptr<WebRtcVideoFrameAdapter> multi_buffer( + new rtc::RefCountedObject<WebRtcVideoFrameAdapter>( + frame_720p, std::vector<scoped_refptr<media::VideoFrame>>(), + resources)); + multi_buffer->Scale(kSize360p.width(), kSize360p.height()); + } + EXPECT_FALSE(resources->GetFeedback().require_mapped_frame); + { + // Do map the buffer. + rtc::scoped_refptr<WebRtcVideoFrameAdapter> multi_buffer( + new rtc::RefCountedObject<WebRtcVideoFrameAdapter>( + frame_720p, std::vector<scoped_refptr<media::VideoFrame>>(), + resources)); + multi_buffer->Scale(kSize360p.width(), kSize360p.height()) + ->GetMappedFrameBuffer(kNv12); + } + EXPECT_TRUE(resources->GetFeedback().require_mapped_frame); +} + +} // namespace blink
diff --git a/third_party/blink/web_tests/SlowTests b/third_party/blink/web_tests/SlowTests index 5bd03802..747980556 100644 --- a/third_party/blink/web_tests/SlowTests +++ b/third_party/blink/web_tests/SlowTests
@@ -734,6 +734,7 @@ crbug.com/1182691 external/wpt/webcodecs/audio-encoder.any.html [ Slow ] crbug.com/1182691 external/wpt/webcodecs/video-encoder.any.html [ Slow ] crbug.com/1176474 wpt_internal/webcodecs/temporal_svc.any.html [ Slow ] +crbug.com/1176474 wpt_internal/webcodecs/temporal_svc.any.worker.html [ Slow ] # Composited scroll snap is not accelerated for testing. crbug.com/1186753 virtual/threaded-prefer-compositing/fast/scroll-snap/snaps-after-scrollbar-scrolling-arrow.html [ Slow ]
diff --git a/third_party/blink/web_tests/external/wpt/paint-timing/fcp-only/svg-in-iframe.html b/third_party/blink/web_tests/external/wpt/paint-timing/fcp-only/svg-in-iframe.html new file mode 100644 index 0000000..230c166c --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/paint-timing/fcp-only/svg-in-iframe.html
@@ -0,0 +1,14 @@ +<!DOCTYPE html> +<title>Performance Paint Timing Test: SVG in iframe does not trigger FCP</title> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="../resources/utils.js"></script> +<iframe src="../resources/svg.html"></iframe> +<script> + promise_test(async (t) => { + await new Promise(resolve => { + window.addEventListener("message", resolve); + }); + return assertNoFirstContentfulPaint(t); + }, "SVG in an iframe does not trigger FCP in the main frame"); +</script>
diff --git a/third_party/blink/web_tests/external/wpt/paint-timing/resources/svg.html b/third_party/blink/web_tests/external/wpt/paint-timing/resources/svg.html new file mode 100644 index 0000000..94185ea --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/paint-timing/resources/svg.html
@@ -0,0 +1,20 @@ +<!DOCTYPE html> +<div id="main"> + <svg viewBox="0 0 10 10"> + <defs> + <circle id="myCircle" cx="5" cy="5" r="4" stroke="blue" /> + </defs> + <use id="circle" href="#myCircle" fill="green" /> + </svg> +</div> +<script> + const observer = new PerformanceObserver(list => { + const fcp = list.getEntriesByName("first-contentful-paint"); + if (!fcp.length) + return; + + // Message the parent when FCP has been reached. + parent.postMessage("GotFCP", '*'); + }); + observer.observe({ type: "paint", buffered: true }); +</script>
diff --git a/third_party/blink/web_tests/external/wpt/webcodecs/audio-encoder.any.js b/third_party/blink/web_tests/external/wpt/webcodecs/audio-encoder.any.js index 0813b79..a6f131b 100644 --- a/third_party/blink/web_tests/external/wpt/webcodecs/audio-encoder.any.js +++ b/third_party/blink/web_tests/external/wpt/webcodecs/audio-encoder.any.js
@@ -1,29 +1,6 @@ // META: global=window // META: script=/webcodecs/utils.js -function make_audio_frame(timestamp, channels, sampleRate, length) { - let buffer = new AudioBuffer({ - length: length, - numberOfChannels: channels, - sampleRate: sampleRate - }); - - for (var channel = 0; channel < buffer.numberOfChannels; channel++) { - // This gives us the actual array that contains the data - var array = buffer.getChannelData(channel); - let hz = 100 + channel * 50; // sound frequency - for (var i = 0; i < array.length; i++) { - let t = (i / sampleRate) * hz * (Math.PI * 2); - array[i] = Math.sin(t); - } - } - - return new AudioFrame({ - timestamp: timestamp, - buffer: buffer - }); -} - // Merge all audio buffers into a new big one with all the data. function join_buffers(buffers) { assert_greater_than_equal(buffers.length, 0); @@ -324,4 +301,4 @@ assert_not_equals(decoder_config, null); assert_not_equals(decoder_config.description, null); encoder.close(); -}, "Emit decoder config and extra data."); \ No newline at end of file +}, "Emit decoder config and extra data.");
diff --git a/third_party/blink/web_tests/external/wpt/webcodecs/audio-frame-serialization.any.js b/third_party/blink/web_tests/external/wpt/webcodecs/audio-frame-serialization.any.js new file mode 100644 index 0000000..a16207f --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/webcodecs/audio-frame-serialization.any.js
@@ -0,0 +1,72 @@ +// META: global=window +// META: script=/common/media.js +// META: script=/webcodecs/utils.js + +var defaultInit = { + timestamp: 1234, + channels: 2, + sampleRate: 8000, + frames: 100, +} + +function createDefaultAudioFrame() { + return make_audio_frame(defaultInit.timestamp, + defaultInit.channels, + defaultInit.sampleRate, + defaultInit.frames); +} + +async_test(t => { + let localFrame = createDefaultAudioFrame(); + + let channel = new MessageChannel(); + let localPort = channel.port1; + let externalPort = channel.port2; + + externalPort.onmessage = t.step_func((e) => { + let externalFrame = e.data; + let buffer = externalFrame.buffer; + // We should have a valid deserialized buffer. + assert_true(buffer != undefined || buffer != null); + assert_equals(buffer.numberOfChannels, + localFrame.buffer.numberOfChannels, "numberOfChannels"); + + for (var channel = 0; channel < buffer.numberOfChannels; channel++) { + // This gives us the actual array that contains the data + var dest_array = buffer.getChannelData(channel); + var source_array = localFrame.buffer.getChannelData(channel); + for (var i = 0; i < dest_array.length; i+=10) { + assert_equals(dest_array[i], source_array[i], + "data (ch=" + channel + ", i=" + i + ")"); + } + } + + externalFrame.close(); + externalPort.postMessage("Done"); + }) + + localPort.onmessage = t.step_func_done((e) => { + assert_true(localFrame.buffer != null); + localFrame.close(); + }) + + localPort.postMessage(localFrame); + +}, 'Verify closing frames does not propagate accross contexts.'); + +async_test(t => { + let localFrame = createDefaultAudioFrame(); + + let channel = new MessageChannel(); + let localPort = channel.port1; + + localPort.onmessage = t.unreached_func(); + + localFrame.close(); + + assert_throws_dom("DataCloneError", () => { + localPort.postMessage(localFrame); + }); + + t.done(); +}, 'Verify posting closed frames throws.');
diff --git a/third_party/blink/web_tests/external/wpt/webcodecs/utils.js b/third_party/blink/web_tests/external/wpt/webcodecs/utils.js index 854efc3..12cb715 100644 --- a/third_party/blink/web_tests/external/wpt/webcodecs/utils.js +++ b/third_party/blink/web_tests/external/wpt/webcodecs/utils.js
@@ -1,3 +1,26 @@ +function make_audio_frame(timestamp, channels, sampleRate, length) { + let buffer = new AudioBuffer({ + length: length, + numberOfChannels: channels, + sampleRate: sampleRate + }); + + for (var channel = 0; channel < buffer.numberOfChannels; channel++) { + // This gives us the actual array that contains the data + var array = buffer.getChannelData(channel); + let hz = 100 + channel * 50; // sound frequency + for (var i = 0; i < array.length; i++) { + let t = (i / sampleRate) * hz * (Math.PI * 2); + array[i] = Math.sin(t); + } + } + + return new AudioFrame({ + timestamp: timestamp, + buffer: buffer + }); +} + function makeOffscreenCanvas(width, height) { let canvas = new OffscreenCanvas(width, height); let ctx = canvas.getContext('2d');
diff --git a/tools/ipc_fuzzer/fuzzer/fuzzer.cc b/tools/ipc_fuzzer/fuzzer/fuzzer.cc index 3f4bb9c5..ef1a2c0 100644 --- a/tools/ipc_fuzzer/fuzzer/fuzzer.cc +++ b/tools/ipc_fuzzer/fuzzer/fuzzer.cc
@@ -13,7 +13,6 @@ #include "base/containers/span.h" #include "base/memory/ptr_util.h" #include "base/stl_util.h" -#include "base/strings/nullable_string16.h" #include "base/strings/string_util.h" #include "base/unguessable_token.h" #include "base/util/type_safety/id_type.h" @@ -444,20 +443,6 @@ }; template <> -struct FuzzTraits<base::NullableString16> { - static bool Fuzz(base::NullableString16* p, Fuzzer* fuzzer) { - std::u16string string = p->string(); - bool is_null = p->is_null(); - if (!FuzzParam(&string, fuzzer)) - return false; - if (!FuzzParam(&is_null, fuzzer)) - return false; - *p = base::NullableString16(string, is_null); - return true; - } -}; - -template <> struct FuzzTraits<base::Time> { static bool Fuzz(base::Time* p, Fuzzer* fuzzer) { int64_t internal_value = p->ToInternalValue();
diff --git a/tools/mb/mb_config.pyl b/tools/mb/mb_config.pyl index 72ce3d98..72c34ba 100644 --- a/tools/mb/mb_config.pyl +++ b/tools/mb/mb_config.pyl
@@ -2003,11 +2003,11 @@ ], 'fuchsia_clang_tot_official_arm64': [ - 'official_optimize_goma', 'fuchsia', 'arm64', 'clang_tot', + 'official_optimize', 'fuchsia', 'arm64', 'clang_tot', 'static', ], 'fuchsia_clang_tot_release_x64': [ - 'fuchsia', 'release_bot', 'clang_tot', + 'fuchsia', 'release', 'clang_tot', 'static', ], 'fuchsia_official_arm64_size': [
diff --git a/tools/mb/mb_config_expectations/chromium.clang.json b/tools/mb/mb_config_expectations/chromium.clang.json index 95a1bf6..2d00e25 100644 --- a/tools/mb/mb_config_expectations/chromium.clang.json +++ b/tools/mb/mb_config_expectations/chromium.clang.json
@@ -137,18 +137,17 @@ "is_component_build": false, "is_debug": false, "llvm_force_head_revision": true, - "target_os": "fuchsia", - "use_goma": true + "target_os": "fuchsia" } }, "ToTFuchsiaOfficial": { "gn_args": { "is_clang": true, + "is_component_build": false, "is_official_build": true, "llvm_force_head_revision": true, "target_cpu": "arm64", - "target_os": "fuchsia", - "use_goma": true + "target_os": "fuchsia" } }, "ToTLinux": {
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml index cc6de42..6561bdef 100644 --- a/tools/metrics/histograms/enums.xml +++ b/tools/metrics/histograms/enums.xml
@@ -3313,6 +3313,9 @@ </enum> <enum name="AppMenuSimilarSelectionType"> + <obsolete> + Deprecated as of 03/2021. + </obsolete> <int value="0" label="selected adding to bookmarks, and then selected all bookmarks"/> <int value="1" @@ -4553,6 +4556,12 @@ <int value="6" label="Document destroyed during stream creation"/> </enum> +<enum name="AudioPermissionState"> + <int value="0" label="Granted"/> + <int value="1" label="Denied but can ask again"/> + <int value="2" label="Denied and can't ask again"/> +</enum> + <enum name="AudioRenderDeviceError"> <int value="0" label="No error"/> <int value="1" label="Error during stream creation"/> @@ -7653,6 +7662,12 @@ <int value="11" label="SDP browse failed"/> <int value="12" label="GATT browse failed"/> <int value="13" label="Unknown"/> + <int value="14" label="BT IO connection failed"/> + <int value="15" label="Not connected"/> + <int value="16" label="Operation not permitted"/> + <int value="17" label="Invalid parameters"/> + <int value="18" label="Connection refused"/> + <int value="19" label="Connection canceled"/> </enum> <enum name="BlueZResultOfPairing"> @@ -31860,6 +31875,8 @@ <int value="3850" label="PermissionsPolicyHeader"/> <int value="3851" label="WebAppManifestUrlHandlers"/> <int value="3852" label="LaxAllowingUnsafeCookies"/> + <int value="3853" label="V8MediaSession_SetMicrophoneActive_Method"/> + <int value="3854" label="V8MediaSession_SetCameraActive_Method"/> </enum> <enum name="FeaturePolicyAllowlistType"> @@ -71490,6 +71507,19 @@ </int> </enum> +<enum name="SimPinOperationResult"> + <int value="0" label="Success"/> + <int value="1" label="Error Device Missing"/> + <int value="2" label="Error Failure"/> + <int value="3" label="Error Incorrect Pin"/> + <int value="4" label="Error Not Found"/> + <int value="5" label="Error Not Supported"/> + <int value="6" label="Error Pin Blocked"/> + <int value="7" label="Error Pin Required"/> + <int value="8" label="Error Timeout"/> + <int value="9" label="Error Unknown"/> +</enum> + <enum name="SimpleCache.EntryCreatedAndStream2Omitted"> <int value="0" label="Stream 2 file was present"/> <int value="1" label="Empty stream 2 file was omitted"/>
diff --git a/tools/metrics/histograms/histograms_xml/histogram_suffixes_list.xml b/tools/metrics/histograms/histograms_xml/histogram_suffixes_list.xml index 39688d7..24edc4c 100644 --- a/tools/metrics/histograms/histograms_xml/histogram_suffixes_list.xml +++ b/tools/metrics/histograms/histograms_xml/histogram_suffixes_list.xml
@@ -12404,6 +12404,9 @@ <suffix name="LoadingPredictor" label="Provides information about subresources predicted to be on the page"/> + <suffix name="LoginDetection" + label="Provides information about hosts that are identified as commonly + logged-in"/> <suffix name="None" label="No optimization type"/> <suffix name="NoScript" label="Disables the fetching and execution of JavaScript">
diff --git a/tools/metrics/histograms/histograms_xml/mobile/histograms.xml b/tools/metrics/histograms/histograms_xml/mobile/histograms.xml index 33b709f..c8dd806 100644 --- a/tools/metrics/histograms/histograms_xml/mobile/histograms.xml +++ b/tools/metrics/histograms/histograms_xml/mobile/histograms.xml
@@ -32,6 +32,9 @@ <histogram name="Mobile.AppMenu.SimilarSelection" enum="AppMenuSimilarSelectionType" expires_after="M95"> + <obsolete> + Deprecated as of 03/2021. + </obsolete> <owner>gangwu@chromium.org</owner> <owner>twellington@chromium.org</owner> <summary>
diff --git a/tools/metrics/histograms/histograms_xml/network/histograms.xml b/tools/metrics/histograms/histograms_xml/network/histograms.xml index 6370425..f55a3898 100644 --- a/tools/metrics/histograms/histograms_xml/network/histograms.xml +++ b/tools/metrics/histograms/histograms_xml/network/histograms.xml
@@ -122,6 +122,17 @@ </summary> </histogram> +<histogram name="Network.Cellular.ESim.ProfileDownload.ActivationCode.Latency" + units="ms" expires_after="2022-03-01"> + <owner>azeemarshad@chromium.org</owner> + <owner>cros-connectivity@google.com</owner> + <owner>hsuregan@chromium.org</owner> + <summary> + Tracks the time for a profile to be fully downloaded from a provided + activation code. + </summary> +</histogram> + <histogram name="Network.Cellular.ESim.ProfileRenameResult" enum="BooleanSuccess" expires_after="2022-03-01"> <owner>azeemarshad@chromium.org</owner> @@ -179,6 +190,20 @@ </summary> </histogram> +<histogram name="Network.Cellular.Pin.{SimPinOperation}" + enum="SimPinOperationResult" expires_after="2022-03-01"> + <owner>azeemarshad@chromium.org</owner> + <owner>cros-system-services-networking@google.com</owner> + <owner>hsuregan@chromium.org</owner> + <summary>Tracks the rate of success of various pin operations.</summary> + <token key="SimPinOperation"> + <variant name="ChangeSuccess"/> + <variant name="LockSuccess"/> + <variant name="UnblockSuccess"/> + <variant name="UnlockSuccess"/> + </token> +</histogram> + <histogram name="Network.Cellular.PSim.ServiceAtLogin.Count" units="units" expires_after="2022-03-01"> <owner>azeemarshad@chromium.org</owner>
diff --git a/tools/metrics/histograms/histograms_xml/others/histograms.xml b/tools/metrics/histograms/histograms_xml/others/histograms.xml index a7c33687..7629dfb 100644 --- a/tools/metrics/histograms/histograms_xml/others/histograms.xml +++ b/tools/metrics/histograms/histograms_xml/others/histograms.xml
@@ -17178,6 +17178,17 @@ </summary> </histogram> +<histogram name="VoiceInteraction.AudioPermissionEvent" + enum="AudioPermissionState" expires_after="2021-08-22"> + <owner>basiaz@google.com</owner> + <owner>chrome-language@google.com</owner> + <summary> + Android: The events related to granting or denying audio permission for + system level transcription (as opposite to Assistant). Will be logged each + time non-Assistant voice recognition is triggered with a mic button click. + </summary> +</histogram> + <histogram name="VoiceInteraction.DismissedEventSource" enum="VoiceInteractionEventSource" expires_after="2021-08-22"> <owner>wylieb@chromium.org</owner>
diff --git a/tools/metrics/histograms/histograms_xml/uma/histograms.xml b/tools/metrics/histograms/histograms_xml/uma/histograms.xml index 4bb8e02..1869d1b39 100644 --- a/tools/metrics/histograms/histograms_xml/uma/histograms.xml +++ b/tools/metrics/histograms/histograms_xml/uma/histograms.xml
@@ -183,7 +183,7 @@ </histogram> <histogram name="UMA.JavaCachingRecorder.DroppedHistogramSampleCount" - units="samples" expires_after="2021-03-21"> + units="samples" expires_after="2022-03-21"> <owner>bttk@chromium.org</owner> <owner>src/base/metrics/OWNERS</owner> <summary> @@ -194,7 +194,7 @@ </histogram> <histogram name="UMA.JavaCachingRecorder.DroppedUserActionCount" - units="samples" expires_after="2021-03-21"> + units="samples" expires_after="2022-03-21"> <owner>bttk@chromium.org</owner> <owner>src/base/metrics/OWNERS</owner> <summary> @@ -204,7 +204,7 @@ </histogram> <histogram name="UMA.JavaCachingRecorder.FlushedHistogramCount" - units="histograms" expires_after="2021-03-21"> + units="histograms" expires_after="2022-03-21"> <owner>bttk@chromium.org</owner> <owner>src/base/metrics/OWNERS</owner> <summary> @@ -213,7 +213,7 @@ </histogram> <histogram name="UMA.JavaCachingRecorder.InputHistogramSampleCount" - units="samples" expires_after="2021-03-21"> + units="samples" expires_after="2022-03-21"> <owner>bttk@chromium.org</owner> <owner>src/base/metrics/OWNERS</owner> <summary> @@ -223,7 +223,7 @@ </histogram> <histogram name="UMA.JavaCachingRecorder.InputUserActionCount" units="samples" - expires_after="2021-03-21"> + expires_after="2022-03-21"> <owner>bttk@chromium.org</owner> <owner>src/base/metrics/OWNERS</owner> <summary>
diff --git a/tools/perf/core/perf_data_generator.py b/tools/perf/core/perf_data_generator.py index fa8df83..eefb6bd 100755 --- a/tools/perf/core/perf_data_generator.py +++ b/tools/perf/core/perf_data_generator.py
@@ -101,7 +101,7 @@ ] # This is an opt-in list for builders which uses dynamic sharding. -DYNAMIC_SHARDING_TESTERS = ['android-pixel2-perf-fyi'] +DYNAMIC_SHARDING_TESTERS = ['android-pixel2-perf-fyi', 'linux-perf-calibration'] CALIBRATION_BUILDERS = { 'linux-perf-calibration': { @@ -221,20 +221,20 @@ }, }, 'fuchsia-perf-fyi': { - # TODO(rohpavone): Temporarily using telemetry_gpu_tests until custom - # test is created to run telemetry benchmarks, as this gets infra up. 'tests': [{ 'isolate': - 'fuchsia_telemetry_gpu_integration_test', + 'performance_web_engine_test_suite', 'extra_args': [ - 'hardware_accelerated_feature', '--show-stdout', - '--browser=web-engine-shell', '--passthrough', '-v', - '--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc', + '--output-format=histograms', + '--experimental-tbmv3-metrics', '--device=custom', - '--custom-device-target=internal.astro_target' + '--custom-device-target=internal.astro_target', + # TODO(rohpavone): Remove this to enable other stories once + # script has stabilized. + '--story-filter=load:chrome:blank', ], 'type': - TEST_TYPES.GENERIC, + TEST_TYPES.TELEMETRY, }], 'platform': 'fuchsia', @@ -935,6 +935,7 @@ # chromium_swarming recipe_module ignore this dimension. 'gpu': None, 'os': 'ChromeOS', + 'device_status': 'available', 'device_type': 'eve', }, }, @@ -1339,6 +1340,8 @@ elif (tester_config['platform'] == 'win' and tester_config['target_bits'] == 64): browser_name = 'release_x64' + elif tester_config['platform'] == 'fuchsia': + browser_name = 'web-engine-shell' else: browser_name ='release' test_args = [
diff --git a/tools/perf/core/perf_json_config_validator.py b/tools/perf/core/perf_json_config_validator.py index 7c9830f..448e902 100644 --- a/tools/perf/core/perf_json_config_validator.py +++ b/tools/perf/core/perf_json_config_validator.py
@@ -11,8 +11,8 @@ _VALID_SWARMING_DIMENSIONS = { 'gpu', 'device_ids', 'os', 'pool', 'perf_tests', 'perf_tests_with_args', - 'cpu', 'device_os', 'device_type', 'device_os_flavor', 'id', 'mac_model', - 'synthetic_product_name' + 'cpu', 'device_os', 'device_status', 'device_type', 'device_os_flavor', + 'id', 'mac_model', 'synthetic_product_name' } _DEFAULT_VALID_PERF_POOLS = { 'chrome.tests.perf',
diff --git a/tools/perf/core/perfetto_binary_roller/binary_deps.json b/tools/perf/core/perfetto_binary_roller/binary_deps.json index 5aacacc..f3a89354 100644 --- a/tools/perf/core/perfetto_binary_roller/binary_deps.json +++ b/tools/perf/core/perfetto_binary_roller/binary_deps.json
@@ -2,15 +2,15 @@ "trace_processor_shell": { "win": { "hash": "c490b300106dbf909633a65af57bb3ab0e41befb", - "remote_path": "perfetto_binaries/trace_processor_shell/win/b0d9de4c263359c154e3ca31af9d40055f9840ad/trace_processor_shell.exe" + "remote_path": "perfetto_binaries/trace_processor_shell/win/6ecb51ac72ea4de3663b4442ad3e72b64f660af1/trace_processor_shell.exe" }, "mac": { "hash": "682dc60bd132e8a5f26c37231cb3f221caf20e57", - "remote_path": "perfetto_binaries/trace_processor_shell/mac/b0d9de4c263359c154e3ca31af9d40055f9840ad/trace_processor_shell" + "remote_path": "perfetto_binaries/trace_processor_shell/mac/6ecb51ac72ea4de3663b4442ad3e72b64f660af1/trace_processor_shell" }, "linux": { "hash": "36eed8cb39578770709ab41a3a76444f5c1ec727", - "remote_path": "perfetto_binaries/trace_processor_shell/linux/3243c529bd7e2e1f3a0163ccedb680f4e3cdf335/trace_processor_shell" + "remote_path": "perfetto_binaries/trace_processor_shell/linux/6ecb51ac72ea4de3663b4442ad3e72b64f660af1/trace_processor_shell" } }, "power_profile.sql": {
diff --git a/tools/perf/core/results_dashboard.py b/tools/perf/core/results_dashboard.py index cea7ec24..20dd157 100755 --- a/tools/perf/core/results_dashboard.py +++ b/tools/perf/core/results_dashboard.py
@@ -177,7 +177,7 @@ subprocess.check_call(cmd) -def MakeListOfPoints(charts, bot, test_name, buildername, +def MakeListOfPoints(charts, bot, test_name, project, buildbucket, buildername, buildnumber, supplemental_columns, perf_dashboard_machine_group, revisions_dict=None): @@ -223,6 +223,9 @@ result['supplemental_columns'].update(revision_columns) result['supplemental_columns'].update( _GetStdioUriColumn(test_name, buildername, buildnumber)) + result['supplemental_columns'].update( + _GetBuildStatusUriColumn(project, buildbucket, buildername, + buildnumber)) result['supplemental_columns'].update(supplemental_columns) result['value'] = trace_values[0] @@ -239,7 +242,8 @@ return results -def MakeDashboardJsonV1(chart_json, revision_dict, test_name, bot, buildername, +def MakeDashboardJsonV1(chart_json, revision_dict, test_name, bot, project, + buildbucket, buildername, buildnumber, supplemental_dict, is_ref, perf_dashboard_machine_group): """Generates Dashboard JSON in the new Telemetry format. @@ -277,6 +281,8 @@ supplemental.update( _GetStdioUriColumn(test_name, buildername, buildnumber)) + supplemental.update( + _GetBuildStatusUriColumn(project, buildbucket, buildername, buildnumber)) # TODO(sullivan): The android recipe sends "test_name.reference" # while the desktop one just sends "test_name" for ref builds. Need @@ -315,7 +321,7 @@ # converting all perf bots to LUCI (crbug.com/803137). if not (project and buildbucket and buildnumber and buildnumber): return None - return 'https://ci.chromium.org/p/%s/builders/%s/%s/%s' % ( + return 'https://ci.chromium.org/ui/p/%s/builders/%s/%s/%s' % ( urllib.quote(project), urllib.quote(buildbucket), urllib.quote(buildername), @@ -330,6 +336,14 @@ return _CreateLinkColumn('stdio_uri', 'Buildbot stdio', url) +def _GetBuildStatusUriColumn(project, buildbucket, buildername, buildnumber): + """Gets a supplemental column containing buildbot status link.""" + url = _MakeBuildStatusUrl(project, buildbucket, buildername, buildnumber) + if not url: + return {} + return _CreateLinkColumn('build_uri', 'Buildbot status page', url) + + def _CreateLinkColumn(name, label, url): """Returns a column containing markdown link to show on dashboard.""" return {'a_' + name: '[%s](%s)' % (label, url)}
diff --git a/tools/perf/core/upload_results_to_perf_dashboard.py b/tools/perf/core/upload_results_to_perf_dashboard.py index b981816..f04ae952 100755 --- a/tools/perf/core/upload_results_to_perf_dashboard.py +++ b/tools/perf/core/upload_results_to_perf_dashboard.py
@@ -55,6 +55,7 @@ # pylint: disable=redefined-variable-type dashboard_json = results_dashboard.MakeListOfPoints( results, options.configuration_name, stripped_test_name, + options.project, options.buildbucket, options.buildername, options.buildnumber, {}, options.perf_dashboard_machine_group, revisions_dict=revisions) @@ -62,6 +63,7 @@ dashboard_json = results_dashboard.MakeDashboardJsonV1( results, revisions, stripped_test_name, options.configuration_name, + options.project, options.buildbucket, options.buildername, options.buildnumber, {}, reference_build, perf_dashboard_machine_group=options.perf_dashboard_machine_group)
diff --git a/ui/color/core_default_color_mixer.cc b/ui/color/core_default_color_mixer.cc index 36ae809..0e443fe3 100644 --- a/ui/color/core_default_color_mixer.cc +++ b/ui/color/core_default_color_mixer.cc
@@ -22,42 +22,39 @@ ColorMixer& AddMixerForDarkMode(ColorProvider* provider, bool high_contrast) { ColorMixer& mixer = provider->AddMixer(); - if (!high_contrast) { - mixer.AddSet( - {kColorSetCoreDefaults, - { - {kColorAccent, gfx::kGoogleBlue300}, - {kColorAlertHighSeverity, gfx::kGoogleRed300}, - {kColorAlertLowSeverity, gfx::kGoogleGreen300}, - {kColorAlertMediumSeverity, gfx::kGoogleYellow300}, - {kColorMidground, gfx::kGoogleGrey800}, - {kColorPrimaryBackground, SkColorSetRGB(0x29, 0x2A, 0x2D)}, - {kColorPrimaryForeground, gfx::kGoogleGrey200}, - {kColorSecondaryForeground, gfx::kGoogleGrey500}, - {kColorSubtleEmphasisBackground, SkColorSetRGB(0x32, 0x36, 0x39)}, - {kColorTextSelectionBackground, gfx::kGoogleBlue800}, - }}); - } + mixer.AddSet( + {kColorSetCoreDefaults, + { + {kColorAccent, gfx::kGoogleBlue300}, + {kColorAlertHighSeverity, gfx::kGoogleRed300}, + {kColorAlertLowSeverity, gfx::kGoogleGreen300}, + {kColorAlertMediumSeverity, gfx::kGoogleYellow300}, + {kColorMidground, gfx::kGoogleGrey800}, + {kColorPrimaryBackground, SkColorSetRGB(0x29, 0x2A, 0x2D)}, + {kColorPrimaryForeground, gfx::kGoogleGrey200}, + {kColorSecondaryForeground, gfx::kGoogleGrey500}, + {kColorSubtleEmphasisBackground, SkColorSetRGB(0x32, 0x36, 0x39)}, + {kColorTextSelectionBackground, gfx::kGoogleBlue800}, + }}); return mixer; } ColorMixer& AddMixerForLightMode(ColorProvider* provider, bool high_contrast) { ColorMixer& mixer = provider->AddMixer(); - if (!high_contrast) { - mixer.AddSet({kColorSetCoreDefaults, - { - {kColorAccent, gfx::kGoogleBlue600}, - {kColorAlertHighSeverity, gfx::kGoogleRed600}, - {kColorAlertLowSeverity, gfx::kGoogleGreen700}, - {kColorAlertMediumSeverity, gfx::kGoogleYellow700}, - {kColorMidground, gfx::kGoogleGrey300}, - {kColorPrimaryBackground, SK_ColorWHITE}, - {kColorPrimaryForeground, gfx::kGoogleGrey900}, - {kColorSecondaryForeground, gfx::kGoogleGrey700}, - {kColorSubtleEmphasisBackground, gfx::kGoogleGrey050}, - {kColorTextSelectionBackground, gfx::kGoogleBlue200}, - }}); - } + mixer.AddSet({kColorSetCoreDefaults, + { + {kColorAccent, gfx::kGoogleBlue600}, + {kColorAlertHighSeverity, gfx::kGoogleRed600}, + {kColorAlertLowSeverity, gfx::kGoogleGreen700}, + {kColorAlertMediumSeverity, gfx::kGoogleYellow700}, + {kColorMidground, gfx::kGoogleGrey300}, + {kColorPrimaryBackground, SK_ColorWHITE}, + {kColorPrimaryForeground, gfx::kGoogleGrey900}, + {kColorSecondaryForeground, gfx::kGoogleGrey700}, + {kColorSubtleEmphasisBackground, gfx::kGoogleGrey050}, + {kColorTextSelectionBackground, gfx::kGoogleBlue200}, + }}); + return mixer; }
diff --git a/ui/color/mac/native_color_mixers.mm b/ui/color/mac/native_color_mixers.mm index 53cd58c..4cc08cd 100644 --- a/ui/color/mac/native_color_mixers.mm +++ b/ui/color/mac/native_color_mixers.mm
@@ -37,7 +37,7 @@ void AddNativeCoreColorMixer(ColorProvider* provider, bool dark_window, bool high_contrast) { - ScopedCurrentNSAppearance scoped_nsappearance(dark_window); + ScopedCurrentNSAppearance scoped_nsappearance(dark_window, high_contrast); ColorMixer& mixer = provider->AddMixer(); mixer.AddSet({kColorSetNative, { @@ -50,7 +50,7 @@ void AddNativeUiColorMixer(ColorProvider* provider, bool dark_window, bool high_contrast) { - ScopedCurrentNSAppearance scoped_nsappearance(dark_window); + ScopedCurrentNSAppearance scoped_nsappearance(dark_window, high_contrast); ColorMixer& mixer = provider->AddMixer(); mixer.AddSet( {kColorSetNative, @@ -84,12 +84,23 @@ ? SkColorSetA(gfx::kGoogleGrey800, 0xCC) : SkColorSetA(SK_ColorBLACK, 0x26); mixer[kColorMenuSeparator] = {menu_separator_color}; + + if (!high_contrast) + return; + + if (dark_window) { + mixer[kColorMenuItemForegroundSelected] = {SK_ColorBLACK}; + mixer[kColorMenuItemBackgroundSelected] = {SK_ColorLTGRAY}; + } else { + mixer[kColorMenuItemForegroundSelected] = {SK_ColorWHITE}; + mixer[kColorMenuItemBackgroundSelected] = {SK_ColorDKGRAY}; + } } void AddSystemTintMixer(ColorProvider* provider) { ColorMixer& mixer = provider->AddMixer(); - for (ui::ColorId id = ui::kUiColorsStart; id < kUiColorsLast; ++id) { + for (ColorId id = kUiColorsStart; id < kUiColorsEnd; ++id) { // Apply system tint to non-OS colors. if (!kNativeOSColorIds.contains(id)) mixer[id] += ApplySystemControlTintIfNeeded();
diff --git a/ui/color/mac/scoped_current_nsappearance.h b/ui/color/mac/scoped_current_nsappearance.h index 7b134c1..73e8901c 100644 --- a/ui/color/mac/scoped_current_nsappearance.h +++ b/ui/color/mac/scoped_current_nsappearance.h
@@ -13,7 +13,7 @@ // based on the desired light/dark colors scheme. class COMPONENT_EXPORT(COLOR) ScopedCurrentNSAppearance { public: - explicit ScopedCurrentNSAppearance(bool dark); + explicit ScopedCurrentNSAppearance(bool dark, bool high_contrast); // There should be no reason to copy or move a ScopedCurrentNSAppearance. ScopedCurrentNSAppearance(const ScopedCurrentNSAppearance&) = delete;
diff --git a/ui/color/mac/scoped_current_nsappearance.mm b/ui/color/mac/scoped_current_nsappearance.mm index 62bfad44..35040b0 100644 --- a/ui/color/mac/scoped_current_nsappearance.mm +++ b/ui/color/mac/scoped_current_nsappearance.mm
@@ -7,10 +7,20 @@ #import <Cocoa/Cocoa.h> namespace ui { -ScopedCurrentNSAppearance::ScopedCurrentNSAppearance(bool dark) { +ScopedCurrentNSAppearance::ScopedCurrentNSAppearance(bool dark, + bool high_contrast) { if (@available(macOS 10.14, *)) { - NSAppearanceName appearance = - dark ? NSAppearanceNameDarkAqua : NSAppearanceNameAqua; + NSAppearanceName appearance; + + if (dark) { + appearance = high_contrast + ? NSAppearanceNameAccessibilityHighContrastDarkAqua + : NSAppearanceNameDarkAqua; + } else { + appearance = high_contrast ? NSAppearanceNameAccessibilityHighContrastAqua + : NSAppearanceNameAqua; + } + [NSAppearance setCurrentAppearance:[NSAppearance appearanceNamed:appearance]]; }
diff --git a/ui/color/win/native_color_mixers.cc b/ui/color/win/native_color_mixers.cc index 0b909ba8..d86b5f8 100644 --- a/ui/color/win/native_color_mixers.cc +++ b/ui/color/win/native_color_mixers.cc
@@ -27,6 +27,9 @@ #define MAP(chrome, native) {chrome, color_utils::GetSysSkColor(native)} ColorMixer& mixer = provider->AddMixer(); + if (!high_contrast) + return; + mixer.AddSet( {kColorSetNative, { @@ -63,9 +66,6 @@ MAP(kColorNativeWindowText, COLOR_WINDOWTEXT), }}); - if (!high_contrast) - return; - // Window Background mixer[kColorPrimaryBackground] = {kColorNativeWindow}; mixer[kColorWindowBackground] = {kColorNativeWindow};
diff --git a/ui/compositor/total_animation_throughput_reporter.cc b/ui/compositor/total_animation_throughput_reporter.cc index 7f9e8012..90fbdce 100644 --- a/ui/compositor/total_animation_throughput_reporter.cc +++ b/ui/compositor/total_animation_throughput_reporter.cc
@@ -44,6 +44,9 @@ ui::Compositor* compositor) { throughput_tracker_->Stop(); throughput_tracker_.reset(); + // Stop observing if no need to report multiple times. + if (report_repeating_callback_.is_null()) + compositor_->RemoveObserver(this); } void TotalAnimationThroughputReporter::OnCompositingShuttingDown(
diff --git a/ui/compositor/total_animation_throughput_reporter_unittest.cc b/ui/compositor/total_animation_throughput_reporter_unittest.cc index e62fba3..f801150 100644 --- a/ui/compositor/total_animation_throughput_reporter_unittest.cc +++ b/ui/compositor/total_animation_throughput_reporter_unittest.cc
@@ -11,6 +11,7 @@ #include "base/time/time.h" #include "build/build_config.h" #include "cc/metrics/frame_sequence_metrics.h" +#include "ui/compositor/compositor_observer.h" #include "ui/compositor/layer.h" #include "ui/compositor/layer_animation_sequence.h" #include "ui/compositor/layer_animator.h" @@ -252,6 +253,32 @@ EXPECT_TRUE(checker.WaitUntilReported()); } +namespace { + +class ObserverChecker : public ui::CompositorObserver { + public: + ObserverChecker(ui::Compositor* compositor, + ui::CompositorObserver* reporter_observer) + : reporter_observer_(reporter_observer) { + EXPECT_TRUE(compositor->HasObserver(reporter_observer_)); + compositor->AddObserver(this); + } + ObserverChecker(const ObserverChecker&) = delete; + ObserverChecker& operator=(const ObserverChecker&) = delete; + ~ObserverChecker() override = default; + + // ui::CompositorObserver: + void OnLastAnimationEnded(ui::Compositor* compositor) override { + EXPECT_FALSE(compositor->HasObserver(reporter_observer_)); + compositor->RemoveObserver(this); + } + + private: + ui::CompositorObserver* const reporter_observer_; +}; + +} // namespace + // Make sure the once reporter is called only once. TEST_F(TotalAnimationThroughputReporterTest, OnceReporter) { Layer layer; @@ -267,6 +294,10 @@ TotalAnimationThroughputReporter reporter( compositor(), checker.once_callback(), /*should_delete=*/false); + // Make sure the TotalAnimationThroughputReporter removes itself + // from compositor as observer. + ObserverChecker observer_checker(compositor(), &reporter); + // Report data for animation of opacity goes to 1. layer.SetOpacity(1.0f); EXPECT_TRUE(checker.WaitUntilReported());
diff --git a/ui/gl/init/gl_initializer_linux_x11.cc b/ui/gl/init/gl_initializer_linux_x11.cc index b1f21c4..d4f6b70 100644 --- a/ui/gl/init/gl_initializer_linux_x11.cc +++ b/ui/gl/init/gl_initializer_linux_x11.cc
@@ -149,11 +149,7 @@ } #endif // !BUILDFLAG(USE_STATIC_ANGLE) - if (implementation == kGLImplementationEGLANGLE) { - SetGLImplementation(kGLImplementationEGLANGLE); - } else { - SetGLImplementation(kGLImplementationEGLGLES2); - } + SetGLImplementation(implementation); InitializeStaticGLBindingsGL(); InitializeStaticGLBindingsEGL();
diff --git a/ui/message_center/public/cpp/message_center_constants.h b/ui/message_center/public/cpp/message_center_constants.h index 8a265b5..d3cf0a1 100644 --- a/ui/message_center/public/cpp/message_center_constants.h +++ b/ui/message_center/public/cpp/message_center_constants.h
@@ -58,17 +58,6 @@ const int kMessageFontSize = 12; // For everything but title. const int kMessageLineHeight = 18; // In DIPs. -// Colors. -// Background of the card. -constexpr SkColor kNotificationBackgroundColor = SK_ColorWHITE; -// The focus border. -constexpr SkColor kFocusBorderColor = SkColorSetRGB(64, 128, 250); -// Foreground of small icon image. -constexpr SkColor kSmallImageMaskForegroundColor = SK_ColorWHITE; -// Background of small icon image. -constexpr SkColor kSmallImageMaskBackgroundColor = - SkColorSetRGB(0xa3, 0xa3, 0xa3); - // For list notifications. // Not used when --enabled-new-style-notification is set. const size_t kNotificationMaximumItems = 5;
diff --git a/ui/message_center/public/cpp/notification.cc b/ui/message_center/public/cpp/notification.cc index b900fde..416feb9 100644 --- a/ui/message_center/public/cpp/notification.cc +++ b/ui/message_center/public/cpp/notification.cc
@@ -134,18 +134,28 @@ origin_url_.SchemeIsHTTPOrHTTPS(); } -gfx::Image Notification::GenerateMaskedSmallIcon(int dip_size, - SkColor color) const { +gfx::Image Notification::GenerateMaskedSmallIcon( + int dip_size, + SkColor mask_color, + SkColor background_color, + SkColor foreground_color) const { if (!vector_small_image().is_empty()) return gfx::Image( - gfx::CreateVectorIcon(vector_small_image(), dip_size, color)); + gfx::CreateVectorIcon(vector_small_image(), dip_size, mask_color)); if (small_image().IsEmpty()) return gfx::Image(); // If |vector_small_image| is not available, fallback to raster based // masking and resizing. - gfx::ImageSkia image = small_image().AsImageSkia(); + gfx::ImageSkia image; + if (small_image_needs_additional_masking()) { + image = GetMaskedSmallImage(small_image().AsImageSkia(), background_color, + foreground_color) + .AsImageSkia(); + } else { + image = small_image().AsImageSkia(); + } #if BUILDFLAG(IS_CHROMEOS_ASH) bool create_masked_image = @@ -156,7 +166,8 @@ if (create_masked_image) { image = gfx::ImageSkiaOperations::CreateMaskedImage( - CreateSolidColorImage(image.width(), image.height(), color), image); + CreateSolidColorImage(image.width(), image.height(), mask_color), + image); } gfx::ImageSkia resized = gfx::ImageSkiaOperations::CreateResizedImage( image, skia::ImageOperations::ResizeMethod::RESIZE_BEST, @@ -164,4 +175,22 @@ return gfx::Image(resized); } +// Take the alpha channel of small_image, mask it with the foreground, +// then add the masked foreground on top of the background +gfx::Image Notification::GetMaskedSmallImage(const gfx::ImageSkia& small_image, + SkColor background_color, + SkColor foreground_color) const { + int width = small_image.width(); + int height = small_image.height(); + + const gfx::ImageSkia background = + CreateSolidColorImage(width, height, background_color); + const gfx::ImageSkia foreground = + CreateSolidColorImage(width, height, foreground_color); + const gfx::ImageSkia masked_small_image = + gfx::ImageSkiaOperations::CreateMaskedImage(foreground, small_image); + return gfx::Image(gfx::ImageSkiaOperations::CreateSuperimposedImage( + background, masked_small_image)); +} + } // namespace message_center
diff --git a/ui/message_center/public/cpp/notification.h b/ui/message_center/public/cpp/notification.h index cbd8c0c60..9aa29701 100644 --- a/ui/message_center/public/cpp/notification.h +++ b/ui/message_center/public/cpp/notification.h
@@ -106,6 +106,11 @@ // notification. Optional. gfx::Image small_image; + // If true, the small image should be masked with the foreground and then + // added on top of the background. Masking is delayed until the notification + // is in the views hierarchy or about to be passed to the OS. + bool small_image_needs_additional_masking = false; + #if BUILDFLAG(IS_CHROMEOS_ASH) // If true, we simply use the raw |small_image| icon, ignoring accent color // styling. For example, this is used with raw icons received from Android. @@ -347,6 +352,14 @@ optional_fields_.small_image = image; } + bool small_image_needs_additional_masking() const { + return optional_fields_.small_image_needs_additional_masking; + } + void set_small_image_needs_additional_masking(bool needs_additional_masking) { + optional_fields_.small_image_needs_additional_masking = + needs_additional_masking; + } + const gfx::VectorIcon& vector_small_image() const { return *optional_fields_.vector_small_image; } @@ -362,7 +375,14 @@ // filled by the |color|. // Otherwise, it uses alpha channel of the rasterized |small_image| for // masking. - gfx::Image GenerateMaskedSmallIcon(int dip_size, SkColor color) const; + gfx::Image GenerateMaskedSmallIcon(int dip_size, + SkColor mask_color, + SkColor background_color, + SkColor foreground_color) const; + + gfx::Image GetMaskedSmallImage(const gfx::ImageSkia& small_image, + SkColor background_color, + SkColor foreground_color) const; // Buttons, with icons fetched asynchronously. const std::vector<ButtonInfo>& buttons() const {
diff --git a/ui/message_center/views/notification_view_md.cc b/ui/message_center/views/notification_view_md.cc index 1a47b7e..cb5efde 100644 --- a/ui/message_center/views/notification_view_md.cc +++ b/ui/message_center/views/notification_view_md.cc
@@ -1022,10 +1022,15 @@ accent_color, GetNotificationHeaderViewBackgroundColor()) .color; + auto* theme = GetNativeTheme(); // TODO(knollr): figure out if this has a performance impact and // cache images if so. (crbug.com/768748) - gfx::Image masked_small_icon = - notification.GenerateMaskedSmallIcon(kSmallImageSizeMD, icon_color); + gfx::Image masked_small_icon = notification.GenerateMaskedSmallIcon( + kSmallImageSizeMD, icon_color, + theme->GetSystemColor( + ui::NativeTheme::kColorId_MessageCenterSmallImageMaskBackground), + theme->GetSystemColor( + ui::NativeTheme::kColorId_MessageCenterSmallImageMaskForeground)); if (masked_small_icon.IsEmpty()) { header_row_->ClearAppIcon();
diff --git a/ui/message_center/views/notification_view_md_unittest.cc b/ui/message_center/views/notification_view_md_unittest.cc index f06c339..cd00246 100644 --- a/ui/message_center/views/notification_view_md_unittest.cc +++ b/ui/message_center/views/notification_view_md_unittest.cc
@@ -1012,9 +1012,18 @@ notification_view()->ToggleExpanded(); EXPECT_TRUE(notification_view()->actions_row_->GetVisible()); + auto* theme = notification_view()->GetNativeTheme(); auto app_icon_color_matches = [&](SkColor color) { SkBitmap expected = - notification->GenerateMaskedSmallIcon(kSmallImageSizeMD, color) + notification + ->GenerateMaskedSmallIcon( + kSmallImageSizeMD, color, + theme->GetSystemColor( + ui::NativeTheme:: + kColorId_MessageCenterSmallImageMaskBackground), + theme->GetSystemColor( + ui::NativeTheme:: + kColorId_MessageCenterSmallImageMaskForeground)) .AsBitmap(); SkBitmap actual = *notification_view() ->header_row_->app_icon_view_for_testing()
diff --git a/ui/native_theme/common_theme.cc b/ui/native_theme/common_theme.cc index 082e12f..ec16386 100644 --- a/ui/native_theme/common_theme.cc +++ b/ui/native_theme/common_theme.cc
@@ -316,6 +316,12 @@ accent, (color_id == kInitial) ? 0x4D : gfx::kGoogleGreyAlpha200); } + // Message Center + case NativeTheme::kColorId_MessageCenterSmallImageMaskForeground: + return SK_ColorWHITE; + case NativeTheme::kColorId_MessageCenterSmallImageMaskBackground: + return SkColorSetRGB(0xa3, 0xa3, 0xa3); + // Notification case NativeTheme::kColorId_NotificationBackground: case NativeTheme::kColorId_NotificationColor:
diff --git a/ui/native_theme/native_theme.cc b/ui/native_theme/native_theme.cc index 3ee6518..3396f06 100644 --- a/ui/native_theme/native_theme.cc +++ b/ui/native_theme/native_theme.cc
@@ -300,7 +300,12 @@ bool NativeTheme::AllowColorPipelineRedirection( ColorScheme color_scheme) const { - return color_scheme != ColorScheme::kPlatformHighContrast; + // TODO(kerenzhu): Don't use UserHasContrastPreference(). + // ColorScheme should encode high contrast info but currently on mac it does + // not. ColorScheme should also allow combination of light/dark mode with high + // contrast. + return color_scheme != ColorScheme::kPlatformHighContrast && + !UserHasContrastPreference(); } SkColor NativeTheme::GetSystemColorDeprecated(ColorId color_id,
diff --git a/ui/native_theme/native_theme_color_id.h b/ui/native_theme/native_theme_color_id.h index efb0f865..3117e88 100644 --- a/ui/native_theme/native_theme_color_id.h +++ b/ui/native_theme/native_theme_color_id.h
@@ -86,6 +86,9 @@ OP(kColorId_OverlayScrollbarThumbHoveredFill), \ OP(kColorId_OverlayScrollbarThumbHoveredStroke), \ OP(kColorId_OverlayScrollbarThumbStroke), \ + /* Message Center */ \ + OP(kColorId_MessageCenterSmallImageMaskBackground), \ + OP(kColorId_MessageCenterSmallImageMaskForeground), \ /* Notification view */ \ OP(kColorId_NotificationBackground), \ OP(kColorId_NotificationBackgroundActive), \
diff --git a/ui/native_theme/native_theme_mac.mm b/ui/native_theme/native_theme_mac.mm index dab062c..3ac43db 100644 --- a/ui/native_theme/native_theme_mac.mm +++ b/ui/native_theme/native_theme_mac.mm
@@ -145,7 +145,7 @@ SkColor NativeThemeMac::GetSystemColorDeprecated(ColorId color_id, ColorScheme color_scheme, bool apply_processing) const { - if (UserHasContrastPreference()) { + if (GetPreferredContrast() == PreferredContrast::kMore) { switch (color_id) { case kColorId_SelectedMenuItemForegroundColor: return color_scheme == ColorScheme::kDark ? SK_ColorBLACK @@ -170,8 +170,9 @@ base::Optional<SkColor> NativeThemeMac::GetOSColor( ColorId color_id, ColorScheme color_scheme) const { - ScopedCurrentNSAppearance scoped_nsappearance(color_scheme == - ColorScheme::kDark); + ScopedCurrentNSAppearance scoped_nsappearance( + color_scheme == ColorScheme::kDark, + GetPreferredContrast() == PreferredContrast::kMore); // Even with --secondary-ui-md, menus use the platform colors and styling, and // Mac has a couple of specific color overrides, documented below.
diff --git a/ui/native_theme/native_theme_unittest.cc b/ui/native_theme/native_theme_unittest.cc index b96b655..1ea1f3e 100644 --- a/ui/native_theme/native_theme_unittest.cc +++ b/ui/native_theme/native_theme_unittest.cc
@@ -7,6 +7,7 @@ #include <ostream> #include <tuple> +#include "base/notreached.h" #include "base/strings/stringprintf.h" #include "base/test/scoped_feature_list.h" #include "build/build_config.h" @@ -18,6 +19,12 @@ #include "ui/color/mac/system_color_utils.h" #endif +namespace { + +enum class ContrastMode { kNonHighContrast, kHighContrast }; + +} // namespace + namespace ui { namespace { @@ -47,17 +54,22 @@ } class NativeThemeRedirectedEquivalenceTest - : public testing::TestWithParam< - std::tuple<NativeTheme::ColorScheme, NativeTheme::ColorId>> { + : public testing::TestWithParam<std::tuple<NativeTheme::ColorScheme, + ContrastMode, + NativeTheme::ColorId>> { public: NativeThemeRedirectedEquivalenceTest() = default; static std::string ParamInfoToString( ::testing::TestParamInfo<std::tuple<NativeTheme::ColorScheme, + ContrastMode, NativeTheme::ColorId>> param_info) { auto param_tuple = param_info.param; - return ColorSchemeToString(std::get<0>(param_tuple)) + "_With_" + - ColorIdToString(std::get<1>(param_tuple)); + return ColorSchemeToString( + std::get<NativeTheme::ColorScheme>(param_tuple)) + + ContrastModeToString(std::get<ContrastMode>(param_tuple)) + + "_With_" + + ColorIdToString(std::get<NativeTheme::ColorId>(param_tuple)); } private: @@ -76,6 +88,18 @@ } } + static std::string ContrastModeToString(ContrastMode contrast_mode) { + switch (contrast_mode) { + case ContrastMode::kNonHighContrast: + return ""; + case ContrastMode::kHighContrast: + return "HighContrast"; + default: + NOTREACHED(); + return "InvalidContrastMode"; + } + } + static std::string ColorIdToString(NativeTheme::ColorId id) { if (id >= NativeTheme::ColorId::kColorId_NumColors) { NOTREACHED() << "Invalid color value " << id; @@ -87,8 +111,17 @@ std::pair<PrintableSkColor, PrintableSkColor> GetOriginalAndRedirected( NativeTheme::ColorId color_id, - NativeTheme::ColorScheme color_scheme) { + NativeTheme::ColorScheme color_scheme, + ContrastMode contrast_mode) { NativeTheme* native_theme = NativeTheme::GetInstanceForNativeUi(); + + if (contrast_mode == ContrastMode::kHighContrast) { +#if defined(OS_WIN) + color_scheme = NativeTheme::ColorScheme::kPlatformHighContrast; +#endif + native_theme->set_preferred_contrast(NativeTheme::PreferredContrast::kMore); + } + PrintableSkColor original{ native_theme->GetSystemColor(color_id, color_scheme)}; @@ -96,6 +129,8 @@ scoped_feature_list.InitAndEnableFeature(features::kColorProviderRedirection); PrintableSkColor redirected{ native_theme->GetSystemColor(color_id, color_scheme)}; + native_theme->set_preferred_contrast( + NativeTheme::PreferredContrast::kNoPreference); return std::make_pair(original, redirected); } @@ -105,10 +140,11 @@ TEST_P(NativeThemeRedirectedEquivalenceTest, NativeUiGetSystemColor) { auto param_tuple = GetParam(); auto color_scheme = std::get<NativeTheme::ColorScheme>(param_tuple); + auto contrast_mode = std::get<ContrastMode>(param_tuple); auto color_id = std::get<NativeTheme::ColorId>(param_tuple); // Verifies that colors with and without the Color Provider are the same. - auto pair = GetOriginalAndRedirected(color_id, color_scheme); + auto pair = GetOriginalAndRedirected(color_id, color_scheme, contrast_mode); auto original = pair.first; auto redirected = pair.second; EXPECT_EQ(original, redirected); @@ -118,11 +154,12 @@ TEST_P(NativeThemeRedirectedEquivalenceTest, NativeUiGetSystemColorWithTint) { auto param_tuple = GetParam(); auto color_scheme = std::get<NativeTheme::ColorScheme>(param_tuple); + auto contrast_mode = std::get<ContrastMode>(param_tuple); auto color_id = std::get<NativeTheme::ColorId>(param_tuple); ScopedEnableGraphiteTint enable_graphite_tint; // Verifies that colors with and without the Color Provider are the same. - auto pair = GetOriginalAndRedirected(color_id, color_scheme); + auto pair = GetOriginalAndRedirected(color_id, color_scheme, contrast_mode); auto original = pair.first; auto redirected = pair.second; EXPECT_EQ(original, redirected); @@ -133,11 +170,11 @@ INSTANTIATE_TEST_SUITE_P( , NativeThemeRedirectedEquivalenceTest, - ::testing::Combine( - ::testing::Values(NativeTheme::ColorScheme::kLight, - NativeTheme::ColorScheme::kDark, - NativeTheme::ColorScheme::kPlatformHighContrast), - ::testing::Values(NATIVE_THEME_COLOR_IDS)), + ::testing::Combine(::testing::Values(NativeTheme::ColorScheme::kLight, + NativeTheme::ColorScheme::kDark), + ::testing::Values(ContrastMode::kNonHighContrast, + ContrastMode::kHighContrast), + ::testing::Values(NATIVE_THEME_COLOR_IDS)), NativeThemeRedirectedEquivalenceTest::ParamInfoToString); #undef OP
diff --git a/ui/ozone/platform/wayland/host/wayland_auxiliary_window.cc b/ui/ozone/platform/wayland/host/wayland_auxiliary_window.cc index 7d8cb10..c22d8be 100644 --- a/ui/ozone/platform/wayland/host/wayland_auxiliary_window.cc +++ b/ui/ozone/platform/wayland/host/wayland_auxiliary_window.cc
@@ -30,6 +30,7 @@ void WaylandAuxiliaryWindow::Hide() { if (!subsurface_) return; + WaylandWindow::Hide(); subsurface_.reset();
diff --git a/ui/ozone/platform/wayland/host/wayland_popup.cc b/ui/ozone/platform/wayland/host/wayland_popup.cc index 73ac973..22b64d9 100644 --- a/ui/ozone/platform/wayland/host/wayland_popup.cc +++ b/ui/ozone/platform/wayland/host/wayland_popup.cc
@@ -78,6 +78,7 @@ if (child_window()) child_window()->Hide(); + WaylandWindow::Hide(); if (shell_popup_) { parent_window()->set_child_window(nullptr);
diff --git a/ui/ozone/platform/wayland/host/wayland_toplevel_window.cc b/ui/ozone/platform/wayland/host/wayland_toplevel_window.cc index 8d86fef3..483ad68 100644 --- a/ui/ozone/platform/wayland/host/wayland_toplevel_window.cc +++ b/ui/ozone/platform/wayland/host/wayland_toplevel_window.cc
@@ -115,6 +115,7 @@ child_window()->Hide(); set_child_window(nullptr); } + WaylandWindow::Hide(); shell_toplevel_.reset(); connection()->ScheduleFlush();
diff --git a/ui/ozone/platform/wayland/host/wayland_window.cc b/ui/ozone/platform/wayland/host/wayland_window.cc index 8926ee3..3089a822 100644 --- a/ui/ozone/platform/wayland/host/wayland_window.cc +++ b/ui/ozone/platform/wayland/host/wayland_window.cc
@@ -175,7 +175,11 @@ } void WaylandWindow::Hide() { - NOTREACHED(); + // Mutter compositor crashes if we don't remove subsurface roles when hiding. + if (primary_subsurface_) + primary_subsurface()->Hide(); + for (auto& subsurface : wayland_subsurfaces_) + subsurface->Hide(); } void WaylandWindow::Close() {
diff --git a/ui/webui/resources/js/icon.js b/ui/webui/resources/js/icon.js index d0af7fd..5c11999 100644 --- a/ui/webui/resources/js/icon.js +++ b/ui/webui/resources/js/icon.js
@@ -131,16 +131,18 @@ * @param {boolean} isSyncedUrlForHistoryUi Should be set to true only if the * caller is an UI aimed at displaying user history, and the requested url * is known to be present in Chrome sync data. - * @param {string} remoteIconUrlForUma In case the entry is contained in - * sync data, we can pass the associated icon url. + * @param {string} remoteIconUrlForUma In case the entry is contained in sync + * data, we can pass the associated icon url. + * @param {number} size The favicon size. * * @return {string} -webkit-image-set for the favicon. */ /* #export */ function getFaviconForPageURL( - url, isSyncedUrlForHistoryUi, remoteIconUrlForUma = '') { + url, isSyncedUrlForHistoryUi, remoteIconUrlForUma = '', size = 16) { // Note: URL param keys used below must match those in the description of // chrome://favicon2 format in components/favicon_base/favicon_url_parser.h. const faviconUrl = getBaseFaviconUrl(); + faviconUrl.searchParams.set('size', size); faviconUrl.searchParams.set('page_url', url); // TODO(dbeam): use the presence of 'allow_google_server_fallback' to // indicate true, otherwise false.
diff --git a/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/PageInfoTest.java b/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/PageInfoTest.java index 65920f6..946a709 100644 --- a/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/PageInfoTest.java +++ b/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/PageInfoTest.java
@@ -11,6 +11,7 @@ import static androidx.test.espresso.matcher.ViewMatchers.withText; import android.content.Context; +import android.os.Build.VERSION_CODES; import android.os.Bundle; import android.view.View; @@ -25,6 +26,7 @@ import org.chromium.base.StrictModeContext; import org.chromium.base.test.util.CriteriaHelper; import org.chromium.base.test.util.CriteriaNotSatisfiedException; +import org.chromium.base.test.util.DisableIf; import org.chromium.content_public.browser.test.util.TestThreadUtils; import org.chromium.weblayer.TestWebLayer; import org.chromium.weblayer.shell.InstrumentationActivity; @@ -104,7 +106,10 @@ @Test @SmallTest - public void testPageInfoCookiesSubPage() { + @DisableIf.Build(message = "Flaky on Android Marshmallow x86, see crbug.com/1188735", + sdk_is_greater_than = VERSION_CODES.LOLLIPOP_MR1, sdk_is_less_than = VERSION_CODES.N) + public void + testPageInfoCookiesSubPage() { Bundle extras = new Bundle(); extras.putBoolean(InstrumentationActivity.EXTRA_URLBAR_TEXT_CLICKABLE, true); InstrumentationActivity activity = mActivityTestRule.launchShellWithUrl(
diff --git a/weblayer/browser/java/org/chromium/weblayer_private/WebLayerImpl.java b/weblayer/browser/java/org/chromium/weblayer_private/WebLayerImpl.java index 4242b19c..515e3bcc 100644 --- a/weblayer/browser/java/org/chromium/weblayer_private/WebLayerImpl.java +++ b/weblayer/browser/java/org/chromium/weblayer_private/WebLayerImpl.java
@@ -48,6 +48,8 @@ import org.chromium.base.library_loader.LibraryLoader; import org.chromium.base.library_loader.LibraryProcessType; import org.chromium.base.metrics.RecordHistogram; +import org.chromium.base.task.AsyncTask; +import org.chromium.base.task.BackgroundOnlyAsyncTask; import org.chromium.base.task.PostTask; import org.chromium.base.task.TaskTraits; import org.chromium.components.browser_ui.contacts_picker.ContactsPickerDialog; @@ -233,7 +235,13 @@ // Load library in the background since it may be expensive. // TODO(crbug.com/1146438): Look into enabling relro sharing in browser process. It seems to // crash when WebView is loaded in the same process. - new Thread(() -> LibraryLoader.getInstance().loadNow()).start(); + new BackgroundOnlyAsyncTask<Void>() { + @Override + protected Void doInBackground() { + LibraryLoader.getInstance().loadNow(); + return null; + } + }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); PackageInfo packageInfo = WebViewFactory.getLoadedPackageInfo();