diff --git a/DEPS b/DEPS index 4948461..35cbc013 100644 --- a/DEPS +++ b/DEPS
@@ -195,11 +195,11 @@ # Three lines of non-changing comments so that # the commit queue can handle CLs rolling Skia # and whatever else without interference from each other. - 'skia_revision': '5bc663a55b26c22f658232e68285201489dfc8f6', + 'skia_revision': 'b82d9b5d3b64d220c34a89a8125420f317ab816d', # 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': '33aadb77fef52be264780851920ccc7d3669c7c2', + 'v8_revision': '26aefc721cb2bbc63e070796204183c53d93abd4', # 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. @@ -211,7 +211,7 @@ # 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': 'aa5bd7657e6cc140b95f3f070f011c65213a2cc1', + 'swiftshader_revision': '4568d8587998b3db6c25ba7def35105ee1c31f42', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling PDFium # and whatever else without interference from each other. @@ -258,7 +258,7 @@ # Three lines of non-changing comments so that # the commit queue can handle CLs rolling catapult # and whatever else without interference from each other. - 'catapult_revision': '2cd291aae824412377a3eb4cc76cb2ec7ee169c4', + 'catapult_revision': 'f80ffe32797885b3ba258485251553dc37a8f5cf', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling libFuzzer # and whatever else without interference from each other. @@ -266,7 +266,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': '9fcc573bd51a414d3554298a0051d1052f329c00', + 'devtools_frontend_revision': 'f3a9afdfa19b5cafa890087b71760c63c736debf', # 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. @@ -946,7 +946,7 @@ Var('chromium_git') + '/codecs/libgav1.git' + '@' + 'e46493b9148e0d1e63f55b5890bff503822616e5', 'src/third_party/glslang/src': - Var('chromium_git') + '/external/github.com/KhronosGroup/glslang.git' + '@' + '7ab4564696a79243a48d6d5169eafdfb117e44fc', + Var('chromium_git') + '/external/github.com/KhronosGroup/glslang.git' + '@' + '928b7b26bd73bcf020687a54e6a8c74052e1e7b2', 'src/third_party/google_toolbox_for_mac/src': { 'url': Var('chromium_git') + '/external/github.com/google/google-toolbox-for-mac.git' + '@' + Var('google_toolbox_for_mac_revision'), @@ -1537,7 +1537,7 @@ Var('chromium_git') + '/v8/v8.git' + '@' + Var('v8_revision'), 'src-internal': { - 'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@ba78a47d745f01b174e7687a89a2c74feb6c357f', + 'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@d134c7ab73b3f2c3afe32d5e04ddc6dcf6c3f1f2', 'condition': 'checkout_src_internal', },
diff --git a/ash/public/cpp/external_arc/message_center/arc_notification_manager.cc b/ash/public/cpp/external_arc/message_center/arc_notification_manager.cc index 13088ce..505cc1e 100644 --- a/ash/public/cpp/external_arc/message_center/arc_notification_manager.cc +++ b/ash/public/cpp/external_arc/message_center/arc_notification_manager.cc
@@ -32,6 +32,7 @@ using arc::mojom::ArcNotificationDataPtr; using arc::mojom::ArcNotificationEvent; using arc::mojom::ArcNotificationPriority; +using arc::mojom::MessageCenterVisibility; using arc::mojom::NotificationConfiguration; using arc::mojom::NotificationConfigurationPtr; using arc::mojom::NotificationsHost; @@ -66,6 +67,31 @@ DISALLOW_COPY_AND_ASSIGN(DoNotDisturbManager); }; +class VisibilityManager : public message_center::MessageCenterObserver { + public: + explicit VisibilityManager(ArcNotificationManager* manager) + : manager_(manager) {} + void OnCenterVisibilityChanged( + message_center::Visibility visibility) override { + manager_->OnMessageCenterVisibilityChanged(toMojom(visibility)); + } + + private: + static MessageCenterVisibility toMojom( + message_center::Visibility visibility) { + if (visibility == message_center::Visibility::VISIBILITY_TRANSIENT) + return MessageCenterVisibility::VISIBILITY_TRANSIENT; + if (visibility == message_center::Visibility::VISIBILITY_MESSAGE_CENTER) + return MessageCenterVisibility::VISIBILITY_MESSAGE_CENTER; + VLOG(2) << "Unknown message_center::Visibility: " << visibility; + return MessageCenterVisibility::VISIBILITY_TRANSIENT; + } + + ArcNotificationManager* const manager_; + + DISALLOW_COPY_AND_ASSIGN(VisibilityManager); +}; + } // namespace class ArcNotificationManager::InstanceOwner { @@ -116,6 +142,7 @@ obs.OnArcNotificationManagerDestroyed(this); message_center_->RemoveObserver(do_not_disturb_manager_.get()); + message_center_->RemoveObserver(visibility_manager_.get()); instance_owner_->holder()->RemoveObserver(this); instance_owner_->holder()->SetHost(nullptr); @@ -143,6 +170,12 @@ // Sync the initial quiet mode state with Android. SetDoNotDisturbStatusOnAndroid(message_center_->IsQuietMode()); + // Sync the initial visibility of message center with Android. + auto visibility = message_center_->IsMessageCenterVisible() + ? MessageCenterVisibility::VISIBILITY_MESSAGE_CENTER + : MessageCenterVisibility::VISIBILITY_TRANSIENT; + OnMessageCenterVisibilityChanged(visibility); + // Set configuration variables for notifications on arc. SetNotificationConfiguration(); } @@ -577,6 +610,20 @@ std::move(configuration)); } +void ArcNotificationManager::OnMessageCenterVisibilityChanged( + MessageCenterVisibility visibility) { + auto* notifications_instance = ARC_GET_INSTANCE_FOR_METHOD( + instance_owner_->holder(), OnMessageCenterVisibilityChanged); + + if (!notifications_instance) { + VLOG(2) << "Trying to report message center visibility (" << visibility + << "), but the ARC channel has already gone."; + return; + } + + notifications_instance->OnMessageCenterVisibilityChanged(visibility); +} + void ArcNotificationManager::AddObserver(Observer* observer) { observers_.AddObserver(observer); } @@ -594,6 +641,7 @@ main_profile_id_ = main_profile_id; message_center_ = message_center; do_not_disturb_manager_ = std::make_unique<DoNotDisturbManager>(this); + visibility_manager_ = std::make_unique<VisibilityManager>(this); instance_owner_->holder()->SetHost(this); instance_owner_->holder()->AddObserver(this); @@ -602,6 +650,7 @@ SetCustomNotificationViewFactory(); } message_center_->AddObserver(do_not_disturb_manager_.get()); + message_center_->AddObserver(visibility_manager_.get()); } } // namespace ash
diff --git a/ash/public/cpp/external_arc/message_center/arc_notification_manager.h b/ash/public/cpp/external_arc/message_center/arc_notification_manager.h index 24777cb..6612dd8 100644 --- a/ash/public/cpp/external_arc/message_center/arc_notification_manager.h +++ b/ash/public/cpp/external_arc/message_center/arc_notification_manager.h
@@ -75,6 +75,10 @@ void CancelPress(const std::string& key); void SetNotificationConfiguration(); + // Methods called from |visibility_manager_|: + void OnMessageCenterVisibilityChanged( + arc::mojom::MessageCenterVisibility visibility); + // ArcNotificationManagerBase implementation: void AddObserver(Observer* observer) override; void RemoveObserver(Observer* observer) override; @@ -96,6 +100,7 @@ message_center::MessageCenter* message_center_ = nullptr; std::unique_ptr<message_center::MessageCenterObserver> do_not_disturb_manager_; + std::unique_ptr<message_center::MessageCenterObserver> visibility_manager_; using ItemMap = std::unordered_map<std::string, std::unique_ptr<ArcNotificationItem>>;
diff --git a/base/cpu.cc b/base/cpu.cc index d2c197e8..4272cbd 100644 --- a/base/cpu.cc +++ b/base/cpu.cc
@@ -15,6 +15,16 @@ #include "base/stl_util.h" +#if defined(OS_LINUX) || defined(OS_ANDROID) || defined(OS_AIX) +#include "base/containers/flat_set.h" +#include "base/files/file_util.h" +#include "base/notreached.h" +#include "base/strings/string_number_conversions.h" +#include "base/strings/stringprintf.h" +#include "base/system/sys_info.h" +#include "base/threading/thread_restrictions.h" +#endif + #if defined(ARCH_CPU_ARM_FAMILY) && (defined(OS_ANDROID) || defined(OS_LINUX)) #include "base/files/file_util.h" #endif @@ -310,4 +320,74 @@ return PENTIUM; } +#if defined(OS_LINUX) || defined(OS_ANDROID) || defined(OS_AIX) +// static +std::vector<CPU::CoreType> CPU::GuessCoreTypes() { + // Try to guess the CPU architecture and cores of each cluster by comparing + // the maximum frequencies of the available (online and offline) cores. + const char kCPUMaxFreqPath[] = + "/sys/devices/system/cpu/cpu%d/cpufreq/cpuinfo_max_freq"; + int num_cpus = base::SysInfo::NumberOfProcessors(); + std::vector<CPU::CoreType> core_index_to_type(num_cpus, CoreType::kUnknown); + + std::vector<uint32_t> max_core_frequencies_mhz(num_cpus, 0); + base::flat_set<uint32_t> frequencies_mhz; + + { + // Reading from cpuinfo_max_freq doesn't block (it amounts to reading a + // struct field from the cpufreq kernel driver). + ThreadRestrictions::ScopedAllowIO allow_io; + for (int core_index = 0; core_index < num_cpus; ++core_index) { + std::string content; + uint32_t frequency_khz = 0; + auto path = base::StringPrintf(kCPUMaxFreqPath, core_index); + if (ReadFileToString(base::FilePath(path), &content)) + base::StringToUint(content, &frequency_khz); + uint32_t frequency_mhz = frequency_khz / 1000; + max_core_frequencies_mhz[core_index] = frequency_mhz; + if (frequency_mhz > 0) + frequencies_mhz.insert(frequency_mhz); + } + } + + size_t num_frequencies = frequencies_mhz.size(); + + for (int core_index = 0; core_index < num_cpus; ++core_index) { + uint32_t core_frequency_mhz = max_core_frequencies_mhz[core_index]; + + CoreType core_type = CoreType::kOther; + if (num_frequencies == 1u) { + core_type = CoreType::kSymmetric; + } else if (num_frequencies == 2u || num_frequencies == 3u) { + auto it = frequencies_mhz.find(core_frequency_mhz); + if (it != frequencies_mhz.end()) { + // base::flat_set is sorted. + size_t frequency_index = it - frequencies_mhz.begin(); + switch (frequency_index) { + case 0: + core_type = num_frequencies == 2u + ? CoreType::kBigLittle_Little + : CoreType::kBigLittleBigger_Little; + break; + case 1: + core_type = num_frequencies == 2u ? CoreType::kBigLittle_Big + : CoreType::kBigLittleBigger_Big; + break; + case 2: + DCHECK_EQ(num_frequencies, 3u); + core_type = CoreType::kBigLittleBigger_Bigger; + break; + default: + NOTREACHED(); + break; + } + } + } + core_index_to_type[core_index] = core_type; + } + + return core_index_to_type; +} +#endif // defined(OS_LINUX) || defined(OS_ANDROID) || defined(OS_AIX) + } // namespace base
diff --git a/base/cpu.h b/base/cpu.h index 4fcda69..a2c3710 100644 --- a/base/cpu.h +++ b/base/cpu.h
@@ -71,6 +71,28 @@ IntelMicroArchitecture GetIntelMicroArchitecture() const; const std::string& cpu_brand() const { return cpu_brand_; } +#if defined(OS_LINUX) || defined(OS_ANDROID) || defined(OS_AIX) + enum class CoreType { + kUnknown = 0, + kOther, + kSymmetric, + kBigLittle_Little, + kBigLittle_Big, + kBigLittleBigger_Little, + kBigLittleBigger_Big, + kBigLittleBigger_Bigger, + kMaxValue = kBigLittleBigger_Bigger + }; + + // Attempts to guess the core types of individual CPU cores based on frequency + // information from /sys/devices/system/cpu/cpuN/cpufreq/cpuinfo_max_freq. + // Beware that it is kernel/hardware dependent whether the information from + // sys is accurate. + // + // Returns a vector with the guessed type for core N at index N. + static std::vector<CoreType> GuessCoreTypes(); +#endif // defined(OS_LINUX) || defined(OS_ANDROID) || defined(OS_AIX) + private: // Query the processor for CPUID information. void Initialize();
diff --git a/base/process/process_metrics.h b/base/process/process_metrics.h index 1dd6049..bdd3134 100644 --- a/base/process/process_metrics.h +++ b/base/process/process_metrics.h
@@ -17,6 +17,7 @@ #include <vector> #include "base/base_export.h" +#include "base/cpu.h" #include "base/gtest_prod_util.h" #include "base/macros.h" #include "base/optional.h" @@ -140,20 +141,9 @@ // combination with a non-zero CPU time value. // NOTE: Currently only supported on Linux/Android, and only on devices that // expose per-pid/tid time_in_state files in /proc. - enum class CoreType { - kUnknown = 0, - kOther, - kSymmetric, - kBigLittle_Little, - kBigLittle_Big, - kBigLittleBigger_Little, - kBigLittleBigger_Big, - kBigLittleBigger_Bigger, - kMaxValue = kBigLittleBigger_Bigger - }; struct ThreadTimeInState { PlatformThreadId thread_id; - CoreType core_type; // type of the cores in this cluster. + CPU::CoreType core_type; // type of the cores in this cluster. uint32_t cluster_core_index; // index of the first core in the cluster. uint64_t core_frequency_khz; TimeDelta cumulative_cpu_time; @@ -253,11 +243,10 @@ #endif #if defined(OS_LINUX) || defined(OS_ANDROID) || defined(OS_AIX) - CoreType GetCoreType(int core_index); - void GuessCoreTypes(); + CPU::CoreType GetCoreType(int core_index); // Initialized on the first call to GetCoreType(). - base::Optional<std::vector<CoreType>> core_index_to_type_; + base::Optional<std::vector<CPU::CoreType>> core_index_to_type_; #endif // defined(OS_LINUX) || defined(OS_ANDROID) || defined(OS_AIX) #if defined(OS_WIN)
diff --git a/base/process/process_metrics_linux.cc b/base/process/process_metrics_linux.cc index 5392ce4..0a7eb6a 100644 --- a/base/process/process_metrics_linux.cc +++ b/base/process/process_metrics_linux.cc
@@ -16,7 +16,7 @@ #include <utility> -#include "base/containers/flat_set.h" +#include "base/cpu.h" #include "base/files/dir_reader_posix.h" #include "base/files/file_util.h" #include "base/logging.h" @@ -29,8 +29,6 @@ #include "base/strings/string_split.h" #include "base/strings/string_tokenizer.h" #include "base/strings/string_util.h" -#include "base/strings/stringprintf.h" -#include "base/system/sys_info.h" #include "base/threading/thread_restrictions.h" #include "build/build_config.h" @@ -448,7 +446,7 @@ PlatformThreadId tid, TimeInStatePerThread& time_in_state_per_thread) { uint32_t current_core_index = 0; - CoreType current_core_type = CoreType::kOther; + CPU::CoreType current_core_type = CPU::CoreType::kOther; bool header_seen = false; const char* begin = content.data(); @@ -509,81 +507,16 @@ return true; } -ProcessMetrics::CoreType ProcessMetrics::GetCoreType(int core_index) { +CPU::CoreType ProcessMetrics::GetCoreType(int core_index) { if (!core_index_to_type_) - GuessCoreTypes(); + core_index_to_type_ = CPU::GuessCoreTypes(); if (static_cast<size_t>(core_index) >= core_index_to_type_->size()) - return CoreType::kUnknown; + return CPU::CoreType::kUnknown; return core_index_to_type_->at(static_cast<size_t>(core_index)); } -void ProcessMetrics::GuessCoreTypes() { - // Try to guess the CPU architecture and cores of each cluster by comparing - // the maximum frequencies of the available (online and offline) cores. - const char kCPUMaxFreqPath[] = - "/sys/devices/system/cpu/cpu%d/cpufreq/cpuinfo_max_freq"; - int num_cpus = base::SysInfo::NumberOfProcessors(); - core_index_to_type_ = std::vector<CoreType>(num_cpus, CoreType::kUnknown); - - std::vector<uint32_t> max_core_frequencies_mhz(num_cpus, 0); - base::flat_set<uint32_t> frequencies_mhz; - - { - // Reading from cpuinfo_max_freq doesn't block (it amounts to reading a - // struct field from the cpufreq kernel driver). - ThreadRestrictions::ScopedAllowIO allow_io; - for (int core_index = 0; core_index < num_cpus; ++core_index) { - std::string content; - uint32_t frequency_khz = 0; - auto path = base::StringPrintf(kCPUMaxFreqPath, core_index); - if (ReadFileToString(base::FilePath(path), &content)) - base::StringToUint(content, &frequency_khz); - uint32_t frequency_mhz = frequency_khz / 1000; - max_core_frequencies_mhz[core_index] = frequency_mhz; - if (frequency_mhz > 0) - frequencies_mhz.insert(frequency_mhz); - } - } - - size_t num_frequencies = frequencies_mhz.size(); - - for (int core_index = 0; core_index < num_cpus; ++core_index) { - uint32_t core_frequency_mhz = max_core_frequencies_mhz[core_index]; - - CoreType core_type = CoreType::kOther; - if (num_frequencies == 1u) { - core_type = CoreType::kSymmetric; - } else if (num_frequencies == 2u || num_frequencies == 3u) { - auto it = frequencies_mhz.find(core_frequency_mhz); - if (it != frequencies_mhz.end()) { - // base::flat_set is sorted. - size_t frequency_index = it - frequencies_mhz.begin(); - switch (frequency_index) { - case 0: - core_type = num_frequencies == 2u - ? CoreType::kBigLittle_Little - : CoreType::kBigLittleBigger_Little; - break; - case 1: - core_type = num_frequencies == 2u ? CoreType::kBigLittle_Big - : CoreType::kBigLittleBigger_Big; - break; - case 2: - DCHECK_EQ(num_frequencies, 3u); - core_type = CoreType::kBigLittleBigger_Bigger; - break; - default: - NOTREACHED(); - break; - } - } - } - (*core_index_to_type_)[core_index] = core_type; - } -} - const char kProcSelfExe[] = "/proc/self/exe"; namespace {
diff --git a/base/time/time.h b/base/time/time.h index b7958b4..4d52f2b 100644 --- a/base/time/time.h +++ b/base/time/time.h
@@ -305,6 +305,15 @@ ? std::numeric_limits<int64_t>::max() : std::numeric_limits<int64_t>::min(); } + constexpr double FltDiv(TimeDelta a) const { + // 0/0 and inf/inf (any combination of positive and negative) are invalid + // (they are almost certainly not intentional, and result in NaN, which + // turns into 0 if clamped to an integer; this makes introducing subtle bugs + // too easy). + CHECK((!is_zero() || !a.is_zero()) && (!is_inf() || !a.is_inf())); + + return ToDouble() / a.ToDouble(); + } constexpr TimeDelta operator%(TimeDelta a) const { return TimeDelta(a.is_inf() ? delta_ : (delta_ % a.delta_)); @@ -348,6 +357,15 @@ // known-positive value. static constexpr TimeDelta FromProduct(int64_t value, int64_t positive_value); + // Returns a double representation of this TimeDelta's tick count. In + // particular, Max()/Min() are converted to +/-infinity. + constexpr double ToDouble() const { + if (!is_inf()) + return static_cast<double>(delta_); + return (delta_ < 0) ? -std::numeric_limits<double>::infinity() + : std::numeric_limits<double>::infinity(); + } + // Delta in microseconds. int64_t delta_; };
diff --git a/base/time/time_unittest.cc b/base/time/time_unittest.cc index 3ed261e..ddeb7641 100644 --- a/base/time/time_unittest.cc +++ b/base/time/time_unittest.cc
@@ -1779,6 +1779,8 @@ ""); static_assert(3 == kElevenSeconds.IntDiv(kThreeSeconds), ""); static_assert(0 == kThreeSeconds.IntDiv(kElevenSeconds), ""); + static_assert(11.0 / 3.0 == kElevenSeconds.FltDiv(kThreeSeconds), ""); + static_assert(3.0 / 11.0 == kThreeSeconds.FltDiv(kElevenSeconds), ""); static_assert(TimeDelta::FromSeconds(2) == kElevenSeconds % kThreeSeconds, ""); } @@ -1826,6 +1828,18 @@ static_assert(TimeDelta::Min().IntDiv(TimeDelta::FromSeconds(-10)) == std::numeric_limits<int64_t>::max(), ""); + static_assert(TimeDelta::Max().FltDiv(TimeDelta::FromSeconds(10)) == + std::numeric_limits<double>::infinity(), + ""); + static_assert(TimeDelta::Max().FltDiv(TimeDelta::FromSeconds(-10)) == + -std::numeric_limits<double>::infinity(), + ""); + static_assert(TimeDelta::Min().FltDiv(TimeDelta::FromSeconds(10)) == + -std::numeric_limits<double>::infinity(), + ""); + static_assert(TimeDelta::Min().FltDiv(TimeDelta::FromSeconds(-10)) == + std::numeric_limits<double>::infinity(), + ""); // Division by zero. static_assert((TimeDelta::FromSeconds(1) / 0).is_max(), ""); @@ -1848,6 +1862,14 @@ static_assert(TimeDelta::Min().IntDiv(TimeDelta()) == std::numeric_limits<int64_t>::min(), ""); + EXPECT_EQ(std::numeric_limits<double>::infinity(), + TimeDelta::FromSeconds(1).FltDiv(TimeDelta())); + EXPECT_EQ(-std::numeric_limits<double>::infinity(), + TimeDelta::FromSeconds(-1).FltDiv(TimeDelta())); + EXPECT_EQ(std::numeric_limits<double>::infinity(), + TimeDelta::Max().FltDiv(TimeDelta())); + EXPECT_EQ(-std::numeric_limits<double>::infinity(), + TimeDelta::Min().FltDiv(TimeDelta())); // Division by infinity. static_assert(kLargeDelta.IntDiv(TimeDelta::Min()) == 0, ""); @@ -1866,6 +1888,10 @@ static_assert(TimeDelta::Max().IntDiv(TimeDelta::Max()) == std::numeric_limits<int64_t>::max(), ""); + static_assert(kLargeDelta.FltDiv(TimeDelta::Min()) == 0, ""); + static_assert(kLargeDelta.FltDiv(TimeDelta::Max()) == 0, ""); + static_assert(kLargeNegative.FltDiv(TimeDelta::Min()) == 0, ""); + static_assert(kLargeNegative.FltDiv(TimeDelta::Max()) == 0, ""); static_assert(TimeDelta::FromSeconds(10) % TimeDelta::Min() == TimeDelta::FromSeconds(10),
diff --git a/build/fuchsia/linux.sdk.sha1 b/build/fuchsia/linux.sdk.sha1 index 4d4f86cd..e408268 100644 --- a/build/fuchsia/linux.sdk.sha1 +++ b/build/fuchsia/linux.sdk.sha1
@@ -1 +1 @@ -0.20200801.2.1 +0.20200803.0.1
diff --git a/build/fuchsia/mac.sdk.sha1 b/build/fuchsia/mac.sdk.sha1 index 565edda..3d997cb 100644 --- a/build/fuchsia/mac.sdk.sha1 +++ b/build/fuchsia/mac.sdk.sha1
@@ -1 +1 @@ -0.20200801.1.1 +0.20200802.3.1
diff --git a/chrome/VERSION b/chrome/VERSION index 810e02f86..8ed0b76 100644 --- a/chrome/VERSION +++ b/chrome/VERSION
@@ -1,4 +1,4 @@ MAJOR=86 MINOR=0 -BUILD=4220 +BUILD=4222 PATCH=0
diff --git a/chrome/android/chrome_java_sources.gni b/chrome/android/chrome_java_sources.gni index 4bea238c..a0bf1e83 100644 --- a/chrome/android/chrome_java_sources.gni +++ b/chrome/android/chrome_java_sources.gni
@@ -749,6 +749,8 @@ "java/src/org/chromium/chrome/browser/gesturenav/AndroidUiNavigationGlow.java", "java/src/org/chromium/chrome/browser/gesturenav/CompositorNavigationGlow.java", "java/src/org/chromium/chrome/browser/gesturenav/GestureNavMetrics.java", + "java/src/org/chromium/chrome/browser/gesturenav/GestureNavigationProperties.java", + "java/src/org/chromium/chrome/browser/gesturenav/GestureNavigationViewBinder.java", "java/src/org/chromium/chrome/browser/gesturenav/HistoryNavigationCoordinator.java", "java/src/org/chromium/chrome/browser/gesturenav/HistoryNavigationDelegate.java", "java/src/org/chromium/chrome/browser/gesturenav/HistoryNavigationLayout.java",
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/SwipeRefreshHandler.java b/chrome/android/java/src/org/chromium/chrome/browser/SwipeRefreshHandler.java index fb3773d..d8afd70 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/SwipeRefreshHandler.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/SwipeRefreshHandler.java
@@ -16,7 +16,7 @@ import org.chromium.base.metrics.RecordUserAction; import org.chromium.base.task.PostTask; import org.chromium.chrome.R; -import org.chromium.chrome.browser.gesturenav.NavigationHandler; +import org.chromium.chrome.browser.gesturenav.HistoryNavigationCoordinator; import org.chromium.chrome.browser.tab.EmptyTabObserver; import org.chromium.chrome.browser.tab.Tab; import org.chromium.chrome.browser.tab.TabWebContentsUserData; @@ -72,7 +72,7 @@ // Handles overscroll history navigation. Gesture events from native layer are forwarded // to this object. Remains null while navigation feature is disabled due to feature flag, // system settings (Q and forward), etc. - private NavigationHandler mNavigationHandler; + private HistoryNavigationCoordinator mNavigationCoordinator; public static SwipeRefreshHandler from(Tab tab) { SwipeRefreshHandler handler = get(tab); @@ -155,7 +155,7 @@ public void cleanupWebContents(WebContents webContents) { detachSwipeRefreshLayoutIfNecessary(); mContainerView = null; - mNavigationHandler = null; + mNavigationCoordinator = null; setEnabled(false); } @@ -188,9 +188,10 @@ attachSwipeRefreshLayoutIfNecessary(); return mSwipeRefreshLayout.start(); } else if (type == OverscrollAction.HISTORY_NAVIGATION) { - if (mNavigationHandler != null) { - mNavigationHandler.onDown(); // Simulates the initial onDown event. - boolean navigable = mNavigationHandler.navigate(navigateForward, startX, startY); + if (mNavigationCoordinator != null) { + mNavigationCoordinator.startGesture(); + boolean navigable = + mNavigationCoordinator.triggerUi(navigateForward, startX, startY); boolean showGlow = navigateForward && !mTab.canGoForward(); return showGlow || navigable; } @@ -200,11 +201,11 @@ } /** - * Sets {@link NavigationHandler} object. - * @param layout {@link NavigationHandler} object. + * Sets {@link HistoryNavigationCoordinator} object. + * @param layout {@link HistoryNavigationCoordinator} object. */ - public void setNavigationHandler(NavigationHandler navigationHandler) { - mNavigationHandler = navigationHandler; + public void setNavigationCoordinator(HistoryNavigationCoordinator navigationHandler) { + mNavigationCoordinator = navigationHandler; } @Override @@ -213,7 +214,7 @@ if (mSwipeType == OverscrollAction.PULL_TO_REFRESH) { mSwipeRefreshLayout.pull(yDelta); } else if (mSwipeType == OverscrollAction.HISTORY_NAVIGATION) { - if (mNavigationHandler != null) mNavigationHandler.pull(xDelta); + if (mNavigationCoordinator != null) mNavigationCoordinator.pull(xDelta); } TraceEvent.end("SwipeRefreshHandler.pull"); } @@ -224,7 +225,7 @@ if (mSwipeType == OverscrollAction.PULL_TO_REFRESH) { mSwipeRefreshLayout.release(allowRefresh); } else if (mSwipeType == OverscrollAction.HISTORY_NAVIGATION) { - if (mNavigationHandler != null) mNavigationHandler.release(allowRefresh); + if (mNavigationCoordinator != null) mNavigationCoordinator.release(allowRefresh); } TraceEvent.end("SwipeRefreshHandler.release"); } @@ -233,7 +234,7 @@ public void reset() { cancelStopRefreshingRunnable(); if (mSwipeRefreshLayout != null) mSwipeRefreshLayout.reset(); - if (mNavigationHandler != null) mNavigationHandler.reset(); + if (mNavigationCoordinator != null) mNavigationCoordinator.reset(); } @Override
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/firstrun/FirstRunUtils.java b/chrome/android/java/src/org/chromium/chrome/browser/firstrun/FirstRunUtils.java index 853bd15..37914d3 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/firstrun/FirstRunUtils.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/firstrun/FirstRunUtils.java
@@ -113,9 +113,17 @@ FirstRunUtilsJni.get().setEulaAccepted(); } + /** + * @return Whether the ToS should be shown during the first-run for CCTs/PWAs. + */ + public static boolean isCctTosDialogEnabled() { + return FirstRunUtilsJni.get().getCctTosDialogEnabled(); + } + @NativeMethods public interface Natives { boolean getFirstRunEulaAccepted(); void setEulaAccepted(); + boolean getCctTosDialogEnabled(); } }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/gesturenav/AndroidUiNavigationGlow.java b/chrome/android/java/src/org/chromium/chrome/browser/gesturenav/AndroidUiNavigationGlow.java index d733e97c..293fcc8a 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/gesturenav/AndroidUiNavigationGlow.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/gesturenav/AndroidUiNavigationGlow.java
@@ -28,6 +28,11 @@ */ private final GlowView mGlowView; + /** + * Total amount of pull offset. + */ + private float mTotalPullOffset; + public AndroidUiNavigationGlow(ViewGroup parentView) { super(parentView); mGlowView = new GlowView(mParentView.getContext()); @@ -43,7 +48,14 @@ } @Override - public void onScroll(float xDelta) { + public float getPullOffset() { + return mTotalPullOffset; + } + + @Override + public void onScroll(float offset) { + float xDelta = -(offset - mTotalPullOffset); + mTotalPullOffset = offset; mGlowView.onPull(xDelta / mParentView.getWidth()); } @@ -54,6 +66,7 @@ if (mGlowView.getParent() != null) { mParentView.postDelayed(mRemoveGlowViewRunnable, REMOVE_RUNNABLE_DELAY_MS); } + mTotalPullOffset = 0.f; } @Override
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/gesturenav/CompositorNavigationGlow.java b/chrome/android/java/src/org/chromium/chrome/browser/gesturenav/CompositorNavigationGlow.java index 38bcbaae..61a3032 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/gesturenav/CompositorNavigationGlow.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/gesturenav/CompositorNavigationGlow.java
@@ -38,9 +38,15 @@ } @Override - public void onScroll(float xDelta) { + public float getPullOffset() { + return mAccumulatedScroll; + } + + @Override + public void onScroll(float offset) { if (mNativeNavigationGlow == 0) return; - mAccumulatedScroll += xDelta; + float xDelta = -(offset - mAccumulatedScroll); + mAccumulatedScroll = offset; CompositorNavigationGlowJni.get().onOverscroll( mNativeNavigationGlow, CompositorNavigationGlow.this, mAccumulatedScroll, xDelta); }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/gesturenav/GestureNavigationProperties.java b/chrome/android/java/src/org/chromium/chrome/browser/gesturenav/GestureNavigationProperties.java new file mode 100644 index 0000000..959707b --- /dev/null +++ b/chrome/android/java/src/org/chromium/chrome/browser/gesturenav/GestureNavigationProperties.java
@@ -0,0 +1,55 @@ +// 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.gesturenav; + +import android.gesture.GesturePoint; + +import org.chromium.ui.modelutil.PropertyKey; +import org.chromium.ui.modelutil.PropertyModel.WritableBooleanPropertyKey; +import org.chromium.ui.modelutil.PropertyModel.WritableFloatPropertyKey; +import org.chromium.ui.modelutil.PropertyModel.WritableIntPropertyKey; +import org.chromium.ui.modelutil.PropertyModel.WritableObjectPropertyKey; + +/** + * Properties used for gesture navigation view model. + */ +class GestureNavigationProperties { + /** + * Gesture navigation action as defined in {@link NavigationHandler.GestureAction}. + */ + static final WritableIntPropertyKey ACTION = new WritableIntPropertyKey(); + + /** + * Gesture navigation direction. {@code true} for forward navigation, {@code false} for back. + */ + static final WritableBooleanPropertyKey DIRECTION = new WritableBooleanPropertyKey(); + + /** + * Whether to allow a sufficiently large pull to trigger the navigation action and + * animation sequence. Set for {@link GestureAction.RELEASE}. + */ + static final WritableBooleanPropertyKey ALLOW_NAV = new WritableBooleanPropertyKey(); + + /** + * Amount of total swipe gesture offset. + */ + static final WritableFloatPropertyKey BUBBLE_OFFSET = new WritableFloatPropertyKey(); + static final WritableFloatPropertyKey GLOW_OFFSET = new WritableFloatPropertyKey(); + + /** + * Type of arrow bubble according to the action it will take when navigating. + * @see {@link NavigationBubble#CloseTarget} + */ + static final WritableIntPropertyKey CLOSE_INDICATOR = new WritableIntPropertyKey(); + + /** + * Current position of gesture action. Used for {@link GestureState.GLOW}. + */ + static final WritableObjectPropertyKey<GesturePoint> GESTURE_POS = + new WritableObjectPropertyKey<>(); + + static final PropertyKey[] ALL_KEYS = { + ACTION, DIRECTION, ALLOW_NAV, BUBBLE_OFFSET, GLOW_OFFSET, CLOSE_INDICATOR, GESTURE_POS}; +}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/gesturenav/GestureNavigationViewBinder.java b/chrome/android/java/src/org/chromium/chrome/browser/gesturenav/GestureNavigationViewBinder.java new file mode 100644 index 0000000..11bfbe41 --- /dev/null +++ b/chrome/android/java/src/org/chromium/chrome/browser/gesturenav/GestureNavigationViewBinder.java
@@ -0,0 +1,62 @@ +// 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.gesturenav; + +import static org.chromium.chrome.browser.gesturenav.GestureNavigationProperties.ACTION; +import static org.chromium.chrome.browser.gesturenav.GestureNavigationProperties.ALLOW_NAV; +import static org.chromium.chrome.browser.gesturenav.GestureNavigationProperties.BUBBLE_OFFSET; +import static org.chromium.chrome.browser.gesturenav.GestureNavigationProperties.CLOSE_INDICATOR; +import static org.chromium.chrome.browser.gesturenav.GestureNavigationProperties.DIRECTION; +import static org.chromium.chrome.browser.gesturenav.GestureNavigationProperties.GESTURE_POS; +import static org.chromium.chrome.browser.gesturenav.GestureNavigationProperties.GLOW_OFFSET; + +import android.view.View; + +import org.chromium.chrome.browser.gesturenav.NavigationHandler.GestureAction; +import org.chromium.ui.modelutil.PropertyKey; +import org.chromium.ui.modelutil.PropertyModel; + +/** + * This class responsible for pushing updates to gesture navigation view. + */ +class GestureNavigationViewBinder { + /** + * view binder that associates a view with a model. + * @param model The {@link PropertyObservable} model. + * @param view The view {@link HistoryNavigationLayout} object that is changing. + * @param key The property of the view that changed. + */ + public static void bind(PropertyModel model, View view, PropertyKey key) { + HistoryNavigationLayout topView = (HistoryNavigationLayout) view; + if (BUBBLE_OFFSET == key) { + topView.pullBubble(model.get(BUBBLE_OFFSET)); + } else if (GLOW_OFFSET == key) { + topView.pullGlow(model.get(GLOW_OFFSET)); + } else if (ACTION == key) { + switch (model.get(ACTION)) { + case GestureAction.SHOW_ARROW: + topView.showBubble(model.get(DIRECTION), model.get(CLOSE_INDICATOR)); + break; + case GestureAction.SHOW_GLOW: + topView.showGlow(model.get(GESTURE_POS)); + break; + case GestureAction.RELEASE_BUBBLE: + topView.releaseBubble(model.get(ALLOW_NAV)); + break; + case GestureAction.RELEASE_GLOW: + topView.releaseGlow(); + break; + case GestureAction.RESET_BUBBLE: + topView.resetBubble(); + break; + case GestureAction.RESET_GLOW: + topView.resetGlow(); + break; + default: + assert false : "Unhandled action"; + } + } + } +}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/gesturenav/HistoryNavigationCoordinator.java b/chrome/android/java/src/org/chromium/chrome/browser/gesturenav/HistoryNavigationCoordinator.java index c063ac3..70b930e 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/gesturenav/HistoryNavigationCoordinator.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/gesturenav/HistoryNavigationCoordinator.java
@@ -7,7 +7,7 @@ import android.graphics.Insets; import android.graphics.Rect; import android.os.Build; -import android.view.View; +import android.view.ViewGroup; import androidx.annotation.VisibleForTesting; @@ -25,7 +25,8 @@ import org.chromium.components.browser_ui.bottomsheet.BottomSheetController; import org.chromium.components.browser_ui.widget.InsetObserverView; import org.chromium.content_public.browser.WebContents; - +import org.chromium.ui.modelutil.PropertyModel; +import org.chromium.ui.modelutil.PropertyModelChangeProcessor; /** * Coordinator object for gesture navigation. */ @@ -33,7 +34,7 @@ implements InsetObserverView.WindowInsetObserver, PauseResumeWithNativeObserver { private final Runnable mUpdateNavigationStateRunnable = this::onNavigationStateChanged; - private CompositorViewHolder mCompositorViewHolder; + private ViewGroup mParentView; private HistoryNavigationLayout mNavigationLayout; private InsetObserverView mInsetObserverView; private ActivityTabTabObserver mActivityTabObserver; @@ -48,6 +49,15 @@ private NavigationHandler mNavigationHandler; private HistoryNavigationDelegate mDelegate; + private NavigationSheet mNavigationSheet; + + private NavigationGlow mCompositorGlowEffect; + private NavigationGlow mJavaGlowEffect; + + private WebContents mWebContents; + + private Runnable mInitRunnable; + private Runnable mCleanupRunnable; /** * Creates the coordinator for gesture navigation and initializes internal objects. @@ -86,10 +96,11 @@ InsetObserverView insetObserverView, Function<Tab, Boolean> backShouldCloseTab, Runnable onBackPressed, Consumer<Tab> showHistoryManager, String historyMenu, Supplier<BottomSheetController> bottomSheetControllerSupplier) { - mNavigationLayout = - new HistoryNavigationLayout(compositorViewHolder.getContext(), this::isNativePage); + mNavigationLayout = new HistoryNavigationLayout(compositorViewHolder.getContext(), + this::getGlowEffect, this::getNavigationSheet, + (direction) -> mNavigationHandler.navigate(direction)); - mCompositorViewHolder = compositorViewHolder; + mParentView = compositorViewHolder; mActivityLifecycleDispatcher = lifecycleDispatcher; mBackShouldCloseTab = backShouldCloseTab; mOnBackPressed = onBackPressed; @@ -104,7 +115,7 @@ @Override protected void onObservingDifferentTab(Tab tab) { if (mTab != null && mTab.isInitialized()) { - SwipeRefreshHandler.from(mTab).setNavigationHandler(null); + SwipeRefreshHandler.from(mTab).setNavigationCoordinator(null); } mTab = tab; updateNavigationHandler(); @@ -121,6 +132,16 @@ } }; + mInitRunnable = () -> { + compositorViewHolder.addTouchEventObserver(mNavigationHandler); + }; + mCleanupRunnable = () -> { + compositorViewHolder.removeCallbacks(mUpdateNavigationStateRunnable); + if (mNavigationHandler != null) { + compositorViewHolder.removeTouchEventObserver(mNavigationHandler); + } + }; + // We wouldn't hear about the first tab until the content changed or we switched tabs // if tabProvider.get() != null. Do here what we do when tab switching happens. if (tabProvider.get() != null) { @@ -128,7 +149,6 @@ onNavigationStateChanged(); } - mNavigationLayout.setVisibility(View.INVISIBLE); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { mInsetObserverView = insetObserverView; insetObserverView.addObserver(this); @@ -183,10 +203,8 @@ return true; } else { // Preserve the previous enabled status if queried when the view is in detached state. - if (mCompositorViewHolder == null || !mCompositorViewHolder.isAttachedToWindow()) { - return mEnabled; - } - Insets insets = mCompositorViewHolder.getRootWindowInsets().getSystemGestureInsets(); + if (mParentView == null || !mParentView.isAttachedToWindow()) return mEnabled; + Insets insets = mParentView.getRootWindowInsets().getSystemGestureInsets(); return insets.left == 0 && insets.right == 0; } } @@ -220,13 +238,37 @@ mShowHistoryManager, mHistoryMenu, mBottomSheetControllerSupplier) : HistoryNavigationDelegate.DEFAULT; initNavigationHandler(delegate); - mNavigationLayout.resetCompositorGlow(webContents); + + // Reset CompositorGlowEffect for new a WebContents. Destroy the current one + // (for its native object) so it can be created again lazily. + if (mWebContents != webContents) { + resetCompositorGlowEffect(); + mWebContents = webContents; + } } } else { - mNavigationLayout.destroy(); + resetCompositorGlowEffect(); } - if (mTab != null) { - SwipeRefreshHandler.from(mTab).setNavigationHandler(mNavigationHandler); + if (mTab != null) SwipeRefreshHandler.from(mTab).setNavigationCoordinator(this); + } + + /** + * Create {@link NavigationGlow} object lazily. + * TODO(jinsukkim): Consider using SceneOverlay to replace this. + */ + private NavigationGlow getGlowEffect() { + if (isNativePage()) { + if (mJavaGlowEffect == null) { + mJavaGlowEffect = new AndroidUiNavigationGlow(mNavigationLayout); + } + return mJavaGlowEffect; + } else { + // TODO(crbug.com/1102275): Investigate when this is called with nulled mWebContents. + if (mCompositorGlowEffect == null && mWebContents != null) { + mCompositorGlowEffect = + new CompositorNavigationGlow(mNavigationLayout, mWebContents); + } + return mCompositorGlowEffect; } } @@ -237,16 +279,32 @@ */ private void initNavigationHandler(HistoryNavigationDelegate delegate) { if (mNavigationHandler == null) { - mNavigationHandler = new NavigationHandler( - mNavigationLayout, mNavigationLayout::getGlowEffect, this::isNativePage); - mCompositorViewHolder.addTouchEventObserver(mNavigationHandler); + PropertyModel model = + new PropertyModel.Builder(GestureNavigationProperties.ALL_KEYS).build(); + + PropertyModelChangeProcessor.create( + model, mNavigationLayout, GestureNavigationViewBinder::bind); + + mNavigationHandler = new NavigationHandler(model, mNavigationLayout, this::isNativePage, + () -> mNavigationSheet.isExpanded()); + mInitRunnable.run(); } if (mDelegate != delegate) { mNavigationHandler.setDelegate(delegate); mDelegate = delegate; + + mNavigationSheet = NavigationSheet.isEnabled() + ? NavigationSheet.create(mNavigationLayout, mNavigationLayout.getContext(), + mDelegate.getBottomSheetController()) + : NavigationSheet.DUMMY; + mNavigationSheet.setDelegate(mDelegate.createSheetDelegate()); } } + private NavigationSheet getNavigationSheet() { + return mNavigationSheet; + } + @Override public void onSafeAreaChanged(Rect area) {} @@ -254,13 +312,60 @@ public void onResumeWithNative() { // Check the enabled status again since the system gesture settings might have changed. // Post the task to work around wrong gesture insets returned from the framework. - mNavigationLayout.post(mUpdateNavigationStateRunnable); + mParentView.post(mUpdateNavigationStateRunnable); } @Override public void onPauseWithNative() {} /** + * Starts preparing an edge swipe gesture. + */ + public void startGesture() { + assert mNavigationHandler != null; + + // Simulates the initial onDown event to update the internal state. + mNavigationHandler.onDown(); + } + + /** + * Makes UI (either arrow puck or overscroll glow) visible when an edge swipe + * is made big enough to trigger it. + * @param forward {@code true} for forward navigation, or {@code false} for back. + * @param x X coordinate of the current position. + * @param y Y coordinate of the current position. + * @return {@code true} if the navigation can be triggered. + */ + public boolean triggerUi(boolean forward, float x, float y) { + return mNavigationHandler != null && mNavigationHandler.triggerUi(forward, x, y); + } + + /** + * Processes a motion event releasing the finger off the screen and possibly + * initializing the navigation. + * @param allowNav {@code true} if release action is supposed to trigger navigation. + */ + public void release(boolean allowNav) { + if (mNavigationHandler != null) mNavigationHandler.release(allowNav); + } + + /** + * Resets a gesture as the result of the successful navigation or cancellation. + */ + public void reset() { + if (mNavigationHandler != null) mNavigationHandler.reset(); + } + + /** + * Signals a pull update. + * @param delta The change in horizontal pull distance (positive if toward right, + * negative if left). + */ + public void pull(float delta) { + if (mNavigationHandler != null) mNavigationHandler.pull(delta); + } + + /** * Destroy HistoryNavigationCoordinator object. */ public void destroy() { @@ -272,15 +377,9 @@ mInsetObserverView.removeObserver(this); mInsetObserverView = null; } - if (mCompositorViewHolder != null && mNavigationHandler != null) { - mCompositorViewHolder.removeTouchEventObserver(mNavigationHandler); - mCompositorViewHolder = null; - } - if (mNavigationLayout != null) { - mNavigationLayout.removeCallbacks(mUpdateNavigationStateRunnable); - mNavigationLayout.destroy(); - mNavigationLayout = null; - } + mCleanupRunnable.run(); + mNavigationLayout = null; + resetCompositorGlowEffect(); mDelegate = HistoryNavigationDelegate.DEFAULT; if (mNavigationHandler != null) { mNavigationHandler.setDelegate(mDelegate); @@ -293,8 +392,20 @@ } } + private void resetCompositorGlowEffect() { + if (mCompositorGlowEffect != null) { + mCompositorGlowEffect.destroy(); + mCompositorGlowEffect = null; + } + } + @VisibleForTesting NavigationHandler getNavigationHandlerForTesting() { return mNavigationHandler; } + + @VisibleForTesting + HistoryNavigationLayout getLayoutForTesting() { + return mNavigationLayout; + } }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/gesturenav/HistoryNavigationLayout.java b/chrome/android/java/src/org/chromium/chrome/browser/gesturenav/HistoryNavigationLayout.java index 3ea546a..6e0051ac 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/gesturenav/HistoryNavigationLayout.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/gesturenav/HistoryNavigationLayout.java
@@ -5,27 +5,49 @@ package org.chromium.chrome.browser.gesturenav; import android.content.Context; +import android.gesture.GesturePoint; import android.view.View; import android.view.ViewGroup; import android.widget.FrameLayout; +import androidx.annotation.VisibleForTesting; + +import org.chromium.base.Callback; import org.chromium.base.supplier.Supplier; -import org.chromium.content_public.browser.WebContents; +import org.chromium.chrome.browser.gesturenav.NavigationBubble.CloseTarget; /** * FrameLayout that supports side-wise slide gesture for history navigation. */ -public class HistoryNavigationLayout - extends FrameLayout implements ViewGroup.OnHierarchyChangeListener { - private final Supplier<Boolean> mIsNativePage; - private WebContents mWebContents; - private NavigationGlow mJavaGlowEffect; - private NavigationGlow mCompositorGlowEffect; +class HistoryNavigationLayout extends FrameLayout implements ViewGroup.OnHierarchyChangeListener { + // Supplies a {@link NavigationGlow} object for either native or rendered page. + private final Supplier<NavigationGlow> mGlowEffect; - public HistoryNavigationLayout(Context context, Supplier<Boolean> isNativePage) { + // Callback that performs navigation action in response to UI., + private final Callback<Boolean> mNavigateCallback; + + // Frame layout hosting the arrow puck UI. + private SideSlideLayout mSideSlideLayout; + + // Navigation sheet showing the navigation history. + private Supplier<NavigationSheet> mNavigationSheet; + + // Async runnable for ending the refresh animation after the page first + // loads a frame. This is used to provide a reasonable minimum animation time. + private Runnable mStopNavigatingRunnable; + + // Handles removing the layout from the view hierarchy. This is posted to ensure + // it does not conflict with pending Android draws. + private Runnable mDetachLayoutRunnable; + + public HistoryNavigationLayout(Context context, Supplier<NavigationGlow> glowEffect, + Supplier<NavigationSheet> navigationSheet, Callback<Boolean> navigateCallback) { super(context); - mIsNativePage = isNativePage; + mGlowEffect = glowEffect; + mNavigationSheet = navigationSheet; + mNavigateCallback = navigateCallback; setOnHierarchyChangeListener(this); + setVisibility(View.INVISIBLE); } @Override @@ -40,39 +62,174 @@ } /** - * Create {@link NavigationGlow} object lazily. + * Creates a view hosting the gesture navigation UI. + * @return The created view. */ - NavigationGlow getGlowEffect() { - if (mIsNativePage.get()) { - if (mJavaGlowEffect == null) mJavaGlowEffect = new AndroidUiNavigationGlow(this); - return mJavaGlowEffect; - } else { - // TODO(crbug.com/1102275): Investigate when this is called with nulled mWebContents. - if (mCompositorGlowEffect == null && mWebContents != null) { - mCompositorGlowEffect = new CompositorNavigationGlow(this, mWebContents); - } - return mCompositorGlowEffect; - } + private SideSlideLayout createLayout() { + mSideSlideLayout = new SideSlideLayout(getContext()); + mSideSlideLayout.setLayoutParams( + new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT)); + return mSideSlideLayout; } /** - * Reset CompositorGlowEffect for new a WebContents. Destroy the current one - * (for its native object) so it can be created again lazily. + * Start showing arrow widget for navigation back/forward. + * @param forward {@code true} for forward navigation, or {@code false} for back. + * @param closeIndicator */ - void resetCompositorGlow(WebContents webContents) { - if (mWebContents == webContents) return; - if (mCompositorGlowEffect != null) { - mCompositorGlowEffect.destroy(); - mCompositorGlowEffect = null; + void showBubble(boolean forward, @CloseTarget int closeIndicator) { + if (mSideSlideLayout == null) { + SideSlideLayout sideSlideLayout = createLayout(); + sideSlideLayout.setOnNavigationListener((direction) -> { + mNavigateCallback.onResult(direction); + cancelStopNavigatingRunnable(); + sideSlideLayout.post(getStopNavigatingRunnable()); + }); + sideSlideLayout.setOnResetListener(() -> { + if (getDetachLayoutRunnable() != null) return; + sideSlideLayout.post(createDetachLayoutRunnable()); + }); } - mWebContents = webContents; + mSideSlideLayout.setEnabled(true); + mSideSlideLayout.setDirection(forward); + mSideSlideLayout.setCloseIndicator(closeIndicator); + attachLayoutIfNecessary(); + mSideSlideLayout.start(); + mNavigationSheet.get().start(forward, closeIndicator != CloseTarget.NONE); } /** - * Performs cleanup upon destruction. + * Start showing edge glow effect. + * @param p Current position of the touch event. */ - void destroy() { - resetCompositorGlow(null); - mWebContents = null; + void showGlow(GesturePoint p) { + if (mGlowEffect.get() != null) mGlowEffect.get().prepare(p.x, p.y); + } + + /** + * Signals a pull update. + * @param offset The change in horizontal pull distance (positive if toward right, + * negative if left). + */ + void pullBubble(float offset) { + if (mSideSlideLayout == null) return; + mSideSlideLayout.pull(offset); + NavigationSheet navigationSheet = mNavigationSheet.get(); + navigationSheet.onScroll(offset - mSideSlideLayout.getPullOffset(), + mSideSlideLayout.getOverscroll(), mSideSlideLayout.willNavigate()); + + mSideSlideLayout.fadeBubble(!navigationSheet.isHidden(), /* animate= */ true); + if (navigationSheet.isExpanded()) mSideSlideLayout.hideBubble(); + } + + /** + * Signals a pull update for glow effect. + * @param offset The change in horizontal pull distance. + */ + void pullGlow(float offset) { + if (mGlowEffect.get() != null) mGlowEffect.get().onScroll(offset); + } + + /** + * Release the active pull. If no pull has started, the release will be ignored. + * If the pull was sufficiently large, the navigation sequence will be initiated. + * @param allowNav {@code true} if release action is supposed to trigger navigation. + */ + void releaseBubble(boolean allowNav) { + if (mSideSlideLayout == null) return; + cancelStopNavigatingRunnable(); + NavigationSheet navigationSheet = mNavigationSheet.get(); + mSideSlideLayout.release(allowNav && navigationSheet.isHidden()); + navigationSheet.release(); + } + + /** + * Release the glow effect. + */ + void releaseGlow() { + if (mGlowEffect.get() != null) mGlowEffect.get().release(); + } + + /** + * Reset navigation bubble UI in action. + */ + void resetBubble() { + if (mSideSlideLayout == null) return; + cancelStopNavigatingRunnable(); + mSideSlideLayout.reset(); + } + + /** + * Reset the glow effect. + */ + void resetGlow() { + if (mGlowEffect.get() != null) mGlowEffect.get().reset(); + } + + /** + * @return {@link SideSlideLayout} object. + */ + SideSlideLayout getSideSlideLayout() { + return mSideSlideLayout; + } + + /** + * Cancel navigation operation by removing the runnable in the queue. + */ + void cancelStopNavigatingRunnable() { + if (mStopNavigatingRunnable != null) { + mSideSlideLayout.removeCallbacks(mStopNavigatingRunnable); + mStopNavigatingRunnable = null; + } + } + + Runnable getDetachLayoutRunnable() { + return mDetachLayoutRunnable; + } + + Runnable createDetachLayoutRunnable() { + mDetachLayoutRunnable = () -> { + mDetachLayoutRunnable = null; + detachLayoutIfNecessary(); + }; + return mDetachLayoutRunnable; + } + + /** + * Cancel the operation detaching the layout from view hierarchy. + */ + void cancelDetachLayoutRunnable() { + if (mDetachLayoutRunnable != null) { + mSideSlideLayout.removeCallbacks(mDetachLayoutRunnable); + mDetachLayoutRunnable = null; + } + } + + Runnable getStopNavigatingRunnable() { + if (mStopNavigatingRunnable == null) { + mStopNavigatingRunnable = () -> mSideSlideLayout.stopNavigating(); + } + return mStopNavigatingRunnable; + } + + /** + * Attach {@link SideSlideLayout} to view hierarchy when UI is activated. + */ + private void attachLayoutIfNecessary() { + // The animation view is attached/detached on-demand to minimize overlap + // with composited SurfaceView content. + cancelDetachLayoutRunnable(); + if (isLayoutDetached()) addView(mSideSlideLayout); + } + + private void detachLayoutIfNecessary() { + if (isLayoutDetached()) return; + cancelDetachLayoutRunnable(); + removeView(mSideSlideLayout); + } + + @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE) + boolean isLayoutDetached() { + return mSideSlideLayout == null || mSideSlideLayout.getParent() == null; } }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/gesturenav/NavigationGlow.java b/chrome/android/java/src/org/chromium/chrome/browser/gesturenav/NavigationGlow.java index b538f15..6a57924 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/gesturenav/NavigationGlow.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/gesturenav/NavigationGlow.java
@@ -24,10 +24,15 @@ public abstract void prepare(float startX, float startY); /** - * Called when user scroll is performed. - * @param xDelta Amount of x scroll in pixel. + * @return The total amount of pull offset. */ - public abstract void onScroll(float xDelta); + public abstract float getPullOffset(); + + /** + * Called when user scroll is performed. + * @param offset Newly updated pull offset. + */ + public abstract void onScroll(float offset); /** * Releases the glow UI in action.
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/gesturenav/NavigationHandler.java b/chrome/android/java/src/org/chromium/chrome/browser/gesturenav/NavigationHandler.java index 893f464..8cd46d6 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/gesturenav/NavigationHandler.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/gesturenav/NavigationHandler.java
@@ -4,12 +4,20 @@ package org.chromium.chrome.browser.gesturenav; +import static org.chromium.chrome.browser.gesturenav.GestureNavigationProperties.ACTION; +import static org.chromium.chrome.browser.gesturenav.GestureNavigationProperties.ALLOW_NAV; +import static org.chromium.chrome.browser.gesturenav.GestureNavigationProperties.BUBBLE_OFFSET; +import static org.chromium.chrome.browser.gesturenav.GestureNavigationProperties.CLOSE_INDICATOR; +import static org.chromium.chrome.browser.gesturenav.GestureNavigationProperties.DIRECTION; +import static org.chromium.chrome.browser.gesturenav.GestureNavigationProperties.GESTURE_POS; +import static org.chromium.chrome.browser.gesturenav.GestureNavigationProperties.GLOW_OFFSET; + import android.content.Context; +import android.gesture.GesturePoint; import android.view.GestureDetector; import android.view.MotionEvent; import android.view.View; import android.view.ViewGroup; -import android.view.ViewGroup.LayoutParams; import androidx.annotation.IntDef; import androidx.annotation.VisibleForTesting; @@ -17,6 +25,7 @@ import org.chromium.base.supplier.Supplier; import org.chromium.chrome.browser.compositor.CompositorViewHolder.TouchEventObserver; import org.chromium.chrome.browser.gesturenav.NavigationBubble.CloseTarget; +import org.chromium.ui.modelutil.PropertyModel; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -24,7 +33,7 @@ /** * Handles history overscroll navigation controlling the underlying UI widget. */ -public class NavigationHandler implements TouchEventObserver { +class NavigationHandler implements TouchEventObserver { // Width of a rectangluar area in dp on the left/right edge used for navigation. // Swipe beginning from a point within these rects triggers the operation. @VisibleForTesting @@ -37,30 +46,36 @@ // |EDGE_WIDTH_DP| in physical pixel. private final float mEdgeWidthPx; - @IntDef({GestureState.NONE, GestureState.STARTED, GestureState.DRAGGED}) + @IntDef({GestureState.NONE, GestureState.STARTED, GestureState.DRAGGED, GestureState.GLOW}) @Retention(RetentionPolicy.SOURCE) - private @interface GestureState { + @interface GestureState { int NONE = 0; int STARTED = 1; int DRAGGED = 2; int GLOW = 3; } + @IntDef({GestureAction.SHOW_ARROW, GestureAction.SHOW_GLOW, GestureAction.RELEASE_BUBBLE, + GestureAction.RELEASE_GLOW, GestureAction.RESET_BUBBLE, GestureAction.RESET_GLOW}) + @Retention(RetentionPolicy.SOURCE) + @interface GestureAction { + int SHOW_ARROW = 1; + int SHOW_GLOW = 2; + int RELEASE_BUBBLE = 3; + int RELEASE_GLOW = 4; + int RESET_BUBBLE = 5; + int RESET_GLOW = 6; + } + private final ViewGroup mParentView; private final Context mContext; private HistoryNavigationDelegate mDelegate; private ActionDelegate mActionDelegate; - private Supplier<NavigationGlow> mGlowEffectSupplier; - - private @GestureState int mState = GestureState.NONE; - // Frame layout where the main logic turning the gesture into corresponding UI resides. private SideSlideLayout mSideSlideLayout; - private NavigationSheet mNavigationSheet; - // Async runnable for ending the refresh animation after the page first // loads a frame. This is used to provide a reasonable minimum animation time. private Runnable mStopNavigatingRunnable; @@ -71,6 +86,14 @@ private GestureDetector mDetector; private View.OnAttachStateChangeListener mAttachStateListener; private final Supplier<Boolean> mIsNativePage; + private final Supplier<Boolean> mIsSheetExpanded; + + private @GestureState int mState; + + private PropertyModel mModel; + + // Total horizontal pull offset for a swipe gesture. + private float mPullOffset; /** * Interface to perform actions for navigating. @@ -114,12 +137,14 @@ } } - public NavigationHandler(ViewGroup parentView, Supplier<NavigationGlow> glowEffect, - Supplier<Boolean> isNativePage) { + public NavigationHandler(PropertyModel model, ViewGroup parentView, + Supplier<Boolean> isNativePage, Supplier<Boolean> isSheetExpanded) { + mModel = model; mParentView = parentView; mContext = parentView.getContext(); - mGlowEffectSupplier = glowEffect; mIsNativePage = isNativePage; + mIsSheetExpanded = isSheetExpanded; + mState = GestureState.NONE; mEdgeWidthPx = EDGE_WIDTH_DP * parentView.getResources().getDisplayMetrics().density; mDetector = new GestureDetector(mContext, new SideNavGestureListener()); mAttachStateListener = new View.OnAttachStateChangeListener() { @@ -134,30 +159,6 @@ parentView.addOnAttachStateChangeListener(mAttachStateListener); } - private void createLayout() { - mSideSlideLayout = new SideSlideLayout(mContext); - mSideSlideLayout.setLayoutParams( - new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT)); - mSideSlideLayout.setOnNavigationListener((forward) -> { - mActionDelegate.navigate(forward); - cancelStopNavigatingRunnable(); - mSideSlideLayout.post(getStopNavigatingRunnable()); - }); - mSideSlideLayout.setOnResetListener(() -> { - if (mDetachLayoutRunnable != null) return; - mDetachLayoutRunnable = () -> { - mDetachLayoutRunnable = null; - detachLayoutIfNecessary(); - }; - mSideSlideLayout.post(mDetachLayoutRunnable); - }); - - mNavigationSheet = NavigationSheet.isEnabled() ? NavigationSheet.create( - mParentView, mContext, mDelegate.getBottomSheetController()) - : NavigationSheet.DUMMY; - mNavigationSheet.setDelegate(mDelegate.createSheetDelegate()); - } - /** * Sets {@link HistoryNavigationDelegate} object. * Also creates new delegates, for horizontal gesture and bottom sheet processing. @@ -166,7 +167,6 @@ void setDelegate(HistoryNavigationDelegate delegate) { mDelegate = delegate; mActionDelegate = delegate.createActionDelegate(); - if (mNavigationSheet != null) mNavigationSheet.setDelegate(delegate.createSheetDelegate()); } @Override @@ -181,14 +181,7 @@ public void handleTouchEvent(MotionEvent e) { if (!mIsNativePage.get()) return; mDetector.onTouchEvent(e); - if (e.getAction() == MotionEvent.ACTION_UP) { - if (mState == GestureState.DRAGGED && mSideSlideLayout != null) { - mSideSlideLayout.release(mNavigationSheet.isHidden()); - mNavigationSheet.release(); - } else if (mState == GestureState.GLOW && mGlowEffectSupplier.get() != null) { - mGlowEffectSupplier.get().release(); - } - } + if (e.getAction() == MotionEvent.ACTION_UP) release(true); } /** @@ -214,7 +207,7 @@ if (mState == GestureState.STARTED) { if (shouldTriggerUi(startX, distanceX, distanceY)) { - navigate(distanceX > 0, endX, endY); + triggerUi(distanceX > 0, endX, endY); } if (!isActive()) mState = GestureState.NONE; } @@ -228,58 +221,38 @@ } /** - * Shows UI in response to gesture events. - * @param forward Direction of the swipe gesture. {@code true} if forward; else back. - * @param x The X position of the event. - * @param y The Y position of the event. - * @return {@code true} if the navigation can be triggered. + * @see {@link HistoryNavigationCoordinator#triggerUi(boolean, float, float)} */ - public boolean navigate(boolean forward, float x, float y) { - assert mActionDelegate != null; + boolean triggerUi(boolean forward, float x, float y) { + mModel.set(DIRECTION, forward); boolean navigable = mActionDelegate.canNavigate(forward); if (navigable) { - showArrowWidget(forward); + if (mState != GestureState.STARTED) mModel.set(ACTION, GestureAction.RESET_BUBBLE); + mModel.set(CLOSE_INDICATOR, getCloseIndicator(forward)); + mModel.set(ACTION, GestureAction.SHOW_ARROW); + mState = GestureState.DRAGGED; } else { - showGlow(x, y); + if (mState != GestureState.STARTED) mModel.set(ACTION, GestureAction.RESET_GLOW); + mModel.set(GESTURE_POS, new GesturePoint(x, y, 0L)); + mModel.set(ACTION, GestureAction.SHOW_GLOW); + mState = GestureState.GLOW; } return navigable; } /** - * Start showing arrow widget for navigation back/forward. - * @param forward {@code true} if navigating forward. + * Perform navigation back or forward. + * @param forward {@code true} for forward navigation, or {@code false} for back. */ - private void showArrowWidget(boolean forward) { - if (mState != GestureState.STARTED) reset(); - if (mSideSlideLayout == null) createLayout(); - mSideSlideLayout.setEnabled(true); - mSideSlideLayout.setDirection(forward); - @CloseTarget - int closeIndicator = getCloseIndicator(forward); - mSideSlideLayout.setCloseIndicator(closeIndicator); - attachLayoutIfNecessary(); - mSideSlideLayout.start(); - mNavigationSheet.start(forward, closeIndicator != CloseTarget.NONE); - mState = GestureState.DRAGGED; + void navigate(boolean forward) { + mActionDelegate.navigate(forward); } /** - * Start showing edge glow effect. - * @param startX X coordinate of the touch event at the beginning. - * @param startY Y coordinate of the touch event at the beginning. + * @return The type of target to close when left swipe is performed. Could be + * the current tab, Chrome app, or none as defined in {@link CloseTarget}. + * @param forward {@code true} for forward navigation, or {@code false} for back. */ - private void showGlow(float startX, float startY) { - if (mState != GestureState.STARTED) reset(); - if (mGlowEffectSupplier.get() != null) mGlowEffectSupplier.get().prepare(startX, startY); - mState = GestureState.GLOW; - } - - private boolean shouldShowCloseIndicator(boolean forward) { - // Some tabs, upon back at the beginning of the history stack, should be just closed - // than closing the entire app. In such case we do not show the close indicator. - return !forward && mActionDelegate.willBackExitApp(); - } - private @CloseTarget int getCloseIndicator(boolean forward) { // Some tabs, upon back at the beginning of the history stack, should be just closed // than closing the entire app. @@ -293,24 +266,44 @@ } /** - * Signals a pull update. - * @param delta The change in horizontal pull distance (positive if toward right, - * negative if left). + * @see {@link HistoryNavigationCoordinator#release(boolean)} */ - public void pull(float delta) { - if (mState == GestureState.DRAGGED && mSideSlideLayout != null) { - mSideSlideLayout.pull(delta); - mNavigationSheet.onScroll( - delta, mSideSlideLayout.getOverscroll(), mSideSlideLayout.willNavigate()); - - mSideSlideLayout.fadeArrow(!mNavigationSheet.isHidden(), /* animate= */ true); - if (mNavigationSheet.isExpanded()) { - mSideSlideLayout.hideArrow(); - mState = GestureState.NONE; - } + void release(boolean allowNav) { + mModel.set(ALLOW_NAV, allowNav); + if (mState == GestureState.DRAGGED) { + mModel.set(ACTION, GestureAction.RELEASE_BUBBLE); } else if (mState == GestureState.GLOW) { - if (mGlowEffectSupplier.get() != null) mGlowEffectSupplier.get().onScroll(-delta); + mModel.set(ACTION, GestureAction.RELEASE_GLOW); } + mPullOffset = 0.f; + } + + /** + * @see {@link HistoryNavigationCoordinator#reset()} + */ + void reset() { + if (mState == GestureState.DRAGGED) { + mModel.set(ACTION, GestureAction.RESET_BUBBLE); + } else if (mState == GestureState.GLOW) { + mModel.set(ACTION, GestureAction.RESET_GLOW); + } + mState = GestureState.NONE; + mPullOffset = 0.f; + } + + /** + * @see {@link HistoryNavigationCoordinator#pull(float)} + */ + void pull(float delta) { + mPullOffset += delta; + if (mState == GestureState.DRAGGED) { + mModel.set(BUBBLE_OFFSET, mPullOffset); + } else if (mState == GestureState.GLOW) { + mModel.set(GLOW_OFFSET, mPullOffset); + } + + // Reset the state if navigation sheet gets expanded. + if (mState == GestureState.DRAGGED && mIsSheetExpanded.get()) mState = GestureState.NONE; } /** @@ -329,74 +322,6 @@ } /** - * Release the active pull. If no pull has started, the release will be ignored. - * If the pull was sufficiently large, the navigation sequence will be initiated. - * @param allowNav Whether to allow a sufficiently large pull to trigger - * the navigation action and animation sequence. - */ - public void release(boolean allowNav) { - if (mState == GestureState.DRAGGED && mSideSlideLayout != null) { - cancelStopNavigatingRunnable(); - mSideSlideLayout.release(allowNav && mNavigationSheet.isHidden()); - mNavigationSheet.release(); - } else if (mState == GestureState.GLOW) { - if (mGlowEffectSupplier.get() != null) mGlowEffectSupplier.get().release(); - } - } - - /** - * Reset navigation UI in action. - */ - public void reset() { - if (mState == GestureState.DRAGGED && mSideSlideLayout != null) { - cancelStopNavigatingRunnable(); - mSideSlideLayout.reset(); - } else if (mState == GestureState.GLOW) { - if (mGlowEffectSupplier.get() != null) mGlowEffectSupplier.get().reset(); - } - mState = GestureState.NONE; - } - - private void cancelStopNavigatingRunnable() { - if (mStopNavigatingRunnable != null) { - mSideSlideLayout.removeCallbacks(mStopNavigatingRunnable); - mStopNavigatingRunnable = null; - } - } - - private void cancelDetachLayoutRunnable() { - if (mDetachLayoutRunnable != null) { - mSideSlideLayout.removeCallbacks(mDetachLayoutRunnable); - mDetachLayoutRunnable = null; - } - } - - private Runnable getStopNavigatingRunnable() { - if (mStopNavigatingRunnable == null) { - mStopNavigatingRunnable = () -> mSideSlideLayout.stopNavigating(); - } - return mStopNavigatingRunnable; - } - - private void attachLayoutIfNecessary() { - // The animation view is attached/detached on-demand to minimize overlap - // with composited SurfaceView content. - cancelDetachLayoutRunnable(); - if (isLayoutDetached()) mParentView.addView(mSideSlideLayout); - } - - private void detachLayoutIfNecessary() { - if (mSideSlideLayout == null) return; - cancelDetachLayoutRunnable(); - if (!isLayoutDetached()) mParentView.removeView(mSideSlideLayout); - } - - @VisibleForTesting - boolean isLayoutDetached() { - return mSideSlideLayout == null || mSideSlideLayout.getParent() == null; - } - - /** * Performs cleanup upon destruction. */ void destroy() {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/gesturenav/SideSlideLayout.java b/chrome/android/java/src/org/chromium/chrome/browser/gesturenav/SideSlideLayout.java index c713a38..3e4cdc1 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/gesturenav/SideSlideLayout.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/gesturenav/SideSlideLayout.java
@@ -283,11 +283,19 @@ } /** + * @param Total amount of pull offset. + */ + float getPullOffset() { + return mTotalMotion; + } + + /** * Apply a pull impulse to the effect. If the effect is disabled or has yet * to start, the pull will be ignored. - * @param delta the magnitude of the pull. + * @param offset Updated total pull offset. */ - public void pull(float delta) { + public void pull(float offset) { + float delta = offset - mTotalMotion; if (!isEnabled() || !mIsBeingDragged) return; float maxDelta = mTotalDragDistance / MIN_PULLS_TO_ACTIVATE; @@ -337,14 +345,14 @@ * @param faded {@code true} if arrow bubble should fade out. * @param animate {@code true} if animation is needed. */ - void fadeArrow(boolean faded, boolean animate) { + void fadeBubble(boolean faded, boolean animate) { mArrowView.setFaded(faded, animate); } /** * Hide arrow bubble by making it fade away at the current position. */ - void hideArrow() { + void hideBubble() { mNavigating = false; startHidingAnimation(mNavigateListener); }
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/gesturenav/NavigationHandlerTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/gesturenav/NavigationHandlerTest.java index 8b9006d..fa26608e 100644 --- a/chrome/android/javatests/src/org/chromium/chrome/browser/gesturenav/NavigationHandlerTest.java +++ b/chrome/android/javatests/src/org/chromium/chrome/browser/gesturenav/NavigationHandlerTest.java
@@ -56,6 +56,7 @@ private static final int PAGELOAD_TIMEOUT_MS = 4000; private EmbeddedTestServer mTestServer; + private HistoryNavigationLayout mNavigationLayout; private NavigationHandler mNavigationHandler; private float mEdgeWidthPx; @@ -73,8 +74,10 @@ TabbedRootUiCoordinator uiCoordinator = (TabbedRootUiCoordinator) mActivityTestRule.getActivity() .getRootUiCoordinatorForTesting(); - mNavigationHandler = uiCoordinator.getHistoryNavigationCoordinatorForTesting() - .getNavigationHandlerForTesting(); + HistoryNavigationCoordinator coordinator = + uiCoordinator.getHistoryNavigationCoordinatorForTesting(); + mNavigationLayout = coordinator.getLayoutForTesting(); + mNavigationHandler = coordinator.getNavigationHandlerForTesting(); } @After @@ -139,7 +142,8 @@ public void testShortSwipeDoesNotTriggerNavigation() { mActivityTestRule.loadUrl(UrlConstants.NTP_URL); shortSwipeFromEdge(LEFT_EDGE); - CriteriaHelper.pollUiThread(mNavigationHandler::isLayoutDetached); + CriteriaHelper.pollUiThread(mNavigationLayout::isLayoutDetached, + "Navigation Layout should be detached after use"); Assert.assertEquals("Current page should not change", UrlConstants.NTP_URL, currentTab().getUrlString()); } @@ -162,8 +166,9 @@ mActivityTestRule.loadUrl(UrlConstants.NTP_URL); mActivityTestRule.loadUrl(UrlConstants.RECENT_TABS_URL); swipeFromEdge(LEFT_EDGE); - CriteriaHelper.pollUiThread(mNavigationHandler::isLayoutDetached, + CriteriaHelper.pollUiThread(mNavigationLayout::isLayoutDetached, "Navigation Layout should be detached after use"); + Assert.assertNull(mNavigationLayout.getDetachLayoutRunnable()); } @Test
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/signin/AccountPickerBottomSheetTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/signin/AccountPickerBottomSheetTest.java index 5c88cf11..3d13fe1 100644 --- a/chrome/android/javatests/src/org/chromium/chrome/browser/signin/AccountPickerBottomSheetTest.java +++ b/chrome/android/javatests/src/org/chromium/chrome/browser/signin/AccountPickerBottomSheetTest.java
@@ -57,8 +57,6 @@ /** * Tests account picker bottom sheet of the web signin flow. - * - * TODO(https://crbug.com/1090356): Add render tests for bottomsheet. */ @RunWith(ChromeJUnit4ClassRunner.class) @CommandLineFlags.Add({ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE})
diff --git a/chrome/android/modules/image_editor/image_editor_module.gni b/chrome/android/modules/image_editor/image_editor_module.gni index 1e265f5..f6db4b29 100644 --- a/chrome/android/modules/image_editor/image_editor_module.gni +++ b/chrome/android/modules/image_editor/image_editor_module.gni
@@ -2,7 +2,6 @@ # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. -import("//build/config/android/channel.gni") import("//build/config/android/rules.gni") _ink_dir = @@ -11,8 +10,7 @@ declare_args() { # Whether to enable the ImageEditor in Chrome. - enable_image_editor = - android_channel == "default" || android_channel == "canary" + enable_image_editor = false } image_editor_module_desc = {
diff --git a/chrome/app/os_settings_strings.grdp b/chrome/app/os_settings_strings.grdp index 42e4c7aa..7dc8fd4c 100644 --- a/chrome/app/os_settings_strings.grdp +++ b/chrome/app/os_settings_strings.grdp
@@ -463,21 +463,57 @@ <message name="IDS_OS_SETTINGS_AMBIENT_MODE_DISABLED" desc="Sub label for the ambient mode row in settings page when it is disabled."> Disabled </message> + <message name="IDS_OS_SETTINGS_AMBIENT_MODE_PAGE_DESCRIPTION" desc="Description for the ambient mode settings page."> + Personalize your idle display. Keep your screen on when device is charging. + </message> <message name="IDS_OS_SETTINGS_AMBIENT_MODE_ON" desc="Label for the toggle row in ambient mode settings page when it is enabled."> On </message> <message name="IDS_OS_SETTINGS_AMBIENT_MODE_OFF" desc="Label for the toggle row in ambient mode settings page when it is disabled."> Off </message> - <message name="IDS_OS_SETTINGS_AMBIENT_MODE_TOPIC_SOURCE_TITLE" desc="Label for the radio button group of topic source, where the screen contents come from."> - Choose what you see on your screen + <message name="IDS_OS_SETTINGS_AMBIENT_MODE_TOPIC_SOURCE_TITLE" desc="Label for the radio button group of ambient mode topic source, where the screen contents come from."> + Background </message> - <message name="IDS_OS_SETTINGS_AMBIENT_MODE_TOPIC_SOURCE_GOOGLE_PHOTOS" desc="Label for the radio button to choose the topic source from Google Photos."> + <message name="IDS_OS_SETTINGS_AMBIENT_MODE_TOPIC_SOURCE_GOOGLE_PHOTOS" desc="Label for the radio button to choose the topic source from Google Photos. The radio button allows the user to set Google Photos for ambient mode, which shows photos to the user when their device is idle."> Google Photos </message> - <message name="IDS_OS_SETTINGS_AMBIENT_MODE_TOPIC_SOURCE_ART_GALLERY" desc="Label for the radio button to choose the topic source from art gallery."> + <message name="IDS_OS_SETTINGS_AMBIENT_MODE_TOPIC_SOURCE_GOOGLE_PHOTOS_DESCRIPTION" desc="Description for the radio button to choose the topic source from Google Photos. The radio button allows the user to set Google Photos for ambient mode, which shows photos to the user when their device is idle."> + Select your memories + </message> + <message name="IDS_OS_SETTINGS_AMBIENT_MODE_TOPIC_SOURCE_GOOGLE_PHOTOS_DESCRIPTION_NO_ALBUM" desc="Description for the radio button to choose the topic source from Google Photos when there is no album. The radio button allows the user to set Google Photos for ambient mode, which shows photos to the user when their device is idle."> + To create albums, go to Google Photos + </message> + <message name="IDS_OS_SETTINGS_AMBIENT_MODE_TOPIC_SOURCE_ART_GALLERY" desc="Label for the radio button to choose the topic source from art gallery. The radio button allows the user to set Art gallery for ambient mode, which shows photos to the user when their device is idle."> Art gallery </message> + <message name="IDS_OS_SETTINGS_AMBIENT_MODE_TOPIC_SOURCE_ART_GALLERY_DESCRIPTION" desc="Description for the radio button to choose the topic source from art gallery. The radio button allows the user to set Art gallery for ambient mode, which shows photos to the user when their device is idle."> + Curated images and artwork + </message> + <message name="IDS_OS_SETTINGS_AMBIENT_MODE_TOPIC_SOURCE_SELECTED_ROW" desc="A11y label for the selected topic source row in ambient mode."> + <ph name="TOPIC_SOURCE">$1<ex>Google Photos</ex></ph> selected, press Enter to select <ph name="TOPIC_SOURCE">$2<ex>Google Photos</ex></ph> albums + </message> + <message name="IDS_OS_SETTINGS_AMBIENT_MODE_TOPIC_SOURCE_UNSELECTED_ROW" desc="A11y label for the unselected topic source row in ambient mode."> + Select <ph name="TOPIC_SOURCE">$1<ex>Google Photos</ex></ph> + </message> + <message name="IDS_OS_SETTINGS_AMBIENT_MODE_TOPIC_SOURCE_SUBPAGE" desc="A11y label for the subpage button to select albums in ambient mode."> + Select <ph name="TOPIC_SOURCE">$1<ex>Google Photos</ex></ph> albums + </message> + <message name="IDS_OS_SETTINGS_AMBIENT_MODE_ALBUMS_SUBPAGE_GOOGLE_PHOTOS_TITLE" desc="Label for the subpage to select albums of Google Photos in ambient mode."> + A slideshow of selected memories will be created. To make any changes to the existing albums, go to<ph name="LINK_BEGIN"><a target="_blank" href="$1<ex>https://google.com/</ex>"></ph>Google Photos<ph name="LINK_END"></a></ph>. + </message> + <message name="IDS_OS_SETTINGS_AMBIENT_MODE_ALBUMS_SUBPAGE_GOOGLE_PHOTOS_NO_ALBUM" desc="Text for the subpage to select albums of Google Photos when there is no album in ambient mode."> + No albums set up yet + </message> + <message name="IDS_OS_SETTINGS_AMBIENT_MODE_ALBUMS_SUBPAGE_RECENT_DESC" desc="Description for the Recent highlights album in ambient mode."> + Your best auto selected photos + </message> + <message name="IDS_OS_SETTINGS_AMBIENT_MODE_ALBUMS_SUBPAGE_PHOTOS_NUM" desc="Number of photos in an album in ambient mode."> + <ph name="NUMBER">$1<ex>100</ex></ph> photos + </message> + <message name="IDS_OS_SETTINGS_AMBIENT_MODE_ALBUMS_SUBPAGE_PHOTOS_NUM_ONE" desc="1 photo in an album in ambient mode."> + 1 photo + </message> <!-- Search and Assistant section. --> <message name="IDS_OS_SETTINGS_SEARCH_ENGINE_LABEL" desc="Label in OS settings describing search engine behavior.">
diff --git a/chrome/app/os_settings_strings_grdp/IDS_OS_SETTINGS_AMBIENT_MODE_ALBUMS_SUBPAGE_GOOGLE_PHOTOS_NO_ALBUM.png.sha1 b/chrome/app/os_settings_strings_grdp/IDS_OS_SETTINGS_AMBIENT_MODE_ALBUMS_SUBPAGE_GOOGLE_PHOTOS_NO_ALBUM.png.sha1 new file mode 100644 index 0000000..8febe91b --- /dev/null +++ b/chrome/app/os_settings_strings_grdp/IDS_OS_SETTINGS_AMBIENT_MODE_ALBUMS_SUBPAGE_GOOGLE_PHOTOS_NO_ALBUM.png.sha1
@@ -0,0 +1 @@ +bf8cd7aff9ee521e28f3b73978c03a2bfc21acee \ No newline at end of file
diff --git a/chrome/app/os_settings_strings_grdp/IDS_OS_SETTINGS_AMBIENT_MODE_ALBUMS_SUBPAGE_GOOGLE_PHOTOS_TITLE.png.sha1 b/chrome/app/os_settings_strings_grdp/IDS_OS_SETTINGS_AMBIENT_MODE_ALBUMS_SUBPAGE_GOOGLE_PHOTOS_TITLE.png.sha1 new file mode 100644 index 0000000..c2e4515c --- /dev/null +++ b/chrome/app/os_settings_strings_grdp/IDS_OS_SETTINGS_AMBIENT_MODE_ALBUMS_SUBPAGE_GOOGLE_PHOTOS_TITLE.png.sha1
@@ -0,0 +1 @@ +ac082b74674f163872219d5f64954a4576852321 \ No newline at end of file
diff --git a/chrome/app/os_settings_strings_grdp/IDS_OS_SETTINGS_AMBIENT_MODE_ALBUMS_SUBPAGE_PHOTOS_NUM.png.sha1 b/chrome/app/os_settings_strings_grdp/IDS_OS_SETTINGS_AMBIENT_MODE_ALBUMS_SUBPAGE_PHOTOS_NUM.png.sha1 new file mode 100644 index 0000000..a1350c1 --- /dev/null +++ b/chrome/app/os_settings_strings_grdp/IDS_OS_SETTINGS_AMBIENT_MODE_ALBUMS_SUBPAGE_PHOTOS_NUM.png.sha1
@@ -0,0 +1 @@ +47bd082ca731bd2b7f80d31ca565c286cc72f908 \ No newline at end of file
diff --git a/chrome/app/os_settings_strings_grdp/IDS_OS_SETTINGS_AMBIENT_MODE_ALBUMS_SUBPAGE_PHOTOS_NUM_ONE.png.sha1 b/chrome/app/os_settings_strings_grdp/IDS_OS_SETTINGS_AMBIENT_MODE_ALBUMS_SUBPAGE_PHOTOS_NUM_ONE.png.sha1 new file mode 100644 index 0000000..a1350c1 --- /dev/null +++ b/chrome/app/os_settings_strings_grdp/IDS_OS_SETTINGS_AMBIENT_MODE_ALBUMS_SUBPAGE_PHOTOS_NUM_ONE.png.sha1
@@ -0,0 +1 @@ +47bd082ca731bd2b7f80d31ca565c286cc72f908 \ No newline at end of file
diff --git a/chrome/app/os_settings_strings_grdp/IDS_OS_SETTINGS_AMBIENT_MODE_ALBUMS_SUBPAGE_RECENT_DESC.png.sha1 b/chrome/app/os_settings_strings_grdp/IDS_OS_SETTINGS_AMBIENT_MODE_ALBUMS_SUBPAGE_RECENT_DESC.png.sha1 new file mode 100644 index 0000000..a1350c1 --- /dev/null +++ b/chrome/app/os_settings_strings_grdp/IDS_OS_SETTINGS_AMBIENT_MODE_ALBUMS_SUBPAGE_RECENT_DESC.png.sha1
@@ -0,0 +1 @@ +47bd082ca731bd2b7f80d31ca565c286cc72f908 \ No newline at end of file
diff --git a/chrome/app/os_settings_strings_grdp/IDS_OS_SETTINGS_AMBIENT_MODE_PAGE_DESCRIPTION.png.sha1 b/chrome/app/os_settings_strings_grdp/IDS_OS_SETTINGS_AMBIENT_MODE_PAGE_DESCRIPTION.png.sha1 new file mode 100644 index 0000000..3c4fcc9 --- /dev/null +++ b/chrome/app/os_settings_strings_grdp/IDS_OS_SETTINGS_AMBIENT_MODE_PAGE_DESCRIPTION.png.sha1
@@ -0,0 +1 @@ +c8a3be00b8006564c838bae42958dcef728e44c7 \ No newline at end of file
diff --git a/chrome/app/os_settings_strings_grdp/IDS_OS_SETTINGS_AMBIENT_MODE_TOPIC_SOURCE_ART_GALLERY.png.sha1 b/chrome/app/os_settings_strings_grdp/IDS_OS_SETTINGS_AMBIENT_MODE_TOPIC_SOURCE_ART_GALLERY.png.sha1 index 7bd499ee..3c4fcc9 100644 --- a/chrome/app/os_settings_strings_grdp/IDS_OS_SETTINGS_AMBIENT_MODE_TOPIC_SOURCE_ART_GALLERY.png.sha1 +++ b/chrome/app/os_settings_strings_grdp/IDS_OS_SETTINGS_AMBIENT_MODE_TOPIC_SOURCE_ART_GALLERY.png.sha1
@@ -1 +1 @@ -14f5b65f17900e406f6bc5e73ae278ddb980693d \ No newline at end of file +c8a3be00b8006564c838bae42958dcef728e44c7 \ No newline at end of file
diff --git a/chrome/app/os_settings_strings_grdp/IDS_OS_SETTINGS_AMBIENT_MODE_TOPIC_SOURCE_ART_GALLERY_DESCRIPTION.png.sha1 b/chrome/app/os_settings_strings_grdp/IDS_OS_SETTINGS_AMBIENT_MODE_TOPIC_SOURCE_ART_GALLERY_DESCRIPTION.png.sha1 new file mode 100644 index 0000000..3c4fcc9 --- /dev/null +++ b/chrome/app/os_settings_strings_grdp/IDS_OS_SETTINGS_AMBIENT_MODE_TOPIC_SOURCE_ART_GALLERY_DESCRIPTION.png.sha1
@@ -0,0 +1 @@ +c8a3be00b8006564c838bae42958dcef728e44c7 \ No newline at end of file
diff --git a/chrome/app/os_settings_strings_grdp/IDS_OS_SETTINGS_AMBIENT_MODE_TOPIC_SOURCE_GOOGLE_PHOTOS.png.sha1 b/chrome/app/os_settings_strings_grdp/IDS_OS_SETTINGS_AMBIENT_MODE_TOPIC_SOURCE_GOOGLE_PHOTOS.png.sha1 index f5956ad..3c4fcc9 100644 --- a/chrome/app/os_settings_strings_grdp/IDS_OS_SETTINGS_AMBIENT_MODE_TOPIC_SOURCE_GOOGLE_PHOTOS.png.sha1 +++ b/chrome/app/os_settings_strings_grdp/IDS_OS_SETTINGS_AMBIENT_MODE_TOPIC_SOURCE_GOOGLE_PHOTOS.png.sha1
@@ -1 +1 @@ -24473868c185ef36c5f8975a29709c25844cd6db \ No newline at end of file +c8a3be00b8006564c838bae42958dcef728e44c7 \ No newline at end of file
diff --git a/chrome/app/os_settings_strings_grdp/IDS_OS_SETTINGS_AMBIENT_MODE_TOPIC_SOURCE_GOOGLE_PHOTOS_DESCRIPTION.png.sha1 b/chrome/app/os_settings_strings_grdp/IDS_OS_SETTINGS_AMBIENT_MODE_TOPIC_SOURCE_GOOGLE_PHOTOS_DESCRIPTION.png.sha1 new file mode 100644 index 0000000..3c4fcc9 --- /dev/null +++ b/chrome/app/os_settings_strings_grdp/IDS_OS_SETTINGS_AMBIENT_MODE_TOPIC_SOURCE_GOOGLE_PHOTOS_DESCRIPTION.png.sha1
@@ -0,0 +1 @@ +c8a3be00b8006564c838bae42958dcef728e44c7 \ No newline at end of file
diff --git a/chrome/app/os_settings_strings_grdp/IDS_OS_SETTINGS_AMBIENT_MODE_TOPIC_SOURCE_GOOGLE_PHOTOS_DESCRIPTION_NO_ALBUM.png.sha1 b/chrome/app/os_settings_strings_grdp/IDS_OS_SETTINGS_AMBIENT_MODE_TOPIC_SOURCE_GOOGLE_PHOTOS_DESCRIPTION_NO_ALBUM.png.sha1 new file mode 100644 index 0000000..b7dc0f2 --- /dev/null +++ b/chrome/app/os_settings_strings_grdp/IDS_OS_SETTINGS_AMBIENT_MODE_TOPIC_SOURCE_GOOGLE_PHOTOS_DESCRIPTION_NO_ALBUM.png.sha1
@@ -0,0 +1 @@ +a842340944fec566639f2ac7bb86804d5a37b281 \ No newline at end of file
diff --git a/chrome/app/os_settings_strings_grdp/IDS_OS_SETTINGS_AMBIENT_MODE_TOPIC_SOURCE_SELECTED_ROW.png.sha1 b/chrome/app/os_settings_strings_grdp/IDS_OS_SETTINGS_AMBIENT_MODE_TOPIC_SOURCE_SELECTED_ROW.png.sha1 new file mode 100644 index 0000000..a59d717 --- /dev/null +++ b/chrome/app/os_settings_strings_grdp/IDS_OS_SETTINGS_AMBIENT_MODE_TOPIC_SOURCE_SELECTED_ROW.png.sha1
@@ -0,0 +1 @@ +2d554d54cf1ff2fb5c7cd5f55c44d141031c0305 \ No newline at end of file
diff --git a/chrome/app/os_settings_strings_grdp/IDS_OS_SETTINGS_AMBIENT_MODE_TOPIC_SOURCE_SUBPAGE.png.sha1 b/chrome/app/os_settings_strings_grdp/IDS_OS_SETTINGS_AMBIENT_MODE_TOPIC_SOURCE_SUBPAGE.png.sha1 new file mode 100644 index 0000000..ddf9c033 --- /dev/null +++ b/chrome/app/os_settings_strings_grdp/IDS_OS_SETTINGS_AMBIENT_MODE_TOPIC_SOURCE_SUBPAGE.png.sha1
@@ -0,0 +1 @@ +0bfa5402b92d5e050789741b121c906fe8141f05 \ No newline at end of file
diff --git a/chrome/app/os_settings_strings_grdp/IDS_OS_SETTINGS_AMBIENT_MODE_TOPIC_SOURCE_TITLE.png.sha1 b/chrome/app/os_settings_strings_grdp/IDS_OS_SETTINGS_AMBIENT_MODE_TOPIC_SOURCE_TITLE.png.sha1 index a36d11b..3c4fcc9 100644 --- a/chrome/app/os_settings_strings_grdp/IDS_OS_SETTINGS_AMBIENT_MODE_TOPIC_SOURCE_TITLE.png.sha1 +++ b/chrome/app/os_settings_strings_grdp/IDS_OS_SETTINGS_AMBIENT_MODE_TOPIC_SOURCE_TITLE.png.sha1
@@ -1 +1 @@ -ed13f271527f62a8b0bad446be8d7140e02a5351 \ No newline at end of file +c8a3be00b8006564c838bae42958dcef728e44c7 \ No newline at end of file
diff --git a/chrome/app/os_settings_strings_grdp/IDS_OS_SETTINGS_AMBIENT_MODE_TOPIC_SOURCE_UNSELECTED_ROW.png.sha1 b/chrome/app/os_settings_strings_grdp/IDS_OS_SETTINGS_AMBIENT_MODE_TOPIC_SOURCE_UNSELECTED_ROW.png.sha1 new file mode 100644 index 0000000..168bb00 --- /dev/null +++ b/chrome/app/os_settings_strings_grdp/IDS_OS_SETTINGS_AMBIENT_MODE_TOPIC_SOURCE_UNSELECTED_ROW.png.sha1
@@ -0,0 +1 @@ +ef34bdfe0522a6902896705320e3340efb5adf8c \ No newline at end of file
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn index 84f0a6b..19f1c708 100644 --- a/chrome/browser/BUILD.gn +++ b/chrome/browser/BUILD.gn
@@ -2711,6 +2711,8 @@ "enterprise/util/android_enterprise_info.h", "file_select_helper_contacts_android.cc", "file_select_helper_contacts_android.h", + "first_run/android/first_run_prefs.cc", + "first_run/android/first_run_prefs.h", "first_run/android/first_run_utils.cc", "flags/android/chrome_feature_list.cc", "flags/android/chrome_feature_list.h",
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc index 6473cbb2..7cd9d1f 100644 --- a/chrome/browser/about_flags.cc +++ b/chrome/browser/about_flags.cc
@@ -2858,6 +2858,9 @@ flag_descriptions::kCrostiniShowMicSettingName, flag_descriptions::kCrostiniShowMicSettingDescription, kOsCrOS, FEATURE_VALUE_TYPE(chromeos::features::kCrostiniShowMicSetting)}, + {"crostini-use-dlc", flag_descriptions::kCrostiniUseDlcName, + flag_descriptions::kCrostiniUseDlcDescription, kOsCrOS, + FEATURE_VALUE_TYPE(chromeos::features::kCrostiniUseDlc)}, {"terminal-system-app-legacy-settings", flag_descriptions::kTerminalSystemAppLegacySettingsName, flag_descriptions::kTerminalSystemAppLegacySettingsDescription, kOsCrOS,
diff --git a/chrome/browser/apps/app_service/app_icon_factory.cc b/chrome/browser/apps/app_service/app_icon_factory.cc index 9195002..1145b59 100644 --- a/chrome/browser/apps/app_service/app_icon_factory.cc +++ b/chrome/browser/apps/app_service/app_icon_factory.cc
@@ -53,7 +53,9 @@ #if defined(OS_CHROMEOS) #include "chrome/browser/chromeos/arc/icon_decode_request.h" +#include "chrome/browser/ui/app_list/icon_standardizer.h" #include "chrome/browser/ui/app_list/md_icon_normalizer.h" +#include "chrome/grit/chrome_unscaled_resources.h" #include "ui/chromeos/resources/grit/ui_chromeos_resources.h" #endif @@ -515,6 +517,20 @@ void IconLoadingPipeline::LoadIconFromResource(int icon_resource) { DCHECK_CURRENTLY_ON(content::BrowserThread::UI); + +#if defined(OS_CHROMEOS) + if (icon_resource == IDR_LOGO_CROSTINI_DEFAULT_192 || + icon_resource == IDR_APP_DEFAULT_ICON) { + // For the Crostini penguin icon, clear the standard icon effects, and use + // the raw icon. + // + // For the default icon, use the raw icon, because the standard icon image + // convert could break the test cases. + icon_effects_ = static_cast<apps::IconEffects>( + icon_effects_ & ~apps::IconEffects::kCrOsStandardIcon); + } +#endif + if (icon_resource == kInvalidIconResource) { MaybeLoadFallbackOrCompleteEmpty(); return; @@ -695,11 +711,6 @@ } gfx::ImageSkia processed_image = image; - // TODO(crbug.com/1083331): - // 1. For kStandard icons, shrink and apply the mask. - // 2. For the default apps, use the raw image, and don't shrink and apply the - // mask. - // Apply the icon effects on the uncompressed data. If the caller requests // an uncompressed icon, return the uncompressed result; otherwise, encode // the icon to a compressed icon, return the compressed result. @@ -943,6 +954,10 @@ *image_skia = gfx::ImageSkiaOperations::CreateMaskedImage( *image_skia, LoadMaskImage(image_skia->size())); } + + if (icon_effects & IconEffects::kCrOsStandardIcon) { + *image_skia = app_list::CreateStandardIconImage(*image_skia); + } #endif const bool from_bookmark = icon_effects & IconEffects::kRoundCorners;
diff --git a/chrome/browser/apps/app_service/app_icon_factory.h b/chrome/browser/apps/app_service/app_icon_factory.h index 73913ebb..1238d4ce 100644 --- a/chrome/browser/apps/app_service/app_icon_factory.h +++ b/chrome/browser/apps/app_service/app_icon_factory.h
@@ -46,8 +46,12 @@ // have not been launched locally yet. They // should appear gray until they are launched. kCrOsStandardBackground = - 0x40, // Add the white background to the standard icon. - kCrOsStandardMask = 0x80, // Apply the mask to the standard icon. + 0x40, // Add the white background to the standard icon. + kCrOsStandardMask = 0x80, // Apply the mask to the standard icon. + kCrOsStandardIcon = 0x100, // Add the white background, maybe shrink the + // icon, and apply the mask to the standard icon + // This effect combines kCrOsStandardBackground + // and kCrOsStandardMask together. }; // Returns a callback that converts compressed data to an ImageSkia.
diff --git a/chrome/browser/apps/app_service/crostini_apps.cc b/chrome/browser/apps/app_service/crostini_apps.cc index 555d7b1a..e7dd29ae 100644 --- a/chrome/browser/apps/app_service/crostini_apps.cc +++ b/chrome/browser/apps/app_service/crostini_apps.cc
@@ -16,6 +16,7 @@ #include "chrome/browser/chromeos/crostini/crostini_util.h" #include "chrome/browser/chromeos/guest_os/guest_os_registry_service_factory.h" #include "chrome/browser/profiles/profile.h" +#include "chrome/common/chrome_features.h" #include "chrome/grit/chrome_unscaled_resources.h" #include "chrome/grit/generated_resources.h" #include "components/prefs/pref_change_registrar.h" @@ -363,7 +364,11 @@ IDR_LOGO_CROSTINI_TERMINAL, apps::IconEffects::kNone); } - return icon_key_factory_.MakeIconKey(apps::IconEffects::kNone); + auto icon_effects = + base::FeatureList::IsEnabled(features::kAppServiceAdaptiveIcon) + ? IconEffects::kCrOsStandardIcon + : IconEffects::kNone; + return icon_key_factory_.MakeIconKey(icon_effects); } void CrostiniApps::PublishAppID(const std::string& app_id,
diff --git a/chrome/browser/apps/app_service/extension_apps_chromeos.cc b/chrome/browser/apps/app_service/extension_apps_chromeos.cc index 3d57b3b..1cf0d32 100644 --- a/chrome/browser/apps/app_service/extension_apps_chromeos.cc +++ b/chrome/browser/apps/app_service/extension_apps_chromeos.cc
@@ -29,6 +29,7 @@ #include "chrome/browser/chromeos/arc/arc_web_contents_data.h" #include "chrome/browser/chromeos/child_accounts/time_limits/app_time_limit_interface.h" #include "chrome/browser/chromeos/crostini/crostini_util.h" +#include "chrome/browser/chromeos/extensions/default_app_order.h" #include "chrome/browser/chromeos/extensions/gfx_utils.h" #include "chrome/browser/chromeos/policy/system_features_disable_list_policy_handler.h" #include "chrome/browser/extensions/extension_service.h" @@ -183,6 +184,8 @@ GetWeakPtr())); OnSystemFeaturesPrefChanged(); } + + chromeos::default_app_order::Get(&default_app_ids_); } void ExtensionAppsChromeOs::LaunchAppWithIntent( @@ -656,8 +659,14 @@ const extensions::Extension* extension, bool paused) { IconEffects icon_effects = IconEffects::kNone; - icon_effects = - static_cast<IconEffects>(icon_effects | IconEffects::kResizeAndPad); + if (base::FeatureList::IsEnabled(features::kAppServiceAdaptiveIcon) && + !base::Contains(default_app_ids_, (extension->id()))) { + icon_effects = + static_cast<IconEffects>(icon_effects | IconEffects::kCrOsStandardIcon); + } else { + icon_effects = + static_cast<IconEffects>(icon_effects | IconEffects::kResizeAndPad); + } if (extensions::util::ShouldApplyChromeBadge(profile(), extension->id())) { icon_effects = static_cast<IconEffects>(icon_effects | IconEffects::kChromeBadge);
diff --git a/chrome/browser/apps/app_service/extension_apps_chromeos.h b/chrome/browser/apps/app_service/extension_apps_chromeos.h index 2307f8f5..116e8eb 100644 --- a/chrome/browser/apps/app_service/extension_apps_chromeos.h +++ b/chrome/browser/apps/app_service/extension_apps_chromeos.h
@@ -158,6 +158,8 @@ extensions::AppWindowRegistry::Observer> app_window_registry_{this}; + std::vector<std::string> default_app_ids_; + PausedApps paused_apps_; std::set<std::string> disabled_apps_;
diff --git a/chrome/browser/apps/app_service/remote_apps.cc b/chrome/browser/apps/app_service/remote_apps.cc index df77fd59..17b3e83 100644 --- a/chrome/browser/apps/app_service/remote_apps.cc +++ b/chrome/browser/apps/app_service/remote_apps.cc
@@ -93,10 +93,12 @@ gfx::ImageSkia icon_image = delegate_->GetIcon(app_id); if (!icon_image.isNull()) { - icon->icon_type = mojom::IconType::kUncompressed; + icon->icon_type = icon_type; + IconEffects icon_effects = (icon_type == mojom::IconType::kStandard) + ? IconEffects::kCrOsStandardIcon + : IconEffects::kResizeAndPad; icon->uncompressed = icon_image; - apps::ApplyIconEffects(apps::IconEffects::kResizeAndPad, size_hint_in_dip, - &icon->uncompressed); + apps::ApplyIconEffects(icon_effects, size_hint_in_dip, &icon->uncompressed); } std::move(callback).Run(std::move(icon));
diff --git a/chrome/browser/apps/app_service/web_apps_chromeos.cc b/chrome/browser/apps/app_service/web_apps_chromeos.cc index 31ace024..44921e6 100644 --- a/chrome/browser/apps/app_service/web_apps_chromeos.cc +++ b/chrome/browser/apps/app_service/web_apps_chromeos.cc
@@ -24,6 +24,7 @@ #include "chrome/browser/chromeos/arc/arc_util.h" #include "chrome/browser/chromeos/arc/arc_web_contents_data.h" #include "chrome/browser/chromeos/crostini/crostini_util.h" +#include "chrome/browser/chromeos/extensions/default_app_order.h" #include "chrome/browser/chromeos/extensions/gfx_utils.h" #include "chrome/browser/notifications/notification_display_service_factory.h" #include "chrome/browser/profiles/profile.h" @@ -105,6 +106,8 @@ notification_display_service_.Add( NotificationDisplayServiceFactory::GetForProfile(profile())); + + chromeos::default_app_order::Get(&default_app_ids_); } void WebAppsChromeOs::LaunchAppWithIntent( @@ -384,12 +387,13 @@ bool paused, bool is_disabled) { IconEffects icon_effects = IconEffects::kNone; - if (base::FeatureList::IsEnabled(features::kAppServiceAdaptiveIcon)) { + if (base::FeatureList::IsEnabled(features::kAppServiceAdaptiveIcon) && + !base::Contains(default_app_ids_, (web_app->app_id()))) { icon_effects = web_app->is_generated_icon() ? static_cast<IconEffects>( icon_effects | IconEffects::kCrOsStandardMask) - : static_cast<IconEffects>(icon_effects | - IconEffects::kResizeAndPad); + : static_cast<IconEffects>( + icon_effects | IconEffects::kCrOsStandardIcon); // TODO(crbug.com/1083331): If the icon is maskable, modify the icon effect, // don't apply the kResizeAndPad effect to shrink the icon, add the
diff --git a/chrome/browser/apps/app_service/web_apps_chromeos.h b/chrome/browser/apps/app_service/web_apps_chromeos.h index 06ddcde..62c6178 100644 --- a/chrome/browser/apps/app_service/web_apps_chromeos.h +++ b/chrome/browser/apps/app_service/web_apps_chromeos.h
@@ -115,6 +115,8 @@ apps::InstanceRegistry* instance_registry_; + std::vector<std::string> default_app_ids_; + PausedApps paused_apps_; ArcAppListPrefs* arc_prefs_ = nullptr;
diff --git a/chrome/browser/chrome_browser_interface_binders.cc b/chrome/browser/chrome_browser_interface_binders.cc index e339ba9..db133346 100644 --- a/chrome/browser/chrome_browser_interface_binders.cc +++ b/chrome/browser/chrome_browser_interface_binders.cc
@@ -152,6 +152,8 @@ #include "chrome/browser/ui/webui/settings/chromeos/os_settings_ui.h" #include "chrome/browser/ui/webui/settings/chromeos/search/search.mojom.h" #include "chrome/browser/ui/webui/settings/chromeos/search/user_action_recorder.mojom.h" +#include "chromeos/components/camera_app_ui/camera_app_helper.mojom.h" +#include "chromeos/components/camera_app_ui/camera_app_ui.h" #include "chromeos/components/help_app_ui/help_app_ui.h" #include "chromeos/components/help_app_ui/help_app_ui.mojom.h" #include "chromeos/components/media_app_ui/media_app_ui.h" @@ -165,6 +167,7 @@ #include "chromeos/services/network_config/public/mojom/cros_network_config.mojom.h" // nogncheck #include "chromeos/services/network_health/public/mojom/network_diagnostics.mojom.h" #include "chromeos/services/network_health/public/mojom/network_health.mojom.h" +#include "media/capture/video/chromeos/mojom/camera_app.mojom.h" #endif #if defined(OS_CHROMEOS) && !defined(OFFICIAL_BUILD) @@ -573,6 +576,12 @@ chromeos::printing::printing_manager::mojom::PrintingMetadataProvider, chromeos::printing::printing_manager::PrintManagementUI>(map); + RegisterWebUIControllerInterfaceBinder<cros::mojom::CameraAppDeviceProvider, + chromeos::CameraAppUI>(map); + + RegisterWebUIControllerInterfaceBinder< + chromeos_camera::mojom::CameraAppHelper, chromeos::CameraAppUI>(map); + RegisterWebUIControllerInterfaceBinder<help_app_ui::mojom::PageHandlerFactory, chromeos::HelpAppUI>(map);
diff --git a/chrome/browser/chromeos/file_manager/file_manager_browsertest.cc b/chrome/browser/chromeos/file_manager/file_manager_browsertest.cc index 561e51d..e4be506 100644 --- a/chrome/browser/chromeos/file_manager/file_manager_browsertest.cc +++ b/chrome/browser/chromeos/file_manager/file_manager_browsertest.cc
@@ -695,7 +695,8 @@ TestCase("defaultTaskDialogDrive"), TestCase("changeDefaultDialogScrollList"), TestCase("genericTaskIsNotExecuted"), - TestCase("genericTaskAndNonGenericTask"))); + TestCase("genericTaskAndNonGenericTask"), + TestCase("noActionBarOpenForDirectories"))); WRAPPED_INSTANTIATE_TEST_SUITE_P( FolderShortcuts, /* folder_shortcuts.js */
diff --git a/chrome/browser/chromeos/input_method/personal_info_suggester.cc b/chrome/browser/chromeos/input_method/personal_info_suggester.cc index 48a9948..b3fbb26 100644 --- a/chrome/browser/chromeos/input_method/personal_info_suggester.cc +++ b/chrome/browser/chromeos/input_method/personal_info_suggester.cc
@@ -185,8 +185,8 @@ return SuggestionStatus::kDismiss; } if (highlighted_index_ == kNoneHighlighted && buttons_.size() > 0) { - if (event.key == "Down") { - highlighted_index_ = 0; + if (event.key == "Down" || event.key == "Up") { + highlighted_index_ = event.key == "Down" ? 0 : buttons_.size() - 1; SetButtonHighlighted(buttons_[highlighted_index_], true); return SuggestionStatus::kBrowsing; }
diff --git a/chrome/browser/chromeos/input_method/personal_info_suggester_unittest.cc b/chrome/browser/chromeos/input_method/personal_info_suggester_unittest.cc index fcbf936a..740a0aa 100644 --- a/chrome/browser/chromeos/input_method/personal_info_suggester_unittest.cc +++ b/chrome/browser/chromeos/input_method/personal_info_suggester_unittest.cc
@@ -74,7 +74,9 @@ return false; } - void ClickButton(const ui::ime::AssistiveWindowButton& button) override {} + void ClickButton(const ui::ime::AssistiveWindowButton& button) override { + button_clicked_ = button.id; + } bool SetButtonHighlighted(int context_id, const ui::ime::AssistiveWindowButton& button, @@ -102,6 +104,9 @@ void VerifyShowSettingLink(const bool show_setting_link) { EXPECT_EQ(show_setting_link_, show_setting_link); } + void VerifyButtonClicked(const ui::ime::ButtonId id) { + EXPECT_EQ(button_clicked_, id); + } bool IsSuggestionAccepted() { return suggestion_accepted_; } @@ -111,6 +116,7 @@ bool show_annotation_ = false; bool show_setting_link_ = false; bool suggestion_accepted_ = false; + ui::ime::ButtonId button_clicked_ = ui::ime::ButtonId::kNone; std::vector<std::string> previous_suggestions_; }; @@ -382,7 +388,7 @@ suggestion_handler_->VerifySuggestion(base::EmptyString16(), 0); } -TEST_F(PersonalInfoSuggesterTest, AcceptSuggestion) { +TEST_F(PersonalInfoSuggesterTest, AcceptSuggestionWithDownEnter) { profile_->set_profile_name(base::UTF16ToUTF8(email_)); suggester_->Suggest(base::UTF8ToUTF16("my email is ")); @@ -393,6 +399,20 @@ EXPECT_TRUE(suggestion_handler_->IsSuggestionAccepted()); } +TEST_F(PersonalInfoSuggesterTest, AcceptSuggestionWithUpEnter) { + DictionaryPrefUpdate update(profile_->GetPrefs(), + prefs::kAssistiveInputFeatureSettings); + update->SetIntKey(kPersonalInfoSuggesterAcceptanceCount, 1); + profile_->set_profile_name(base::UTF16ToUTF8(email_)); + + suggester_->Suggest(base::UTF8ToUTF16("my email is ")); + SendKeyboardEvent("Up"); + SendKeyboardEvent("Enter"); + + suggestion_handler_->VerifySuggestion(base::EmptyString16(), 0); + EXPECT_TRUE(suggestion_handler_->IsSuggestionAccepted()); +} + TEST_F(PersonalInfoSuggesterTest, DismissSuggestion) { autofill::AutofillProfile autofill_profile(base::GenerateGUID(), autofill::test::kEmptyOrigin); @@ -511,4 +531,35 @@ suggestion_handler_->VerifyShowSettingLink(false); } +TEST_F(PersonalInfoSuggesterTest, ClickSettingsWithDownDownEnter) { + DictionaryPrefUpdate update(profile_->GetPrefs(), + prefs::kAssistiveInputFeatureSettings); + update->RemoveKey(kPersonalInfoSuggesterShowSettingCount); + update->RemoveKey(kPersonalInfoSuggesterAcceptanceCount); + profile_->set_profile_name(base::UTF16ToUTF8(email_)); + + suggester_->Suggest(base::UTF8ToUTF16("my email is ")); + SendKeyboardEvent("Down"); + SendKeyboardEvent("Down"); + SendKeyboardEvent("Enter"); + + suggestion_handler_->VerifyButtonClicked( + ui::ime::ButtonId::kSmartInputsSettingLink); +} + +TEST_F(PersonalInfoSuggesterTest, ClickSettingsWithUpEnter) { + DictionaryPrefUpdate update(profile_->GetPrefs(), + prefs::kAssistiveInputFeatureSettings); + update->RemoveKey(kPersonalInfoSuggesterShowSettingCount); + update->RemoveKey(kPersonalInfoSuggesterAcceptanceCount); + profile_->set_profile_name(base::UTF16ToUTF8(email_)); + + suggester_->Suggest(base::UTF8ToUTF16("my email is ")); + SendKeyboardEvent("Up"); + SendKeyboardEvent("Enter"); + + suggestion_handler_->VerifyButtonClicked( + ui::ime::ButtonId::kSmartInputsSettingLink); +} + } // namespace chromeos
diff --git a/chrome/browser/chromeos/power/ml/recent_events_counter.cc b/chrome/browser/chromeos/power/ml/recent_events_counter.cc index 06764295..677386c 100644 --- a/chrome/browser/chromeos/power/ml/recent_events_counter.cc +++ b/chrome/browser/chromeos/power/ml/recent_events_counter.cc
@@ -31,7 +31,7 @@ if (timestamp > latest_) { latest_ = timestamp; } - int bucket_index = GetBucketIndex(timestamp); + const int bucket_index = GetBucketIndex(timestamp); if (timestamp < first_bucket_time_ + duration_) { // The event is within the current time window so increment the bucket. event_count_[bucket_index]++; @@ -61,17 +61,17 @@ timestamp - (timestamp % bucket_duration_) + bucket_duration_ - duration_; } -int RecentEventsCounter::GetTotal(base::TimeDelta now) { +int RecentEventsCounter::GetTotal(base::TimeDelta now) const { DCHECK_GE(now, latest_); if (now >= first_bucket_time_ + 2 * duration_) { return 0; } int total = 0; - base::TimeDelta start = + const base::TimeDelta start = std::max(first_bucket_time_, now - duration_ + bucket_duration_); - base::TimeDelta end = + const base::TimeDelta end = std::min(now, first_bucket_time_ + duration_ - bucket_duration_); - int end_index = GetBucketIndex(end); + const int end_index = GetBucketIndex(end); for (int i = GetBucketIndex(start); i != end_index; i = (i + 1) % num_buckets_) { total += event_count_[i]; @@ -83,7 +83,7 @@ int RecentEventsCounter::GetBucketIndex(base::TimeDelta timestamp) const { DCHECK_GE(timestamp, base::TimeDelta()); - int index = (timestamp % duration_).IntDiv(bucket_duration_); + const int index = (timestamp % duration_).IntDiv(bucket_duration_); DCHECK_GE(index, 0); DCHECK_LT(index, num_buckets_); return index;
diff --git a/chrome/browser/chromeos/power/ml/recent_events_counter.h b/chrome/browser/chromeos/power/ml/recent_events_counter.h index a233791..b1345b5a 100644 --- a/chrome/browser/chromeos/power/ml/recent_events_counter.h +++ b/chrome/browser/chromeos/power/ml/recent_events_counter.h
@@ -41,17 +41,18 @@ // Log an event at timedelta |timestamp|. |timestamp| cannot be negative. void Log(base::TimeDelta timestamp); - // Return the count of events reported in the |duration_| preceeding |now|. - int GetTotal(base::TimeDelta now); + // Return the count of events reported in the |duration_| preceding |now|. + // |now| must be >= any |timestamp| previously passed to Log(). + int GetTotal(base::TimeDelta now) const; private: // Return the index of the bucket containing |timestamp|. int GetBucketIndex(base::TimeDelta timestamp) const; // The length of time that events should be recorded. - base::TimeDelta duration_; + const base::TimeDelta duration_; // The number of buckets to use to record the events. - int num_buckets_; + const int num_buckets_; // The number of events in each bucket. std::vector<int> event_count_; // The index of the first bucket. |event_count_| is a circular array.
diff --git a/chrome/browser/extensions/api/signed_in_devices/id_mapping_helper_unittest.cc b/chrome/browser/extensions/api/signed_in_devices/id_mapping_helper_unittest.cc index 28db171..8cde18ea 100644 --- a/chrome/browser/extensions/api/signed_in_devices/id_mapping_helper_unittest.cc +++ b/chrome/browser/extensions/api/signed_in_devices/id_mapping_helper_unittest.cc
@@ -26,7 +26,7 @@ sync_pb::SyncEnums_DeviceType_TYPE_LINUX, device_id, "manufacturer", "model", base::Time(), syncer::DeviceInfoUtil::GetPulseInterval(), /*send_tab_to_self_receiving_enabled=*/true, - /*sharing_info=*/base::nullopt); + /*sharing_info=*/base::nullopt, /*fcm_registration_token=*/std::string()); } } // namespace
diff --git a/chrome/browser/extensions/api/signed_in_devices/signed_in_devices_api_unittest.cc b/chrome/browser/extensions/api/signed_in_devices/signed_in_devices_api_unittest.cc index de0fca5f..daa6d4ed 100644 --- a/chrome/browser/extensions/api/signed_in_devices/signed_in_devices_api_unittest.cc +++ b/chrome/browser/extensions/api/signed_in_devices/signed_in_devices_api_unittest.cc
@@ -36,7 +36,8 @@ sync_pb::SyncEnums_DeviceType_TYPE_LINUX, "device_id", "manufacturer", "model", base::Time(), syncer::DeviceInfoUtil::GetPulseInterval(), /*send_tab_to_self_receiving_enabled=*/true, - /*sharing_info=*/base::nullopt); + /*sharing_info=*/base::nullopt, + /*fcm_registration_token=*/std::string()); } } // namespace
diff --git a/chrome/browser/extensions/chrome_extensions_browser_interface_binders.cc b/chrome/browser/extensions/chrome_extensions_browser_interface_binders.cc index 21bbab9..72481da7 100644 --- a/chrome/browser/extensions/chrome_extensions_browser_interface_binders.cc +++ b/chrome/browser/extensions/chrome_extensions_browser_interface_binders.cc
@@ -21,22 +21,14 @@ #include "chrome/browser/profiles/profile.h" #include "chrome/browser/speech/extension_api/tts_engine_extension_observer.h" #include "chrome/common/extensions/extension_constants.h" -#include "chromeos/components/camera_app_ui/camera_app_helper.mojom.h" -#include "chromeos/components/camera_app_ui/camera_app_helper_impl.h" +#include "chromeos/components/camera_app_ui/camera_app_ui.h" #include "chromeos/services/cfm/public/buildflags/buildflags.h" #include "chromeos/services/media_perception/public/mojom/media_perception.mojom.h" #include "chromeos/services/tts/public/mojom/tts_service.mojom.h" -#include "components/arc/intent_helper/arc_intent_helper_bridge.h" -#include "content/public/browser/browser_task_traits.h" -#include "content/public/browser/media_device_id.h" -#include "content/public/browser/video_capture_service.h" #include "extensions/browser/api/extensions_api_client.h" #include "extensions/browser/api/media_perception_private/media_perception_api_delegate.h" -#include "media/capture/video/chromeos/camera_app_device_provider_impl.h" -#include "media/capture/video/chromeos/mojom/camera_app.mojom.h" #include "mojo/public/cpp/bindings/pending_receiver.h" #include "mojo/public/cpp/bindings/self_owned_receiver.h" -#include "ui/aura/window.h" #if BUILDFLAG(GOOGLE_CHROME_BRANDING) #include "chromeos/services/ime/public/mojom/input_engine.mojom.h" @@ -92,80 +84,6 @@ } #endif // BUILDFLAG(GOOGLE_CHROME_BRANDING) -// Translates the renderer-side source ID to video device id. -void TranslateVideoDeviceId( - const std::string& salt, - const url::Origin& origin, - const std::string& source_id, - base::OnceCallback<void(const base::Optional<std::string>&)> callback) { - auto callback_on_io_thread = base::BindOnce( - [](const std::string& salt, const url::Origin& origin, - const std::string& source_id, - base::OnceCallback<void(const base::Optional<std::string>&)> - callback) { - content::GetMediaDeviceIDForHMAC( - blink::mojom::MediaStreamType::DEVICE_VIDEO_CAPTURE, salt, - std::move(origin), source_id, std::move(callback)); - }, - salt, std::move(origin), source_id, std::move(callback)); - content::GetIOThreadTaskRunner({})->PostTask( - FROM_HERE, std::move(callback_on_io_thread)); -} - -void HandleCameraResult( - content::BrowserContext* context, - uint32_t intent_id, - arc::mojom::CameraIntentAction action, - const std::vector<uint8_t>& data, - chromeos_camera::mojom::CameraAppHelper::HandleCameraResultCallback - callback) { - auto* intent_helper = - arc::ArcIntentHelperBridge::GetForBrowserContext(context); - intent_helper->HandleCameraResult(intent_id, action, data, - std::move(callback)); -} - -// Connects to CameraAppDeviceProvider which could be used to get -// CameraAppDevice from video capture service through CameraAppDeviceBridge. -void ConnectToCameraAppDeviceProvider( - content::RenderFrameHost* source, - mojo::PendingReceiver<cros::mojom::CameraAppDeviceProvider> receiver) { - mojo::PendingRemote<cros::mojom::CameraAppDeviceBridge> device_bridge; - auto device_bridge_receiver = device_bridge.InitWithNewPipeAndPassReceiver(); - - // Connects to CameraAppDeviceBridge from video_capture service. - content::GetVideoCaptureService().ConnectToCameraAppDeviceBridge( - std::move(device_bridge_receiver)); - - auto security_origin = source->GetLastCommittedOrigin(); - auto media_device_id_salt = - source->GetProcess()->GetBrowserContext()->GetMediaDeviceIDSalt(); - - auto mapping_callback = - base::BindRepeating(&TranslateVideoDeviceId, media_device_id_salt, - std::move(security_origin)); - - auto camera_app_device_provider = - std::make_unique<media::CameraAppDeviceProviderImpl>( - std::move(device_bridge), std::move(mapping_callback)); - mojo::MakeSelfOwnedReceiver(std::move(camera_app_device_provider), - std::move(receiver)); -} - -// Connects to CameraAppHelper that could handle camera intents. -void ConnectToCameraAppHelper( - content::RenderFrameHost* source, - mojo::PendingReceiver<chromeos_camera::mojom::CameraAppHelper> receiver) { - auto handle_result_callback = base::BindRepeating( - &HandleCameraResult, source->GetProcess()->GetBrowserContext()); - auto* window = source->GetNativeView()->GetToplevelWindow(); - auto camera_app_helper = - std::make_unique<chromeos_camera::CameraAppHelperImpl>( - std::move(handle_result_callback), window); - mojo::MakeSelfOwnedReceiver(std::move(camera_app_helper), - std::move(receiver)); -} - void BindTtsStream( content::RenderFrameHost* render_frame_host, mojo::PendingReceiver<chromeos::tts::mojom::TtsStream> receiver) { @@ -173,6 +91,7 @@ Profile::FromBrowserContext(render_frame_host->GetBrowserContext())) ->BindTtsStream(std::move(receiver)); } + #endif } // namespace @@ -240,10 +159,10 @@ if (extension->id().compare(extension_misc::kCameraAppId) == 0 || extension->id().compare(extension_misc::kCameraAppDevId) == 0) { - binder_map->Add<cros::mojom::CameraAppDeviceProvider>( - base::BindRepeating(&ConnectToCameraAppDeviceProvider)); + binder_map->Add<cros::mojom::CameraAppDeviceProvider>(base::BindRepeating( + &chromeos::CameraAppUI::ConnectToCameraAppDeviceProvider)); binder_map->Add<chromeos_camera::mojom::CameraAppHelper>( - base::BindRepeating(&ConnectToCameraAppHelper)); + base::BindRepeating(&chromeos::CameraAppUI::ConnectToCameraAppHelper)); } if (extension->id() == extension_misc::kGoogleSpeechSynthesisExtensionId) {
diff --git a/chrome/browser/first_run/android/first_run_prefs.cc b/chrome/browser/first_run/android/first_run_prefs.cc new file mode 100644 index 0000000..3287072 --- /dev/null +++ b/chrome/browser/first_run/android/first_run_prefs.cc
@@ -0,0 +1,12 @@ +// 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 "chrome/browser/first_run/android/first_run_prefs.h" + +namespace first_run { + +// Whether the ToS should be shown during the first-run for CCTs/PWAs. +const char kCCTToSDialogEnabled[] = "policy.cct_tos_dialog_enabled"; + +} // namespace first_run
diff --git a/chrome/browser/first_run/android/first_run_prefs.h b/chrome/browser/first_run/android/first_run_prefs.h new file mode 100644 index 0000000..fa87914a --- /dev/null +++ b/chrome/browser/first_run/android/first_run_prefs.h
@@ -0,0 +1,14 @@ +// 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 CHROME_BROWSER_FIRST_RUN_ANDROID_FIRST_RUN_PREFS_H_ +#define CHROME_BROWSER_FIRST_RUN_ANDROID_FIRST_RUN_PREFS_H_ + +namespace first_run { + +extern const char kCCTToSDialogEnabled[]; + +} // namespace first_run + +#endif // CHROME_BROWSER_FIRST_RUN_ANDROID_FIRST_RUN_PREFS_H_
diff --git a/chrome/browser/first_run/android/first_run_utils.cc b/chrome/browser/first_run/android/first_run_utils.cc index 8a03e7f..1649376 100644 --- a/chrome/browser/first_run/android/first_run_utils.cc +++ b/chrome/browser/first_run/android/first_run_utils.cc
@@ -6,6 +6,8 @@ #include "chrome/android/chrome_jni_headers/FirstRunUtils_jni.h" #include "chrome/browser/browser_process.h" +#include "chrome/browser/first_run/android/first_run_prefs.h" +#include "components/policy/core/common/policy_pref_names.h" #include "components/prefs/pref_service.h" #include "components/web_resource/web_resource_pref_names.h" @@ -16,3 +18,8 @@ static void JNI_FirstRunUtils_SetEulaAccepted(JNIEnv* env) { g_browser_process->local_state()->SetBoolean(prefs::kEulaAccepted, true); } + +static jboolean JNI_FirstRunUtils_GetCctTosDialogEnabled(JNIEnv* env) { + return g_browser_process->local_state()->GetBoolean( + first_run::kCCTToSDialogEnabled); +}
diff --git a/chrome/browser/flag-metadata.json b/chrome/browser/flag-metadata.json index dcef6f4..495c3c6 100644 --- a/chrome/browser/flag-metadata.json +++ b/chrome/browser/flag-metadata.json
@@ -702,6 +702,11 @@ "expiry_milestone": 86 }, { + "name": "crostini-use-dlc", + "owners": [ "sidereal", "davidmunro", "nverne" ], + "expiry_milestone": 88 + }, + { "name": "crostini-webui-upgrader", "owners": [ "nverne", "davidmunro@google.com", "benwells" ], "expiry_milestone": 88
diff --git a/chrome/browser/flag_descriptions.cc b/chrome/browser/flag_descriptions.cc index ff752a6..d9d60b6 100644 --- a/chrome/browser/flag_descriptions.cc +++ b/chrome/browser/flag_descriptions.cc
@@ -3547,6 +3547,11 @@ const char kCrostiniWebUIUpgraderDescription[] = "Enable the new WebUI Crostini Upgrader Dialog."; +const char kCrostiniUseDlcName[] = "Crostini Use DLC"; +const char kCrostiniUseDlcDescription[] = + "Download the termina VM using the new DLC service instead of the old " + "component updater."; + const char kCryptAuthV2DeviceActivityStatusName[] = "CryptAuth Device Activity Status"; const char kCryptAuthV2DeviceActivityStatusDescription[] =
diff --git a/chrome/browser/flag_descriptions.h b/chrome/browser/flag_descriptions.h index b8c8c59..f6c6a00 100644 --- a/chrome/browser/flag_descriptions.h +++ b/chrome/browser/flag_descriptions.h
@@ -2050,6 +2050,9 @@ extern const char kCrostiniWebUIUpgraderName[]; extern const char kCrostiniWebUIUpgraderDescription[]; +extern const char kCrostiniUseDlcName[]; +extern const char kCrostiniUseDlcDescription[]; + extern const char kCryptAuthV2DeviceActivityStatusName[]; extern const char kCryptAuthV2DeviceActivityStatusDescription[];
diff --git a/chrome/browser/image_editor/internal/BUILD.gn b/chrome/browser/image_editor/internal/BUILD.gn index 408e6458..12c1e6d 100644 --- a/chrome/browser/image_editor/internal/BUILD.gn +++ b/chrome/browser/image_editor/internal/BUILD.gn
@@ -9,9 +9,5 @@ android_library("java") { sources = [ "android/java/src/org/chromium/chrome/browser/image_editor/ImageEditorDialogCoordinatorImpl.java" ] - deps = [ - "//chrome/browser/image_editor/public:java", - "//chrome/browser/share:java", - "//chrome/browser/tab:java", - ] + deps = [ "//chrome/browser/image_editor/public:java" ] }
diff --git a/chrome/browser/policy/configuration_policy_handler_list_factory.cc b/chrome/browser/policy/configuration_policy_handler_list_factory.cc index 250e8e7..8b8efc2 100644 --- a/chrome/browser/policy/configuration_policy_handler_list_factory.cc +++ b/chrome/browser/policy/configuration_policy_handler_list_factory.cc
@@ -95,6 +95,7 @@ #include "ppapi/buildflags/buildflags.h" #if defined(OS_ANDROID) +#include "chrome/browser/first_run/android/first_run_prefs.h" #include "chrome/browser/search/contextual_search_policy_handler_android.h" #else // defined(OS_ANDROID) #include "chrome/browser/download/default_download_dir_policy_handler.h" @@ -1334,6 +1335,12 @@ { key::kInsecureFormsWarningsEnabled, prefs::kMixedFormsWarningsEnabled, base::Value::Type::BOOLEAN }, + +#if defined(OS_ANDROID) + { key::kCCTToSDialogEnabled, + first_run::kCCTToSDialogEnabled, + base::Value::Type::BOOLEAN }, +#endif // defined(OS_ANDROID) }; // clang-format on
diff --git a/chrome/browser/prefs/browser_prefs.cc b/chrome/browser/prefs/browser_prefs.cc index 0dab3c2..9ea5f09 100644 --- a/chrome/browser/prefs/browser_prefs.cc +++ b/chrome/browser/prefs/browser_prefs.cc
@@ -218,6 +218,7 @@ #include "chrome/browser/android/oom_intervention/oom_intervention_decider.h" #include "chrome/browser/android/preferences/browser_prefs_android.h" #include "chrome/browser/android/usage_stats/usage_stats_bridge.h" +#include "chrome/browser/first_run/android/first_run_prefs.h" #include "chrome/browser/media/android/cdm/media_drm_origin_id_manager.h" #include "chrome/browser/ssl/known_interception_disclosure_infobar_delegate.h" #include "components/cdm/browser/media_drm_storage_impl.h" @@ -746,7 +747,9 @@ #if defined(OS_ANDROID) ::android::RegisterPrefs(registry); -#else + + registry->RegisterBooleanPref(first_run::kCCTToSDialogEnabled, true); +#else // defined(OS_ANDROID) enterprise_connectors::RegisterLocalStatePrefs(registry); enterprise_reporting::RegisterLocalStatePrefs(registry); gcm::RegisterPrefs(registry); @@ -759,7 +762,7 @@ UpgradeDetector::RegisterPrefs(registry); registry->RegisterBooleanPref(kNtpActivateHideShortcutsFieldTrial, false); -#endif // !defined(OS_ANDROID) +#endif // defined(OS_ANDROID) #if defined(OS_CHROMEOS) arc::prefs::RegisterLocalStatePrefs(registry);
diff --git a/chrome/browser/profiles/profile_destroyer.cc b/chrome/browser/profiles/profile_destroyer.cc index 7182f1f..6b81529 100644 --- a/chrome/browser/profiles/profile_destroyer.cc +++ b/chrome/browser/profiles/profile_destroyer.cc
@@ -101,23 +101,30 @@ void ProfileDestroyer::DestroyOffTheRecordProfileNow(Profile* const profile) { DCHECK(profile); DCHECK(profile->IsOffTheRecord()); - if (pending_destroyers_) { - for (auto i = pending_destroyers_->begin(); i != pending_destroyers_->end(); - ++i) { - if ((*i)->profile_ == profile) { - // We want to signal this in debug builds so that we don't lose sight of - // these potential leaks, but we handle it in release so that we don't - // crash or corrupt profile data on disk. - NOTREACHED() << "A render process host wasn't destroyed early enough."; - (*i)->profile_ = NULL; - break; - } - } + if (ResetPendingDestroyers(profile)) { + // We want to signal this in debug builds so that we don't lose sight of + // these potential leaks, but we handle it in release so that we don't + // crash or corrupt profile data on disk. + NOTREACHED() << "A render process host wasn't destroyed early enough."; } DCHECK(profile->GetOriginalProfile()); profile->GetOriginalProfile()->DestroyOffTheRecordProfile(profile); } +bool ProfileDestroyer::ResetPendingDestroyers(Profile* const profile) { + DCHECK(profile); + bool found = false; + if (pending_destroyers_) { + for (auto* i : *pending_destroyers_) { + if (i->profile_ == profile) { + i->profile_ = nullptr; + found = true; + } + } + } + return found; +} + ProfileDestroyer::ProfileDestroyer(Profile* const profile, HostSet* hosts) : num_hosts_(0), profile_(profile) { if (pending_destroyers_ == NULL) @@ -185,7 +192,14 @@ DCHECK(profile_->GetOriginalProfile()); profile_->GetOriginalProfile()->DestroyOffTheRecordProfile(profile_); +#if defined(OS_ANDROID) + // It is possible on Android platform that more than one destroyer + // is instantiated to delete a single profile. Reset the others to + // avoid UAF. See https://crbug.com/1029677. + ResetPendingDestroyers(profile_); +#else profile_ = nullptr; +#endif // And stop the timer so we can be released early too. timer_.Stop();
diff --git a/chrome/browser/profiles/profile_destroyer.h b/chrome/browser/profiles/profile_destroyer.h index 30fda03..e7e5365 100644 --- a/chrome/browser/profiles/profile_destroyer.h +++ b/chrome/browser/profiles/profile_destroyer.h
@@ -27,6 +27,11 @@ static void DestroyProfileWhenAppropriate(Profile* const profile); static void DestroyOffTheRecordProfileNow(Profile* const profile); + // Reset pending destroyers whose target profile matches the given one + // to make it stop attempting to destroy it. Returns true if any object + // object was found to match and get reset. + static bool ResetPendingDestroyers(Profile* const profile); + private: typedef std::set<content::RenderProcessHost*> HostSet; typedef std::set<ProfileDestroyer*> DestroyerSet;
diff --git a/chrome/browser/resources/settings/chromeos/ambient_mode_page/BUILD.gn b/chrome/browser/resources/settings/chromeos/ambient_mode_page/BUILD.gn index 17eb709..c496b2a 100644 --- a/chrome/browser/resources/settings/chromeos/ambient_mode_page/BUILD.gn +++ b/chrome/browser/resources/settings/chromeos/ambient_mode_page/BUILD.gn
@@ -45,6 +45,7 @@ ":constants", "..:os_route", "../..:router", + "../localized_link:localized_link", "//ui/webui/resources/js:assert", "//ui/webui/resources/js:cr", "//ui/webui/resources/js:i18n_behavior", @@ -57,9 +58,9 @@ js_library("topic_source_item") { deps = [ + ":constants", "//ui/webui/resources/js:cr", "//ui/webui/resources/js:i18n_behavior", - ":constants" ] } @@ -124,8 +125,8 @@ sources = [ "$root_gen_dir/chrome/browser/resources/settings/chromeos/ambient_mode_page/topic_source_item.m.js" ] deps = [ ":constants.m", - "//ui/webui/resources/js:i18n_behavior.m", "//third_party/polymer/v3_0/components-chromium/polymer:polymer_bundled", + "//ui/webui/resources/js:i18n_behavior.m", ] extra_deps = [ ":topic_source_item_module" ] }
diff --git a/chrome/browser/resources/settings/chromeos/ambient_mode_page/ambient_mode_page.html b/chrome/browser/resources/settings/chromeos/ambient_mode_page/ambient_mode_page.html index a05d247..90e6e622 100644 --- a/chrome/browser/resources/settings/chromeos/ambient_mode_page/ambient_mode_page.html +++ b/chrome/browser/resources/settings/chromeos/ambient_mode_page/ambient_mode_page.html
@@ -21,6 +21,16 @@ <style include="cr-shared-style settings-shared iron-flex"> #ambientModeEnable { border-bottom: var(--cr-separator-line); + border-top: var(--cr-separator-line); + } + + #pageDescription { + padding-inline-start: var(--cr-section-padding); + padding-top: 0; + } + + #topicSourceListDiv { + border-bottom: var(--cr-separator-line); } /* Set padding on children instead of the container itself to ensure that @@ -32,6 +42,10 @@ padding-inline-start: var(--cr-section-padding); } </style> + <h2 id="pageDescription"> + $i18n{ambientModePageDescription} + </h2> + <settings-toggle-button id="ambientModeEnable" class="primary-toggle" pref="{{prefs.settings.ambient_mode.enabled}}"
diff --git a/chrome/browser/resources/settings/chromeos/ambient_mode_page/ambient_mode_photos_page.html b/chrome/browser/resources/settings/chromeos/ambient_mode_page/ambient_mode_photos_page.html index 26de907..2368a3a 100644 --- a/chrome/browser/resources/settings/chromeos/ambient_mode_page/ambient_mode_photos_page.html +++ b/chrome/browser/resources/settings/chromeos/ambient_mode_page/ambient_mode_photos_page.html
@@ -6,6 +6,7 @@ <link rel="import" href="chrome://resources/html/assert.html"> <link rel="import" href="chrome://resources/html/web_ui_listener_behavior.html"> <link rel="import" href="constants.html"> +<link rel="import" href="../localized_link/localized_link.html"> <link rel="import" href="../os_route.html"> <link rel="import" href="../../router.html"> <link rel="import" href="../../settings_shared_css.html"> @@ -14,12 +15,10 @@ <template> <style include="settings-shared"> </style> + <settings-localized-link class="cr-row first" + localized-string="[[getTitleInnerHtml_(topicSource_)]]"> + </settings-localized-link> - <div class="cr-row first"> - <h2 class="flex"> - [[getDescription_(topicSource_)]] - </h2> - </div> <iron-list id="containers" class="list-frame" items="[[photosContainers_]]"> <template> <cr-checkbox class="list-item"
diff --git a/chrome/browser/resources/settings/chromeos/ambient_mode_page/ambient_mode_photos_page.js b/chrome/browser/resources/settings/chromeos/ambient_mode_page/ambient_mode_photos_page.js index 60a8bad..d6a5598 100644 --- a/chrome/browser/resources/settings/chromeos/ambient_mode_page/ambient_mode_photos_page.js +++ b/chrome/browser/resources/settings/chromeos/ambient_mode_page/ambient_mode_photos_page.js
@@ -85,12 +85,11 @@ * @return {string} * @private */ - getDescription_(topicSource) { - // TODO(b/159766700, items 2&3): Finalize the strings and i18n. + getTitleInnerHtml_(topicSource) { if (topicSource === AmbientModeTopicSource.GOOGLE_PHOTOS) { - return 'A slideshow of selected memories will be created'; + return this.i18nAdvanced('ambientModeAlbumsSubpageGooglePhotosTitle'); } else { - return 'Curated images and artwork'; + return this.i18n('ambientModeTopicSourceArtGalleryDescription'); } },
diff --git a/chrome/browser/resources/settings/chromeos/ambient_mode_page/topic_source_item.html b/chrome/browser/resources/settings/chromeos/ambient_mode_page/topic_source_item.html index e6440e7c..564b4f9 100644 --- a/chrome/browser/resources/settings/chromeos/ambient_mode_page/topic_source_item.html +++ b/chrome/browser/resources/settings/chromeos/ambient_mode_page/topic_source_item.html
@@ -33,7 +33,10 @@ </div> <div id="labelWrapper" aria-hidden="true"> - <span>[[getItemName_(item)]]</span> + <div>[[getItemName_(item)]]</div> + <div class="cr-secondary-text"> + [[getItemDescription_(item)]] + </div> </div> <cr-icon-button class="subpage-arrow" id="subpage-button"
diff --git a/chrome/browser/resources/settings/chromeos/ambient_mode_page/topic_source_item.js b/chrome/browser/resources/settings/chromeos/ambient_mode_page/topic_source_item.js index 3614679..b95c166 100644 --- a/chrome/browser/resources/settings/chromeos/ambient_mode_page/topic_source_item.js +++ b/chrome/browser/resources/settings/chromeos/ambient_mode_page/topic_source_item.js
@@ -64,13 +64,26 @@ }, /** + * @return {string} + * @private + */ + getItemDescription_() { + if (this.item === AmbientModeTopicSource.GOOGLE_PHOTOS) { + return this.i18n('ambientModeTopicSourceGooglePhotosDescription'); + } else if (this.item === AmbientModeTopicSource.ART_GALLERY) { + return this.i18n('ambientModeTopicSourceArtGalleryDescription'); + } else { + return ''; + } + }, + + /** * The aria label for the subpage button. * @return {string} * @private */ getButtonLabel_() { - // TODO(b/159766700, item 5): Finalize the strings and i18n. - return 'Select ' + this.getItemName_() + ' albums'; + return this.i18n('ambientModeTopicSourceSubpage', this.getItemName_()); }, /** @@ -78,12 +91,12 @@ * @private */ computeAriaLabel_() { - // TODO(b/159766700, item 6): Finalize the strings and i18n. + const rowLabel = this.getItemName_() + ' ' + this.getItemDescription_(); if (this.checked) { - return 'Topic source ' + this.getItemName_() + ' selected, ' + - this.getButtonLabel_(); + return this.i18n( + 'ambientModeTopicSourceSelectedRow', rowLabel, this.getItemName_()); } - return 'Select topic source ' + this.getItemName_(); + return this.i18n('ambientModeTopicSourceUnselectedRow', rowLabel); }, /**
diff --git a/chrome/browser/resources/settings/chromeos/ambient_mode_page/topic_source_list.html b/chrome/browser/resources/settings/chromeos/ambient_mode_page/topic_source_list.html index f8d24eb..58a69cb 100644 --- a/chrome/browser/resources/settings/chromeos/ambient_mode_page/topic_source_list.html +++ b/chrome/browser/resources/settings/chromeos/ambient_mode_page/topic_source_list.html
@@ -11,6 +11,11 @@ <dom-module id="topic-source-list"> <template> <style include="settings-shared cr-shared-style iron-flex"> + #topicSourceTitle { + padding-bottom: 16px; + padding-top: 16px; + } + topic-source-item { align-items: center; height: 48px; @@ -20,15 +25,13 @@ border-top: var(--cr-separator-line); } - iron-list > :focus { + iron-list > :focus { background-color: var(--cr-focused-item-color); } </style> - <div class="first"> - <h2> - $i18n{ambientModeTopicSourceTitle} - </h2> - </div> + <h2 id="topicSourceTitle"> + $i18n{ambientModeTopicSourceTitle} + </h2> <iron-list id="topicSourceList" selection-enabled items="[[topicSources]]"
diff --git a/chrome/browser/sharing/fake_device_info.cc b/chrome/browser/sharing/fake_device_info.cc index 3a5b0d7d7..5140e69d 100644 --- a/chrome/browser/sharing/fake_device_info.cc +++ b/chrome/browser/sharing/fake_device_info.cc
@@ -18,5 +18,6 @@ guid, name, "chrome_version", "user_agent", device_type, "device_id", manufacturer_name, model_name, last_updated_timestamp, syncer::DeviceInfoUtil::GetPulseInterval(), - /*send_tab_to_self_receiving_enabled=*/false, sharing_info); + /*send_tab_to_self_receiving_enabled=*/false, sharing_info, + /*fcm_registration_token=*/std::string()); }
diff --git a/chrome/browser/sharing/webrtc/sharing_service_host.cc b/chrome/browser/sharing/webrtc/sharing_service_host.cc index 5a0ac461..abee3a43 100644 --- a/chrome/browser/sharing/webrtc/sharing_service_host.cc +++ b/chrome/browser/sharing/webrtc/sharing_service_host.cc
@@ -111,7 +111,8 @@ /*model_name=*/std::string(), /*last_updated_timestamp=*/base::Time(), /*pulse_interval=*/base::TimeDelta(), - /*send_tab_to_self_receiving_enabled=*/true, std::move(sharing_info)); + /*send_tab_to_self_receiving_enabled=*/true, std::move(sharing_info), + /*fcm_registration_token=*/std::string()); } // We want to send to the passed |fcm_configuration| as it is the most up to
diff --git a/chrome/browser/sync/device_info_sync_service_factory.cc b/chrome/browser/sync/device_info_sync_service_factory.cc index a293667..145aeb7 100644 --- a/chrome/browser/sync/device_info_sync_service_factory.cc +++ b/chrome/browser/sync/device_info_sync_service_factory.cc
@@ -19,10 +19,12 @@ #include "chrome/browser/sharing/sharing_sync_preference.h" #include "chrome/browser/signin/chrome_device_id_helper.h" #include "chrome/browser/sync/model_type_store_service_factory.h" +#include "chrome/browser/sync/sync_invalidations_service_factory.h" #include "chrome/common/channel_info.h" #include "components/keyed_service/content/browser_context_dependency_manager.h" #include "components/send_tab_to_self/features.h" #include "components/sync/base/sync_prefs.h" +#include "components/sync/invalidations/sync_invalidations_service.h" #include "components/sync/model/model_type_store_service.h" #include "components/sync_device_info/device_info_prefs.h" #include "components/sync_device_info/device_info_sync_client.h" @@ -107,6 +109,7 @@ "DeviceInfoSyncService", BrowserContextDependencyManager::GetInstance()) { DependsOn(ModelTypeStoreServiceFactory::GetInstance()); + DependsOn(SyncInvalidationsServiceFactory::GetInstance()); } DeviceInfoSyncServiceFactory::~DeviceInfoSyncServiceFactory() {} @@ -120,7 +123,9 @@ auto local_device_info_provider = std::make_unique<syncer::LocalDeviceInfoProviderImpl>( chrome::GetChannel(), chrome::GetVersionString(), - device_info_sync_client.get()); + device_info_sync_client.get(), + SyncInvalidationsServiceFactory::GetForProfile(profile)); + auto device_prefs = std::make_unique<syncer::DeviceInfoPrefs>( profile->GetPrefs(), base::DefaultClock::GetInstance());
diff --git a/chrome/browser/ui/views/accessibility/browser_accessibility_uitest_auralinux.cc b/chrome/browser/ui/views/accessibility/browser_accessibility_uitest_auralinux.cc index 50726904..20b0cc5 100644 --- a/chrome/browser/ui/views/accessibility/browser_accessibility_uitest_auralinux.cc +++ b/chrome/browser/ui/views/accessibility/browser_accessibility_uitest_auralinux.cc
@@ -6,6 +6,7 @@ #include <stddef.h> #include "base/macros.h" +#include "chrome/browser/devtools/devtools_window_testing.h" #include "chrome/browser/ui/browser.h" #include "chrome/browser/ui/browser_window.h" #include "chrome/browser/ui/tab_modal_confirm_dialog.h" @@ -14,6 +15,7 @@ #include "content/public/browser/render_widget_host_view.h" #include "content/public/browser/web_contents.h" #include "content/public/test/browser_test.h" +#include "content/public/test/test_navigation_observer.h" #include "ui/accessibility/platform/ax_platform_node.h" class AuraLinuxAccessibilityInProcessBrowserTest : public InProcessBrowserTest { @@ -187,3 +189,43 @@ VerifyEmbedRelationships(); } + +// Tests that the embedded relationship is set on the main web contents when +// the DevTools is opened. +IN_PROC_BROWSER_TEST_F(AuraLinuxAccessibilityInProcessBrowserTest, + EmbeddedRelationshipWithDevTools) { + // Force the creation of the document's native object which sets up the + // relationship. + content::WebContents* active_web_contents = + browser()->tab_strip_model()->GetActiveWebContents(); + ASSERT_NE(nullptr, active_web_contents->GetRenderWidgetHostView() + ->GetNativeViewAccessible()); + EXPECT_EQ(1, browser()->tab_strip_model()->count()); + EXPECT_EQ(0, browser()->tab_strip_model()->active_index()); + + // Opens DevTools docked. + DevToolsWindow* devtools = + DevToolsWindowTesting::OpenDevToolsWindowSync(browser(), true); + VerifyEmbedRelationships(); + + // Closes the DevTools window. + DevToolsWindowTesting::CloseDevToolsWindowSync(devtools); + VerifyEmbedRelationships(); + + // Opens DevTools in a separate window. + devtools = DevToolsWindowTesting::OpenDevToolsWindowSync(browser(), false); + // Waits until the DevTools is loaded. + base::RunLoop runloop_undocked; + devtools->SetLoadCompletedCallback(runloop_undocked.QuitClosure()); + runloop_undocked.Run(); + // Waits until the DevTools finishes the navigation. + views::WebView* webview = BrowserView::GetBrowserViewForBrowser(browser()) + ->GetDevToolsWebViewForTest(); + content::TestNavigationObserver observer_undocked(webview->web_contents()); + observer_undocked.WaitForNavigationFinished(); + VerifyEmbedRelationships(); + + // Closes the DevTools window. + DevToolsWindowTesting::CloseDevToolsWindowSync(devtools); + VerifyEmbedRelationships(); +}
diff --git a/chrome/browser/ui/views/frame/browser_view.cc b/chrome/browser/ui/views/frame/browser_view.cc index 92274e8..adbc523 100644 --- a/chrome/browser/ui/views/frame/browser_view.cc +++ b/chrome/browser/ui/views/frame/browser_view.cc
@@ -568,6 +568,7 @@ contents_container->AddChildView(std::move(devtools_web_view)); contents_web_view_ = contents_container->AddChildView(std::move(contents_web_view)); + contents_web_view_->set_is_primary_web_contents_for_window(true); contents_container->SetLayoutManager(std::make_unique<ContentsLayoutManager>( devtools_web_view_, contents_web_view_));
diff --git a/chrome/browser/ui/views/sharing/sharing_browsertest.cc b/chrome/browser/ui/views/sharing/sharing_browsertest.cc index df3a744..2868551d 100644 --- a/chrome/browser/ui/views/sharing/sharing_browsertest.cc +++ b/chrome/browser/ui/views/sharing/sharing_browsertest.cc
@@ -152,7 +152,8 @@ original_device.last_updated_timestamp(), original_device.pulse_interval(), original_device.send_tab_to_self_receiving_enabled(), - original_device.sharing_info()); + original_device.sharing_info(), + original_device.fcm_registration_token()); fake_device_info_tracker_.Add(fake_device.get()); device_infos_.push_back(std::move(fake_device)); }
diff --git a/chrome/browser/ui/webui/chrome_web_ui_controller_factory.cc b/chrome/browser/ui/webui/chrome_web_ui_controller_factory.cc index c11f5c0..beb0a32 100644 --- a/chrome/browser/ui/webui/chrome_web_ui_controller_factory.cc +++ b/chrome/browser/ui/webui/chrome_web_ui_controller_factory.cc
@@ -76,8 +76,6 @@ #include "chrome/common/pref_names.h" #include "chrome/common/url_constants.h" #include "chrome/common/webui_url_constants.h" -#include "chromeos/components/camera_app_ui/camera_app_ui.h" -#include "chromeos/components/camera_app_ui/url_constants.h" #include "components/favicon/core/favicon_service.h" #include "components/favicon_base/favicon_util.h" #include "components/favicon_base/select_favicon_frames.h" @@ -190,6 +188,8 @@ #include "chrome/browser/ui/webui/chromeos/smb_shares/smb_share_dialog.h" #include "chrome/browser/ui/webui/chromeos/sys_internals/sys_internals_ui.h" #include "chrome/browser/ui/webui/settings/chromeos/os_settings_ui.h" +#include "chromeos/components/camera_app_ui/camera_app_ui.h" +#include "chromeos/components/camera_app_ui/url_constants.h" #include "chromeos/components/help_app_ui/help_app_ui.h" #include "chromeos/components/help_app_ui/url_constants.h" #include "chromeos/components/media_app_ui/media_app_guest_ui.h"
diff --git a/chrome/browser/ui/webui/settings/chromeos/personalization_section.cc b/chrome/browser/ui/webui/settings/chromeos/personalization_section.cc index dfbbaf1..ffe1ca4 100644 --- a/chrome/browser/ui/webui/settings/chromeos/personalization_section.cc +++ b/chrome/browser/ui/webui/settings/chromeos/personalization_section.cc
@@ -7,6 +7,7 @@ #include "ash/public/cpp/ambient/ambient_prefs.h" #include "base/bind.h" #include "base/no_destructor.h" +#include "base/strings/utf_string_conversions.h" #include "chrome/browser/profiles/profile.h" #include "chrome/browser/ui/webui/settings/chromeos/ambient_mode_handler.h" #include "chrome/browser/ui/webui/settings/chromeos/change_picture_handler.h" @@ -15,6 +16,7 @@ #include "chrome/browser/ui/webui/settings/chromeos/wallpaper_handler.h" #include "chrome/browser/ui/webui/webui_util.h" #include "chrome/common/chrome_features.h" +#include "chrome/common/url_constants.h" #include "chrome/grit/generated_resources.h" #include "chromeos/constants/chromeos_features.h" #include "components/prefs/pref_service.h" @@ -22,6 +24,7 @@ #include "content/public/browser/web_ui_data_source.h" #include "ui/base/l10n/l10n_util.h" #include "ui/base/webui/web_ui_util.h" +#include "url/gurl.h" namespace chromeos { namespace settings { @@ -111,6 +114,10 @@ return chromeos::features::IsAmbientModeEnabled(); } +GURL GetGooglePhotosURL() { + return GURL(chrome::kGooglePhotosURL); +} + } // namespace PersonalizationSection::PersonalizationSection( @@ -147,14 +154,30 @@ {"ambientModeTitle", IDS_OS_SETTINGS_AMBIENT_MODE_TITLE}, {"ambientModeEnabled", IDS_OS_SETTINGS_AMBIENT_MODE_ENABLED}, {"ambientModeDisabled", IDS_OS_SETTINGS_AMBIENT_MODE_DISABLED}, + {"ambientModePageDescription", + IDS_OS_SETTINGS_AMBIENT_MODE_PAGE_DESCRIPTION}, {"ambientModeOn", IDS_OS_SETTINGS_AMBIENT_MODE_ON}, {"ambientModeOff", IDS_OS_SETTINGS_AMBIENT_MODE_OFF}, {"ambientModeTopicSourceTitle", IDS_OS_SETTINGS_AMBIENT_MODE_TOPIC_SOURCE_TITLE}, {"ambientModeTopicSourceGooglePhotos", IDS_OS_SETTINGS_AMBIENT_MODE_TOPIC_SOURCE_GOOGLE_PHOTOS}, + {"ambientModeTopicSourceGooglePhotosDescription", + IDS_OS_SETTINGS_AMBIENT_MODE_TOPIC_SOURCE_GOOGLE_PHOTOS_DESCRIPTION}, + {"ambientModeTopicSourceGooglePhotosDescriptionNoAlbum", + IDS_OS_SETTINGS_AMBIENT_MODE_TOPIC_SOURCE_GOOGLE_PHOTOS_DESCRIPTION_NO_ALBUM}, {"ambientModeTopicSourceArtGallery", IDS_OS_SETTINGS_AMBIENT_MODE_TOPIC_SOURCE_ART_GALLERY}, + {"ambientModeTopicSourceArtGalleryDescription", + IDS_OS_SETTINGS_AMBIENT_MODE_TOPIC_SOURCE_ART_GALLERY_DESCRIPTION}, + {"ambientModeTopicSourceSelectedRow", + IDS_OS_SETTINGS_AMBIENT_MODE_TOPIC_SOURCE_SELECTED_ROW}, + {"ambientModeTopicSourceUnselectedRow", + IDS_OS_SETTINGS_AMBIENT_MODE_TOPIC_SOURCE_UNSELECTED_ROW}, + {"ambientModeTopicSourceSubpage", + IDS_OS_SETTINGS_AMBIENT_MODE_TOPIC_SOURCE_SUBPAGE}, + {"ambientModeAlbumsSubpageGooglePhotosNoAlbum", + IDS_OS_SETTINGS_AMBIENT_MODE_ALBUMS_SUBPAGE_GOOGLE_PHOTOS_NO_ALBUM}, {"changePictureTitle", IDS_OS_SETTINGS_CHANGE_PICTURE_TITLE}, {"openWallpaperApp", IDS_OS_SETTINGS_OPEN_WALLPAPER_APP}, {"personalizationPageTitle", IDS_OS_SETTINGS_PERSONALIZATION}, @@ -184,6 +207,11 @@ "changePictureVideoModeEnabled", base::FeatureList::IsEnabled(::features::kChangePictureVideoMode)); html_source->AddBoolean("isAmbientModeEnabled", IsAmbientModeAllowed()); + html_source->AddString( + "ambientModeAlbumsSubpageGooglePhotosTitle", + l10n_util::GetStringFUTF16( + IDS_OS_SETTINGS_AMBIENT_MODE_ALBUMS_SUBPAGE_GOOGLE_PHOTOS_TITLE, + base::UTF8ToUTF16(GetGooglePhotosURL().spec()))); } void PersonalizationSection::AddHandlers(content::WebUI* web_ui) {
diff --git a/chrome/browser/web_applications/components/BUILD.gn b/chrome/browser/web_applications/components/BUILD.gn index 7af3d35b..2202106a 100644 --- a/chrome/browser/web_applications/components/BUILD.gn +++ b/chrome/browser/web_applications/components/BUILD.gn
@@ -12,6 +12,8 @@ "app_registry_controller.h", "app_shortcut_manager.cc", "app_shortcut_manager.h", + "external_app_install_features.cc", + "external_app_install_features.h", "external_install_options.cc", "external_install_options.h", "externally_installed_web_app_prefs.cc",
diff --git a/chrome/browser/web_applications/components/external_app_install_features.cc b/chrome/browser/web_applications/components/external_app_install_features.cc new file mode 100644 index 0000000..3ad3da25 --- /dev/null +++ b/chrome/browser/web_applications/components/external_app_install_features.cc
@@ -0,0 +1,37 @@ +// 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 "chrome/browser/web_applications/components/external_app_install_features.h" + +#include "base/feature_list.h" + +namespace web_app { + +namespace { + +// A hard coded list of features available for externally installed apps to gate +// their installation on via their config file settings. +constexpr base::Feature kExternalAppInstallFeatures[] = {}; + +bool g_always_enabled_for_testing = false; + +} // namespace + +bool IsExternalAppInstallFeatureEnabled(base::StringPiece feature_name) { + if (g_always_enabled_for_testing) + return true; + + for (const base::Feature& feature : kExternalAppInstallFeatures) { + if (feature.name == feature_name) + return base::FeatureList::IsEnabled(feature); + } + + return false; +} + +base::AutoReset<bool> SetExternalAppInstallFeatureAlwaysEnabledForTesting() { + return base::AutoReset<bool>(&g_always_enabled_for_testing, true); +} + +} // namespace web_app
diff --git a/chrome/browser/web_applications/components/external_app_install_features.h b/chrome/browser/web_applications/components/external_app_install_features.h new file mode 100644 index 0000000..2a6c1e5 --- /dev/null +++ b/chrome/browser/web_applications/components/external_app_install_features.h
@@ -0,0 +1,26 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROME_BROWSER_WEB_APPLICATIONS_COMPONENTS_EXTERNAL_APP_INSTALL_FEATURES_H_ +#define CHROME_BROWSER_WEB_APPLICATIONS_COMPONENTS_EXTERNAL_APP_INSTALL_FEATURES_H_ + +#include "base/auto_reset.h" +#include "base/strings/string_piece_forward.h" + +namespace base { +struct Feature; +} + +namespace web_app { + +// Returns the base::Feature in |kExternalAppInstallFeatures| that corresponds +// to |feature_name|. Used by external app install configs to gate installation +// on features listed in |kExternalAppInstallFeatures|. +bool IsExternalAppInstallFeatureEnabled(base::StringPiece feature_name); + +base::AutoReset<bool> SetExternalAppInstallFeatureAlwaysEnabledForTesting(); + +} // namespace web_app + +#endif // CHROME_BROWSER_WEB_APPLICATIONS_COMPONENTS_EXTERNAL_APP_INSTALL_FEATURES_H_
diff --git a/chrome/browser/web_applications/external_web_app_manager.cc b/chrome/browser/web_applications/external_web_app_manager.cc index a69a3582..06aa083 100644 --- a/chrome/browser/web_applications/external_web_app_manager.cc +++ b/chrome/browser/web_applications/external_web_app_manager.cc
@@ -24,6 +24,7 @@ #include "build/build_config.h" #include "chrome/browser/apps/user_type_filter.h" #include "chrome/browser/profiles/profile.h" +#include "chrome/browser/web_applications/components/external_app_install_features.h" #include "chrome/browser/web_applications/components/pending_app_manager.h" #include "chrome/browser/web_applications/components/web_app_constants.h" #include "chrome/browser/web_applications/components/web_app_install_utils.h" @@ -58,7 +59,9 @@ constexpr char kCreateShortcuts[] = "create_shortcuts"; // kFeatureName is an optional string parameter specifying a feature -// associated with this app. If specified: +// associated with this app. The feature must be present in +// |kExternalAppInstallFeatures| to be applicable. +// If specified: // - if the feature is enabled, the app will be installed // - if the feature is not enabled, the app will be removed. constexpr char kFeatureName[] = "feature_name"; @@ -84,30 +87,6 @@ bool g_skip_startup_scan_for_testing_ = false; -bool IsFeatureEnabled(const std::string& feature_name) { - // The feature system ensures there is only ever one Feature instance for each - // given feature name. To enable multiple apps to be gated by the same field - // trial this means there needs to be a global map of Features that is used. - static base::NoDestructor< - std::map<std::string, std::unique_ptr<base::Feature>>> - feature_map; - if (!feature_map->count(feature_name)) { - // To ensure the string used in the feature (which is a char*) is stable - // (i.e. is not freed later on), the key of the map is used. So, first - // insert a null Feature into the map, and then swap it with a real Feature - // constructed using the pointer from the key. - auto it = feature_map->insert(std::make_pair(feature_name, nullptr)).first; - it->second = std::make_unique<base::Feature>( - base::Feature{it->first.c_str(), base::FEATURE_DISABLED_BY_DEFAULT}); - } - - // Use the feature from the map, not the one in the pair above, as it has a - // stable address. - const auto it = feature_map->find(feature_name); - DCHECK(it != feature_map->end()); - return base::FeatureList::IsEnabled(*it->second); -} - base::Optional<ExternalInstallOptions> ParseConfig( base::FilePath file, const std::string& user_type, @@ -127,9 +106,11 @@ const base::Value* value = app_config.FindKeyOfType(kFeatureName, base::Value::Type::STRING); if (value) { - std::string feature_name = value->GetString(); + // TODO(crbug.com/1104696): Add metrics for whether the app was + // enabled/disabled by the feature. + const std::string& feature_name = value->GetString(); VLOG(1) << file << " checking feature " << feature_name; - if (!IsFeatureEnabled(feature_name)) { + if (!IsExternalAppInstallFeatureEnabled(feature_name)) { VLOG(1) << file << " feature not enabled"; return base::nullopt; }
diff --git a/chrome/browser/web_applications/external_web_app_manager_unittest.cc b/chrome/browser/web_applications/external_web_app_manager_unittest.cc index 0667d9e..b374fb6 100644 --- a/chrome/browser/web_applications/external_web_app_manager_unittest.cc +++ b/chrome/browser/web_applications/external_web_app_manager_unittest.cc
@@ -18,6 +18,7 @@ #include "base/test/scoped_feature_list.h" #include "base/test/scoped_path_override.h" #include "chrome/browser/supervised_user/supervised_user_constants.h" +#include "chrome/browser/web_applications/components/external_app_install_features.h" #include "chrome/browser/web_applications/components/web_app_constants.h" #include "chrome/common/chrome_features.h" #include "chrome/common/chrome_paths.h" @@ -337,9 +338,9 @@ } TEST_F(ScanDirForExternalWebAppsTest, EnabledByFinch) { - base::test::ScopedFeatureList scoped_feature_list; - scoped_feature_list.InitAndEnableFeature( - base::Feature{"test_feature_name", base::FEATURE_DISABLED_BY_DEFAULT}); + base::AutoReset<bool> testing_scope = + SetExternalAppInstallFeatureAlwaysEnabledForTesting(); + const auto app_infos = ScanTestDirForExternalWebApps("enabled_by_finch"); // The enabled_by_finch directory contains two JSON file containing apps
diff --git a/chrome/build/mac.pgo.txt b/chrome/build/mac.pgo.txt index 05a4a36..c100433 100644 --- a/chrome/build/mac.pgo.txt +++ b/chrome/build/mac.pgo.txt
@@ -1 +1 @@ -chrome-mac-master-1596302245-695ebe4bcd9d65e6d10de8973a8cfa80d2288da6.profdata +chrome-mac-master-1596412401-499a1d19f67343992fe5d914f92b43fa1d307048.profdata
diff --git a/chrome/build/win32.pgo.txt b/chrome/build/win32.pgo.txt index c422af98..515440c 100644 --- a/chrome/build/win32.pgo.txt +++ b/chrome/build/win32.pgo.txt
@@ -1 +1 @@ -chrome-win32-master-1596302245-7200f9e3b515c87353205d63e1689e3308ba5fbd.profdata +chrome-win32-master-1596423223-91ce8ffb27fe07737e8454516dffd91f532e6919.profdata
diff --git a/chrome/build/win64.pgo.txt b/chrome/build/win64.pgo.txt index 51d4c3ef..eb13382 100644 --- a/chrome/build/win64.pgo.txt +++ b/chrome/build/win64.pgo.txt
@@ -1 +1 @@ -chrome-win64-master-1596302245-ba2a3d88fb1e555e9ead57ee269f293e98e0f6ea.profdata +chrome-win64-master-1596412401-579029213d4f74aa7dcc7fceb7d50be1699c065b.profdata
diff --git a/chrome/common/url_constants.cc b/chrome/common/url_constants.cc index 95114621..4b9d5c1 100644 --- a/chrome/common/url_constants.cc +++ b/chrome/common/url_constants.cc
@@ -169,6 +169,8 @@ const char kGooglePasswordManagerURL[] = "https://passwords.google.com"; +const char kGooglePhotosURL[] = "https://photos.google.com"; + const char kLearnMoreReportingURL[] = "https://support.google.com/chrome/?p=ui_usagestat";
diff --git a/chrome/common/url_constants.h b/chrome/common/url_constants.h index f608dbf9..cc1ac278 100644 --- a/chrome/common/url_constants.h +++ b/chrome/common/url_constants.h
@@ -164,6 +164,9 @@ // URL of the Google Password Manager. extern const char kGooglePasswordManagerURL[]; +// URL of the Google Photos. +extern const char kGooglePhotosURL[]; + // The URL for the "Learn more" page for the usage/crash reporting option in the // first run dialog. extern const char kLearnMoreReportingURL[];
diff --git a/chrome/test/data/policy/policy_test_cases.json b/chrome/test/data/policy/policy_test_cases.json index feb3fe6..13a6ad7 100644 --- a/chrome/test/data/policy/policy_test_cases.json +++ b/chrome/test/data/policy/policy_test_cases.json
@@ -7920,5 +7920,14 @@ } } ] + }, + "CCTToSDialogEnabled": { + "os": ["android"], + "policy_pref_mapping_test": [ + { + "policies": { "CCTToSDialogEnabled": false }, + "prefs": { "policy.cct_tos_dialog_enabled": { "local_state": true } } + } + ] } }
diff --git a/chromeos/components/camera_app_ui/BUILD.gn b/chromeos/components/camera_app_ui/BUILD.gn index ddc407e..efa06804 100644 --- a/chromeos/components/camera_app_ui/BUILD.gn +++ b/chromeos/components/camera_app_ui/BUILD.gn
@@ -26,6 +26,7 @@ "//chromeos/resources:camera_app_resources", "//chromeos/strings", "//chromeos/system", + "//components/arc", "//content/public/browser", "//content/public/common", "//media/capture:capture_lib",
diff --git a/chromeos/components/camera_app_ui/DEPS b/chromeos/components/camera_app_ui/DEPS index 14ae680..3bd6afc 100644 --- a/chromeos/components/camera_app_ui/DEPS +++ b/chromeos/components/camera_app_ui/DEPS
@@ -1,7 +1,9 @@ include_rules = [ "+ash/public/cpp", + "+components/arc/intent_helper", "+components/content_settings/core/common", "+content/public/browser", + "+media/capture/video/chromeos", "+ui/aura", "+ui/webui", ]
diff --git a/chromeos/components/camera_app_ui/camera_app_helper_impl.cc b/chromeos/components/camera_app_ui/camera_app_helper_impl.cc index 912506c..35efb6f 100644 --- a/chromeos/components/camera_app_ui/camera_app_helper_impl.cc +++ b/chromeos/components/camera_app_ui/camera_app_helper_impl.cc
@@ -44,6 +44,12 @@ ash::ScreenBacklight::Get()->RemoveObserver(this); } +void CameraAppHelperImpl::Bind( + mojo::PendingReceiver<mojom::CameraAppHelper> receiver) { + receiver_.reset(); + receiver_.Bind(std::move(receiver)); +} + void CameraAppHelperImpl::HandleCameraResult( uint32_t intent_id, arc::mojom::CameraIntentAction action,
diff --git a/chromeos/components/camera_app_ui/camera_app_helper_impl.h b/chromeos/components/camera_app_ui/camera_app_helper_impl.h index e4c5588..f1f5b5a3 100644 --- a/chromeos/components/camera_app_ui/camera_app_helper_impl.h +++ b/chromeos/components/camera_app_ui/camera_app_helper_impl.h
@@ -34,6 +34,7 @@ CameraAppHelperImpl(CameraResultCallback camera_result_callback, aura::Window* window); ~CameraAppHelperImpl() override; + void Bind(mojo::PendingReceiver<mojom::CameraAppHelper> receiver); // mojom::CameraAppHelper implementations. void HandleCameraResult(uint32_t intent_id, @@ -61,6 +62,8 @@ mojo::Remote<TabletModeMonitor> tablet_monitor_; mojo::Remote<ScreenStateMonitor> screen_state_monitor_; + mojo::Receiver<chromeos_camera::mojom::CameraAppHelper> receiver_{this}; + DISALLOW_COPY_AND_ASSIGN(CameraAppHelperImpl); };
diff --git a/chromeos/components/camera_app_ui/camera_app_ui.cc b/chromeos/components/camera_app_ui/camera_app_ui.cc index 98d1192b..5a4b019 100644 --- a/chromeos/components/camera_app_ui/camera_app_ui.cc +++ b/chromeos/components/camera_app_ui/camera_app_ui.cc
@@ -4,17 +4,28 @@ #include "chromeos/components/camera_app_ui/camera_app_ui.h" -#include <utility> - +#include "ash/public/cpp/window_properties.h" #include "base/bind.h" +#include "chromeos/components/camera_app_ui/camera_app_helper_impl.h" #include "chromeos/components/camera_app_ui/url_constants.h" #include "chromeos/grit/chromeos_camera_app_resources.h" #include "chromeos/grit/chromeos_camera_app_resources_map.h" +#include "components/arc/intent_helper/arc_intent_helper_bridge.h" #include "components/content_settings/core/common/content_settings_types.h" +#include "content/public/browser/browser_context.h" +#include "content/public/browser/browser_task_traits.h" +#include "content/public/browser/media_device_id.h" +#include "content/public/browser/render_process_host.h" +#include "content/public/browser/video_capture_service.h" #include "content/public/browser/web_contents.h" #include "content/public/browser/web_ui_data_source.h" +#include "media/capture/video/chromeos/camera_app_device_provider_impl.h" +#include "media/capture/video/chromeos/mojom/camera_app.mojom.h" +#include "mojo/public/cpp/bindings/pending_receiver.h" +#include "mojo/public/cpp/bindings/self_owned_receiver.h" #include "mojo/public/js/grit/mojo_bindings_resources.h" #include "services/network/public/mojom/content_security_policy.mojom.h" +#include "ui/aura/window.h" #include "ui/webui/webui_allowlist.h" namespace chromeos { @@ -74,6 +85,70 @@ return source; } +// Translates the renderer-side source ID to video device id. +void TranslateVideoDeviceId( + const std::string& salt, + const url::Origin& origin, + const std::string& source_id, + base::OnceCallback<void(const base::Optional<std::string>&)> callback) { + auto callback_on_io_thread = base::BindOnce( + [](const std::string& salt, const url::Origin& origin, + const std::string& source_id, + base::OnceCallback<void(const base::Optional<std::string>&)> + callback) { + content::GetMediaDeviceIDForHMAC( + blink::mojom::MediaStreamType::DEVICE_VIDEO_CAPTURE, salt, + std::move(origin), source_id, std::move(callback)); + }, + salt, std::move(origin), source_id, std::move(callback)); + content::GetIOThreadTaskRunner({})->PostTask( + FROM_HERE, std::move(callback_on_io_thread)); +} + +void HandleCameraResult( + content::BrowserContext* context, + uint32_t intent_id, + arc::mojom::CameraIntentAction action, + const std::vector<uint8_t>& data, + chromeos_camera::mojom::CameraAppHelper::HandleCameraResultCallback + callback) { + auto* intent_helper = + arc::ArcIntentHelperBridge::GetForBrowserContext(context); + intent_helper->HandleCameraResult(intent_id, action, data, + std::move(callback)); +} + +std::unique_ptr<media::CameraAppDeviceProviderImpl> +CreateCameraAppDeviceProvider(const url::Origin& security_origin, + content::BrowserContext* context) { + auto media_device_id_salt = context->GetMediaDeviceIDSalt(); + + mojo::PendingRemote<cros::mojom::CameraAppDeviceBridge> device_bridge; + auto device_bridge_receiver = device_bridge.InitWithNewPipeAndPassReceiver(); + + // Connects to CameraAppDeviceBridge from video_capture service. + content::GetVideoCaptureService().ConnectToCameraAppDeviceBridge( + std::move(device_bridge_receiver)); + + auto mapping_callback = + base::BindRepeating(&TranslateVideoDeviceId, media_device_id_salt, + std::move(security_origin)); + + return std::make_unique<media::CameraAppDeviceProviderImpl>( + std::move(device_bridge), std::move(mapping_callback)); +} + +std::unique_ptr<chromeos_camera::CameraAppHelperImpl> CreateCameraAppHelper( + content::BrowserContext* browser_context, + aura::Window* window) { + DCHECK_NE(window, nullptr); + auto handle_result_callback = + base::BindRepeating(&HandleCameraResult, browser_context); + + return std::make_unique<chromeos_camera::CameraAppHelperImpl>( + std::move(handle_result_callback), window); +} + } // namespace /////////////////////////////////////////////////////////////////////////////// @@ -82,6 +157,26 @@ // /////////////////////////////////////////////////////////////////////////////// +// static +void CameraAppUI::ConnectToCameraAppDeviceProvider( + content::RenderFrameHost* source, + mojo::PendingReceiver<cros::mojom::CameraAppDeviceProvider> receiver) { + auto provider = + CreateCameraAppDeviceProvider(source->GetLastCommittedOrigin(), + source->GetProcess()->GetBrowserContext()); + mojo::MakeSelfOwnedReceiver(std::move(provider), std::move(receiver)); +} + +// static +void CameraAppUI::ConnectToCameraAppHelper( + content::RenderFrameHost* source, + mojo::PendingReceiver<chromeos_camera::mojom::CameraAppHelper> receiver) { + auto* window = source->GetNativeView()->GetToplevelWindow(); + auto helper = + CreateCameraAppHelper(source->GetProcess()->GetBrowserContext(), window); + mojo::MakeSelfOwnedReceiver(std::move(helper), std::move(receiver)); +} + CameraAppUI::CameraAppUI(content::WebUI* web_ui) : ui::MojoWebUIController(web_ui) { content::BrowserContext* browser_context = @@ -113,4 +208,25 @@ CameraAppUI::~CameraAppUI() = default; +void CameraAppUI::BindInterface( + mojo::PendingReceiver<cros::mojom::CameraAppDeviceProvider> receiver) { + provider_ = CreateCameraAppDeviceProvider( + url::Origin::Create(GURL(chromeos::kChromeUICameraAppURL)), + web_ui()->GetWebContents()->GetBrowserContext()); + provider_->Bind(std::move(receiver)); +} + +void CameraAppUI::BindInterface( + mojo::PendingReceiver<chromeos_camera::mojom::CameraAppHelper> receiver) { + helper_ = CreateCameraAppHelper( + web_ui()->GetWebContents()->GetBrowserContext(), window()); + helper_->Bind(std::move(receiver)); +} + +aura::Window* CameraAppUI::window() { + return web_ui()->GetWebContents()->GetTopLevelNativeWindow(); +} + +WEB_UI_CONTROLLER_TYPE_IMPL(CameraAppUI) + } // namespace chromeos
diff --git a/chromeos/components/camera_app_ui/camera_app_ui.h b/chromeos/components/camera_app_ui/camera_app_ui.h index 64bb1c2..31eafc50 100644 --- a/chromeos/components/camera_app_ui/camera_app_ui.h +++ b/chromeos/components/camera_app_ui/camera_app_ui.h
@@ -6,8 +6,19 @@ #define CHROMEOS_COMPONENTS_CAMERA_APP_UI_CAMERA_APP_UI_H_ #include "base/macros.h" +#include "chromeos/components/camera_app_ui/camera_app_helper.mojom.h" +#include "media/capture/video/chromeos/mojom/camera_app.mojom.h" +#include "ui/aura/window.h" #include "ui/webui/mojo_web_ui_controller.h" +namespace chromeos_camera { +class CameraAppHelperImpl; +} // namespace chromeos_camera + +namespace media { +class CameraAppDeviceProviderImpl; +} // namespace media + namespace chromeos { class CameraAppUI : public ui::MojoWebUIController { @@ -15,7 +26,40 @@ explicit CameraAppUI(content::WebUI* web_ui); ~CameraAppUI() override; + // [To be deprecated] This method is only used for CCA as a platform app and + // will be deprecated once we migrate CCA to SWA. + // Connects to CameraAppDeviceProvider which could be used to get + // CameraAppDevice from video capture service through CameraAppDeviceBridge. + static void ConnectToCameraAppDeviceProvider( + content::RenderFrameHost* source, + mojo::PendingReceiver<cros::mojom::CameraAppDeviceProvider> receiver); + + // [To be deprecated] This method is only used for CCA as a platform app and + // will be deprecated once we migrate CCA to SWA. + // Connects to CameraAppHelper that could handle camera intents. + static void ConnectToCameraAppHelper( + content::RenderFrameHost* source, + mojo::PendingReceiver<chromeos_camera::mojom::CameraAppHelper> receiver); + + // Instantiates implementor of the cros::mojom::CameraAppDeviceProvider mojo + // interface passing the pending receiver that will be internally bound. + void BindInterface( + mojo::PendingReceiver<cros::mojom::CameraAppDeviceProvider> receiver); + + // Instantiates implementor of the chromeos_camera::mojom::CameraAppHelper + // mojo interface passing the pending receiver that will be internally bound. + void BindInterface( + mojo::PendingReceiver<chromeos_camera::mojom::CameraAppHelper> receiver); + private: + aura::Window* window(); + + std::unique_ptr<media::CameraAppDeviceProviderImpl> provider_; + + std::unique_ptr<chromeos_camera::CameraAppHelperImpl> helper_; + + WEB_UI_CONTROLLER_TYPE_DECL(); + DISALLOW_COPY_AND_ASSIGN(CameraAppUI); };
diff --git a/chromeos/components/camera_app_ui/resources/src/js/background.js b/chromeos/components/camera_app_ui/resources/src/js/background.js index 98494da..8ecdf67 100644 --- a/chromeos/components/camera_app_ui/resources/src/js/background.js +++ b/chromeos/components/camera_app_ui/resources/src/js/background.js
@@ -68,9 +68,9 @@ /** * Callbacks called when specific window events happened. * @typedef {{ - * onActive: !function(CCAWindow), - * onSuspended: !function(CCAWindow), - * onClosed: !function(CCAWindow), + * onActive: function(CCAWindow), + * onSuspended: function(CCAWindow), + * onClosed: function(CCAWindow), * }} */ let WindowEventCallbacks; // eslint-disable-line no-unused-vars @@ -80,8 +80,8 @@ * onCreated()/onClosed() is called when AppWindow created/closed with window * url. onError() is called with error happening in AppWindow. * @typedef {{ - * onCreated: !function(string), - * onClosed: !function(string), + * onCreated: function(string), + * onClosed: function(string), * onError: !TestingErrorCallback, * }} */
diff --git a/chromeos/components/camera_app_ui/resources/src/js/browser_proxy/BUILD.gn b/chromeos/components/camera_app_ui/resources/src/js/browser_proxy/BUILD.gn index e423fa0..bcce2cf 100644 --- a/chromeos/components/camera_app_ui/resources/src/js/browser_proxy/BUILD.gn +++ b/chromeos/components/camera_app_ui/resources/src/js/browser_proxy/BUILD.gn
@@ -10,7 +10,11 @@ js_library("browser_proxy") { deps = [ + "..:background_ops", "..:chrome_util", + "..:intent", + "..:perf", + "..:type", "../models:chrome_file_system_entry", "../mojo:chrome_helper", ]
diff --git a/chromeos/components/camera_app_ui/resources/src/js/browser_proxy/browser_proxy.js b/chromeos/components/camera_app_ui/resources/src/js/browser_proxy/browser_proxy.js index 58d036c..c825ff8 100644 --- a/chromeos/components/camera_app_ui/resources/src/js/browser_proxy/browser_proxy.js +++ b/chromeos/components/camera_app_ui/resources/src/js/browser_proxy/browser_proxy.js
@@ -2,8 +2,9 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -import {promisify} from '../chrome_util.js'; +import {assert, promisify} from '../chrome_util.js'; import {ChromeDirectoryEntry} from '../models/chrome_file_system_entry.js'; +import {Resolution} from '../type.js'; // eslint-disable-next-line no-unused-vars import {BrowserProxy} from './browser_proxy_interface.js'; @@ -162,6 +163,84 @@ isMp4RecordingEnabled() { return true; } + + /** @override */ + getBackgroundOps() { + assert(window['backgroundOps'] !== undefined); + return window['backgroundOps']; + } + + /** @override */ + isFullscreenOrMaximized() { + return chrome.app.window.current().outerBounds.width >= screen.width || + chrome.app.window.current().outerBounds.height >= screen.height; + } + + /** @override */ + async fitWindow() { + const appWindow = chrome.app.window.current(); + + /** + * Get a preferred window size which can fit in current screen. + * @return {Resolution} Preferred window size. + */ + const getPreferredWindowSize = () => { + const inner = appWindow.innerBounds; + const outer = appWindow.outerBounds; + + const predefinedWidth = inner.minWidth; + const availableWidth = screen.availWidth; + + const topBarHeight = outer.height - inner.height; + const fixedRatioMaxWidth = + Math.floor((screen.availHeight - topBarHeight) * 16 / 9); + + let preferredWidth = + Math.min(predefinedWidth, availableWidth, fixedRatioMaxWidth); + preferredWidth -= preferredWidth % 16; + const preferredHeight = preferredWidth * 9 / 16; + + return new Resolution(preferredWidth, preferredHeight); + }; + + const {width, height} = getPreferredWindowSize(); + + return new Promise((resolve) => { + const inner = appWindow.innerBounds; + if (inner.width === width && inner.height === height) { + resolve(); + return; + } + + const listener = () => { + appWindow.onBoundsChanged.removeListener(listener); + resolve(); + }; + appWindow.onBoundsChanged.addListener(listener); + + Object.assign(inner, {width, height, minWidth: width, minHeight: height}); + }); + } + + /** @override */ + showWindow() { + chrome.app.window.current().show(); + } + + /** @override */ + hideWindow() { + chrome.app.window.current().hide(); + } + + /** @override */ + isMinimized() { + return chrome.app.window.current().isMinimized(); + } + + /** @override */ + addOnMinimizedListener(listener) { + chrome.app.window.current().onMinimized.addListener(listener); + } } export const browserProxy = new ChromeAppBrowserProxy();
diff --git a/chromeos/components/camera_app_ui/resources/src/js/browser_proxy/browser_proxy_interface.js b/chromeos/components/camera_app_ui/resources/src/js/browser_proxy/browser_proxy_interface.js index 06a8fd5..44096a8 100644 --- a/chromeos/components/camera_app_ui/resources/src/js/browser_proxy/browser_proxy_interface.js +++ b/chromeos/components/camera_app_ui/resources/src/js/browser_proxy/browser_proxy_interface.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. +// eslint-disable-next-line no-unused-vars +import {BackgroundOps} from '../background_ops.js'; import { AbstractDirectoryEntry, // eslint-disable-line no-unused-vars AbstractFileEntry, // eslint-disable-line no-unused-vars @@ -121,4 +123,44 @@ * @abstract */ isMp4RecordingEnabled() {} + + /** + * @return {BackgroundOps} + * @abstract + */ + getBackgroundOps() {} + + /** + * @return {boolean} + * @abstract + */ + isFullscreenOrMaximized() {} + + /** + * @return {!Promise} + * @abstract + */ + async fitWindow() {} + + /** + * @abstract + */ + showWindow() {} + + /** + * @abstract + */ + hideWindow() {} + + /** + * @return {boolean} + * @abstract + */ + isMinimized() {} + + /** + * @param {function(): void} listener + * @abstract + */ + addOnMinimizedListener(listener) {} }
diff --git a/chromeos/components/camera_app_ui/resources/src/js/browser_proxy/webui_browser_proxy.js b/chromeos/components/camera_app_ui/resources/src/js/browser_proxy/webui_browser_proxy.js index 560fc96..2558d47 100644 --- a/chromeos/components/camera_app_ui/resources/src/js/browser_proxy/webui_browser_proxy.js +++ b/chromeos/components/camera_app_ui/resources/src/js/browser_proxy/webui_browser_proxy.js
@@ -3,6 +3,10 @@ // found in the LICENSE file. // eslint-disable-next-line no-unused-vars +import {BackgroundOps} from '../background_ops.js'; +import {Intent} from '../intent.js'; +import {PerfLogger} from '../perf.js'; +// eslint-disable-next-line no-unused-vars import {BrowserProxy} from './browser_proxy_interface.js'; /* eslint-disable new-cap */ @@ -142,6 +146,55 @@ isMp4RecordingEnabled() { return false; } + + /** @override */ + getBackgroundOps() { + // TODO(980846): Refactor after migrating to SWA since there is no + // background page for SWA. + const perfLogger = new PerfLogger(); + const url = window.location.href; + const intent = url.includes('intent') ? Intent.create(new URL(url)) : null; + return /** @type {BackgroundOps} */ ({ + bindForegroundOps: (ops) => {}, + getIntent: () => intent, + getPerfLogger: () => perfLogger, + getTestingErrorCallback: () => null, + notifyActivation: () => {}, + notifySuspension: () => {}, + }); + } + + /** @override */ + isFullscreenOrMaximized() { + // TODO(980846): Implement the fullscreen monitor. + return false; + } + + /** @override */ + fitWindow() { + // TODO(980846): Remove the method once we migrate to SWA. + } + + /** @override */ + showWindow() { + // TODO(980846): Remove the method once we migrate to SWA. + } + + /** @override */ + hideWindow() { + // TODO(980846): Remove the method once we migrate to SWA. + } + + /** @override */ + isMinimized() { + // TODO(980846): Implement the minimization monitor. + return false; + } + + /** @override */ + addOnMinimizedListener(listener) { + // TODO(980846): Implement the minimization monitor. + } } export const browserProxy = new WebUIBrowserProxy();
diff --git a/chromeos/components/camera_app_ui/resources/src/js/device/constraints_preferrer.js b/chromeos/components/camera_app_ui/resources/src/js/device/constraints_preferrer.js index 10ccd5e5..ec93f6b 100644 --- a/chromeos/components/camera_app_ui/resources/src/js/device/constraints_preferrer.js +++ b/chromeos/components/camera_app_ui/resources/src/js/device/constraints_preferrer.js
@@ -32,13 +32,13 @@ */ export class ConstraintsPreferrer { /** - * @param {!function()} doReconfigureStream Trigger stream reconfiguration to + * @param {function()} doReconfigureStream Trigger stream reconfiguration to * reflect changes in user preferred settings. * @protected */ constructor(doReconfigureStream) { /** - * @type {!function()} + * @type {function()} * @protected */ this.doReconfigureStream_ = doReconfigureStream; @@ -69,7 +69,7 @@ /** * Listener for changes of preferred resolution used on particular video * device. - * @type {!function(string, !Resolution)} + * @type {function(string, !Resolution)} * @private */ this.preferredResolutionChangeListener_ = () => {}; @@ -153,7 +153,7 @@ /** * Sets listener for changes of preferred resolution used in taking photo on * particular video device. - * @param {!function(string, !Resolution)} listener + * @param {function(string, !Resolution)} listener */ setPreferredResolutionChangeListener(listener) { this.preferredResolutionChangeListener_ = listener; @@ -171,7 +171,7 @@ */ export class VideoConstraintsPreferrer extends ConstraintsPreferrer { /** - * @param {!function()} doReconfigureStream + * @param {function()} doReconfigureStream * @public */ constructor(doReconfigureStream) { @@ -437,7 +437,7 @@ */ export class PhotoConstraintsPreferrer extends ConstraintsPreferrer { /** - * @param {!function()} doReconfigureStream + * @param {function()} doReconfigureStream * @public */ constructor(doReconfigureStream) {
diff --git a/chromeos/components/camera_app_ui/resources/src/js/device/device_info_updater.js b/chromeos/components/camera_app_ui/resources/src/js/device/device_info_updater.js index ac5cd95e..8db11fd 100644 --- a/chromeos/components/camera_app_ui/resources/src/js/device/device_info_updater.js +++ b/chromeos/components/camera_app_ui/resources/src/js/device/device_info_updater.js
@@ -39,7 +39,7 @@ /** * Listeners to be called after new camera information is available. - * @type {!Array<!function(!DeviceInfoUpdater): Promise>} + * @type {!Array<function(!DeviceInfoUpdater): Promise>} * @private */ this.deviceChangeListeners_ = []; @@ -181,7 +181,7 @@ /** * Registers listener to be called when state of available devices changes. - * @param {!function(!DeviceInfoUpdater)} listener + * @param {function(!DeviceInfoUpdater)} listener */ addDeviceChangeListener(listener) { this.deviceChangeListeners_.push(listener); @@ -191,7 +191,7 @@ * Requests to lock update of device information. This function is preserved * for device information reader to lock the update capability so as to ensure * getting consistent data between all information providers. - * @param {!function(): Promise} callback Called after + * @param {function(): Promise} callback Called after * update capability is locked. Getting information from all providers in * callback are guaranteed to be consistent. */
diff --git a/chromeos/components/camera_app_ui/resources/src/js/error.js b/chromeos/components/camera_app_ui/resources/src/js/error.js index 26ce0dd..649e977 100644 --- a/chromeos/components/camera_app_ui/resources/src/js/error.js +++ b/chromeos/components/camera_app_ui/resources/src/js/error.js
@@ -174,7 +174,6 @@ onTestingError({type, level, stack: formatErrorStack(error), time}); return; } - metrics.log( - metrics.Type.ERROR, type, level, errorName, fileName, funcName, lineNo, - colNo); + metrics.sendErrorEvent( + {type, level, errorName, fileName, funcName, lineNo, colNo}); }
diff --git a/chromeos/components/camera_app_ui/resources/src/js/intent.js b/chromeos/components/camera_app_ui/resources/src/js/intent.js index 1b8c1c55..6a59e98 100644 --- a/chromeos/components/camera_app_ui/resources/src/js/intent.js +++ b/chromeos/components/camera_app_ui/resources/src/js/intent.js
@@ -109,7 +109,7 @@ } this.done_ = true; await this.chromeHelper_.finish(this.intentId); - metrics.log(metrics.Type.INTENT, this, metrics.IntentResultType.CONFIRMED); + this.logResult(metrics.IntentResultType.CONFIRMED); } /** @@ -122,7 +122,7 @@ } this.done_ = true; await this.chromeHelper_.cancel(this.intentId); - metrics.log(metrics.Type.INTENT, this, metrics.IntentResultType.CANCELED); + this.logResult(metrics.IntentResultType.CANCELED); } /** @@ -149,6 +149,20 @@ } /** + * Logs the intent result to metrics. + * @param {metrics.IntentResultType} result + */ + logResult(result) { + metrics.sendIntentEvent({ + mode: this.mode, + result, + shouldHandleResult: this.shouldHandleResult, + shouldDownScale: this.shouldDownScale, + isSecure: this.isSecure, + }); + } + + /** * @param {!URL} url Url passed along with app launch event. * @return {!Intent} Created intent object. Returns null if input is not a * valid intent url.
diff --git a/chromeos/components/camera_app_ui/resources/src/js/main.js b/chromeos/components/camera_app_ui/resources/src/js/main.js index 29fc2e03..13b4204 100644 --- a/chromeos/components/camera_app_ui/resources/src/js/main.js +++ b/chromeos/components/camera_app_ui/resources/src/js/main.js
@@ -192,7 +192,7 @@ // Migrate pictures might take some time. Since it won't affect other // camera functions, we don't await here to avoid harming UX. filesystem.checkMigration(promptMigrate).then((ackMigrate) => { - metrics.log(metrics.Type.LAUNCH, ackMigrate); + metrics.sendLaunchEvent({ackMigrate}); }); const externalDir = filesystem.getExternalDirectory(); @@ -201,15 +201,15 @@ } catch (error) { console.error(error); if (error && error.message === 'no-migrate') { - chrome.app.window.current().close(); + window.close(); return; } nav.open(ViewName.WARNING, 'filesystem-failure'); } const showWindow = (async () => { - await util.fitWindow(); - chrome.app.window.current().show(); + await browserProxy.fitWindow(); + browserProxy.showWindow(); this.backgroundOps_.notifyActivation(); })(); const startCamera = (async () => { @@ -238,7 +238,7 @@ async suspend() { state.set(state.State.SUSPEND, true); await this.cameraView_.start(); - chrome.app.window.current().hide(); + browserProxy.hideWindow(); this.backgroundOps_.notifySuspension(); } @@ -247,7 +247,7 @@ */ resume() { state.set(state.State.SUSPEND, false); - chrome.app.window.current().show(); + browserProxy.showWindow(); this.backgroundOps_.notifyActivation(); } } @@ -265,8 +265,7 @@ if (instance !== null) { return; } - assert(window['backgroundOps'] !== undefined); - const /** !BackgroundOps */ bgOps = window['backgroundOps']; + const bgOps = browserProxy.getBackgroundOps(); const testErrorCallback = bgOps.getTestingErrorCallback(); metrics.initMetrics(); @@ -281,7 +280,7 @@ // Setup listener for performance events. perfLogger.addListener((event, duration, extras) => { - metrics.log(metrics.Type.PERF, event, duration, extras); + metrics.sendPerfEvent({event, duration, extras}); }); const states = Object.values(PerfEvent); states.push(state.State.TAKING);
diff --git a/chromeos/components/camera_app_ui/resources/src/js/metrics.js b/chromeos/components/camera_app_ui/resources/src/js/metrics.js index 444a576..5cb391d 100644 --- a/chromeos/components/camera_app_ui/resources/src/js/metrics.js +++ b/chromeos/components/camera_app_ui/resources/src/js/metrics.js
@@ -5,8 +5,6 @@ import {browserProxy} from './browser_proxy/browser_proxy.js'; import {assert} from './chrome_util.js'; // eslint-disable-next-line no-unused-vars -import {Intent} from './intent.js'; -// eslint-disable-next-line no-unused-vars import {PerfEvent} from './perf.js'; import * as state from './state.js'; // eslint-disable-next-line no-unused-vars @@ -38,7 +36,11 @@ * @param {Map<number, Object>=} dimen Optional object contains dimension * information. */ -function sendEvent(event, dimen = null) { +async function sendEvent(event, dimen = null) { + assert(window.ga !== null); + assert(ready !== null); + await ready; + const assignDimension = (e, d) => { d.forEach((value, key) => e[`dimension${key}`] = value); }; @@ -126,10 +128,17 @@ } /** - * Sends launch type event. - * @param {boolean} ackMigrate Whether acknowledged to migrate during launch. + * Parameters for logging launch event. |ackMigrate| stands for whether + * the user acknowledged to migrate during launch. + * @typedef {{ackMigrate: boolean}} */ -function sendLaunchEvent(ackMigrate) { +export let LaunchEventParam; + +/** + * Sends launch type event. + * @param {!LaunchEventParam} param + */ +export function sendLaunchEvent({ackMigrate}) { sendEvent({ eventCategory: 'launch', eventAction: 'start', @@ -211,7 +220,7 @@ * Sends capture type event. * @param {!CaptureEventParam} param */ -function sendCaptureEvent({ +export function sendCaptureEvent({ facing, duration = 0, resolution, @@ -265,13 +274,38 @@ ])); } + +/** + * Parameters for logging perf event. + * @record + */ +export class PerfEventParam { + /** + * @public + */ + constructor() { + /** + * @type {!PerfEvent} Target event type. + */ + this.event; + + /** + * @type {number} Duration of the event in ms. + */ + this.duration; + + /** + * @type {!Object|undefined} Optional information for the event. + */ + this.extras; + } +} + /** * Sends perf type event. - * @param {PerfEvent} event The target event type. - * @param {number} duration The duration of the event in ms. - * @param {Object=} extras Optional information for the event. + * @param {!PerfEventParam} param */ -function sendPerfEvent(event, duration, extras = {}) { +export function sendPerfEvent({event, duration, extras = {}}) { const {resolution = '', facing = ''} = extras; sendEvent( { @@ -289,38 +323,59 @@ } /** - * Sends intent type event. - * @param {!Intent} intent Intent to be logged. - * @param {!IntentResultType} intentResult + * See Intent class in intent.js for the descriptions of each field. + * TODO(b/131133953): Pass an Intent directly once the type-only import feature + * is implemented in Closure Compiler. + * @typedef {{ + * mode: Mode, + * result: IntentResultType, + * shouldHandleResult: boolean, + * shouldDownScale: boolean, + * isSecure: boolean, + * }} */ -function sendIntentEvent(intent, intentResult) { +export let IntentEventParam; + +/** + * Sends intent type event. + * @param {!IntentEventParam} param + */ +export function sendIntentEvent( + {mode, result, shouldHandleResult, shouldDownScale, isSecure}) { const getBoolValue = (b) => b ? '1' : '0'; sendEvent( { eventCategory: 'intent', - eventAction: intent.mode, - eventLabel: intentResult, + eventAction: mode, + eventLabel: result, }, new Map([ - [12, intentResult], - [13, getBoolValue(intent.shouldHandleResult)], - [14, getBoolValue(intent.shouldDownScale)], - [15, getBoolValue(intent.isSecure)], + [12, result], + [13, getBoolValue(shouldHandleResult)], + [14, getBoolValue(shouldDownScale)], + [15, getBoolValue(isSecure)], ])); } /** - * Sends error type event. - * @param {string} type - * @param {string} level - * @param {string} errorName - * @param {string} fileName - * @param {string} funcName - * @param {string} lineNo - * @param {string} colNo + * @typedef {{ + * type: string, + * level: string, + * errorName: string, + * fileName: string, + * funcName: string, + * lineNo: string, + * colNo: string, + * }} */ -function sendErrorEvent( - type, level, errorName, fileName, funcName, lineNo, colNo) { +export let ErrorEventParam; + +/** + * Sends error type event. + * @param {!ErrorEventParam} param + */ +export function sendErrorEvent( + {type, level, errorName, fileName, funcName, lineNo, colNo}) { sendEvent( { eventCategory: 'error', @@ -335,29 +390,3 @@ [20, colNo], ])); } - -/** - * Metrics types. - * @enum {function(...)} - */ -export const Type = { - LAUNCH: sendLaunchEvent, - CAPTURE: sendCaptureEvent, - PERF: sendPerfEvent, - INTENT: sendIntentEvent, - ERROR: sendErrorEvent, -}; - -/** - * Logs the given metrics. - * @param {!Type} type Metrics type. - * @param {...*} args Optional rest parameters for logging metrics. - * @return {!Promise} - */ -export async function log(type, ...args) { - assert(window.ga !== null); - assert(ready !== null); - - await ready; - type(...args); -}
diff --git a/chromeos/components/camera_app_ui/resources/src/js/mojo/device_operator.js b/chromeos/components/camera_app_ui/resources/src/js/mojo/device_operator.js index 906ab974..2b392ac2 100644 --- a/chromeos/components/camera_app_ui/resources/src/js/mojo/device_operator.js +++ b/chromeos/components/camera_app_ui/resources/src/js/mojo/device_operator.js
@@ -366,7 +366,7 @@ /** * Adds a metadata observer to Camera App Device through Mojo IPC. * @param {string} deviceId The id for target camera device. - * @param {!function(cros.mojom.CameraMetadata)} callback Callback that + * @param {function(cros.mojom.CameraMetadata)} callback Callback that * handles the metadata. * @param {cros.mojom.StreamType} streamType Stream type which the observer * gets the metadata from. @@ -408,7 +408,7 @@ * underlying camera HAL after sensor finishes frame capturing. * * @param {string} deviceId The id for target camera device. - * @param {!function()} callback Callback to trigger on shutter done. + * @param {function()} callback Callback to trigger on shutter done. * @return {!Promise<number>} Id for the added observer. * @throws {Error} if fails to construct device connection. */
diff --git a/chromeos/components/camera_app_ui/resources/src/js/perf.js b/chromeos/components/camera_app_ui/resources/src/js/perf.js index 02b1d457..6e4bf3b 100644 --- a/chromeos/components/camera_app_ui/resources/src/js/perf.js +++ b/chromeos/components/camera_app_ui/resources/src/js/perf.js
@@ -25,7 +25,7 @@ }; /** - * @typedef {function(PerfEvent, number, Object=)} + * @typedef {function(PerfEvent, number, !Object=)} */ let PerfEventListener; // eslint-disable-line no-unused-vars
diff --git a/chromeos/components/camera_app_ui/resources/src/js/util.js b/chromeos/components/camera_app_ui/resources/src/js/util.js index 63d8ed6..95597d0 100644 --- a/chromeos/components/camera_app_ui/resources/src/js/util.js +++ b/chromeos/components/camera_app_ui/resources/src/js/util.js
@@ -5,7 +5,7 @@ import {browserProxy} from './browser_proxy/browser_proxy.js'; import * as state from './state.js'; import * as tooltip from './tooltip.js'; -import {Facing, Resolution} from './type.js'; +import {Facing} from './type.js'; /** * Gets the clockwise rotation and flip that can orient a photo to its upright @@ -265,18 +265,6 @@ } /** - * Checks if the window is maximized or fullscreen. - * @return {boolean} True if maximized or fullscreen, false otherwise. - */ -export function isWindowFullSize() { - // App-window's isFullscreen, isMaximized state and window's outer-size may - // not be updated immediately during resizing. Use if app-window's outerBounds - // width matches screen width here as workarounds. - return chrome.app.window.current().outerBounds.width >= screen.width || - chrome.app.window.current().outerBounds.height >= screen.height; -} - -/** * Opens help. */ export function openHelp() { @@ -391,56 +379,6 @@ } /** - * Sets CCA window inner bound to size which can fit in current screen. - * @return {!Promise} Promise which is resolved when size change actually - * happen or is resolved immediately when there is no need to change. - */ -export function fitWindow() { - const appWindow = chrome.app.window.current(); - - /** - * Get a preferred window size which can fit in current screen. - * @return {Resolution} Preferred window size. - */ - const getPreferredWindowSize = () => { - const inner = appWindow.innerBounds; - const outer = appWindow.outerBounds; - - const predefinedWidth = inner.minWidth; - const availableWidth = screen.availWidth; - - const topBarHeight = outer.height - inner.height; - const fixedRatioMaxWidth = - Math.floor((screen.availHeight - topBarHeight) * 16 / 9); - - let preferredWidth = - Math.min(predefinedWidth, availableWidth, fixedRatioMaxWidth); - preferredWidth -= preferredWidth % 16; - const preferredHeight = preferredWidth * 9 / 16; - - return new Resolution(preferredWidth, preferredHeight); - }; - - const {width, height} = getPreferredWindowSize(); - - return new Promise((resolve) => { - const inner = appWindow.innerBounds; - if (inner.width === width && inner.height === height) { - resolve(); - return; - } - - const listener = () => { - appWindow.onBoundsChanged.removeListener(listener); - resolve(); - }; - appWindow.onBoundsChanged.addListener(listener); - - Object.assign(inner, {width, height, minWidth: width, minHeight: height}); - }); -} - -/** * Binds on/off of specified state with different aria label on an element. * @param {!{element: !Element, state: state.State, onLabel: string, * offLabel: string}} params
diff --git a/chromeos/components/camera_app_ui/resources/src/js/views/camera.js b/chromeos/components/camera_app_ui/resources/src/js/views/camera.js index afa3e9c..e6db4ce 100644 --- a/chromeos/components/camera_app_ui/resources/src/js/views/camera.js +++ b/chromeos/components/camera_app_ui/resources/src/js/views/camera.js
@@ -247,7 +247,9 @@ this.start(); } }); - chrome.app.window.current().onMinimized.addListener(() => this.start()); + browserProxy.addOnMinimizedListener(() => { + this.start(); + }); document.addEventListener('visibilitychange', () => { const recording = state.get(state.State.TAKING) && state.get(Mode.VIDEO); @@ -302,7 +304,7 @@ * @return {boolean} */ isSuspended() { - return this.locked_ || chrome.app.window.current().isMinimized() || + return this.locked_ || browserProxy.isMinimized() || state.get(state.State.SUSPEND) || state.get(state.State.SCREEN_OFF_AUTO) || this.isTabletBackground_(); } @@ -372,7 +374,7 @@ * @protected */ async doSavePhoto_({resolution, blob, isVideoSnapshot = false}, name) { - metrics.log(metrics.Type.CAPTURE, { + metrics.sendCaptureEvent({ facing: this.facingMode_, resolution, shutterType: this.shutterType_, @@ -393,7 +395,7 @@ * @protected */ async doSaveVideo_({resolution, duration, videoSaver, everPaused}) { - metrics.log(metrics.Type.CAPTURE, { + metrics.sendCaptureEvent({ facing: this.facingMode_, duration, resolution,
diff --git a/chromeos/components/camera_app_ui/resources/src/js/views/camera/BUILD.gn b/chromeos/components/camera_app_ui/resources/src/js/views/camera/BUILD.gn index 9e559707..32dfe14 100644 --- a/chromeos/components/camera_app_ui/resources/src/js/views/camera/BUILD.gn +++ b/chromeos/components/camera_app_ui/resources/src/js/views/camera/BUILD.gn
@@ -25,6 +25,7 @@ deps = [ "../..:chrome_util", "../..:type", + "../../browser_proxy", ] } @@ -59,6 +60,7 @@ "../..:chrome_util", "../..:nav", "../..:type", + "../../browser_proxy", "//media/capture/video/chromeos/mojom:cros_camera_js_library_for_compile", ] }
diff --git a/chromeos/components/camera_app_ui/resources/src/js/views/camera/layout.js b/chromeos/components/camera_app_ui/resources/src/js/views/camera/layout.js index b903cd3..0b863c5 100644 --- a/chromeos/components/camera_app_ui/resources/src/js/views/camera/layout.js +++ b/chromeos/components/camera_app_ui/resources/src/js/views/camera/layout.js
@@ -2,10 +2,10 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +import {browserProxy} from '../../browser_proxy/browser_proxy.js'; import {assert} from '../../chrome_util.js'; import * as state from '../../state.js'; import {Mode, Resolution} from '../../type.js'; -import * as util from '../../util.js'; /** * CSS rules. @@ -103,10 +103,9 @@ * Updates the layout for video-size or window-size changes. */ update() { - const fullWindow = util.isWindowFullSize(); + const fullWindow = browserProxy.isFullscreenOrMaximized(); const tall = window.innerHeight > window.innerWidth; - const tabletLandscape = fullWindow && !tall; - state.set(state.State.TABLET_LANDSCAPE, tabletLandscape); + state.set(state.State.TABLET_LANDSCAPE, fullWindow && !tall); state.set(state.State.MAX_WND, fullWindow); state.set(state.State.TALL, tall);
diff --git a/chromeos/components/camera_app_ui/resources/src/js/views/camera/modes.js b/chromeos/components/camera_app_ui/resources/src/js/views/camera/modes.js index ea1c1a4..206b36d6 100644 --- a/chromeos/components/camera_app_ui/resources/src/js/views/camera/modes.js +++ b/chromeos/components/camera_app_ui/resources/src/js/views/camera/modes.js
@@ -35,10 +35,10 @@ /** * Contains video recording result. * @typedef {{ - * resolution: {width: number, height: number}, - * duration: number, - * videoSaver: !VideoSaver, - * everPaused: boolean, + * resolution: !Resolution, + * duration: number, + * videoSaver: !VideoSaver, + * everPaused: boolean, * }} */ export let VideoResult; @@ -46,9 +46,9 @@ /** * Contains photo taking result. * @typedef {{ - * resolution: {width: number, height: number}, - * blob: !Blob, - * isVideoSnapshot: (boolean|undefined), + * resolution: !Resolution, + * blob: !Blob, + * isVideoSnapshot: (boolean|undefined), * }} */ export let PhotoResult; @@ -707,7 +707,11 @@ const {width, height} = await util.blobToImage(blob); const imageName = (new Filenamer()).newImageName(); await this.doSaveSnapshot_( - {resolution: {width, height}, blob, isVideoSnapshot: true}, + { + resolution: new Resolution(width, height), + blob, + isVideoSnapshot: true, + }, imageName); }; this.snapshots_.push(doSnapshot); @@ -1192,7 +1196,10 @@ throw e; } const {width, height} = await util.blobToImage(blob); - await this.doSavePhoto_({resolution: {width, height}, blob}, imageName); + await this.doSavePhoto_( + {resolution: new Resolution(width, height), blob}, + imageName, + ); }; const refSave = saveResult(reference, refImageName);
diff --git a/chromeos/components/camera_app_ui/resources/src/js/views/camera/options.js b/chromeos/components/camera_app_ui/resources/src/js/views/camera/options.js index d82cce0..e0e0e30 100644 --- a/chromeos/components/camera_app_ui/resources/src/js/views/camera/options.js +++ b/chromeos/components/camera_app_ui/resources/src/js/views/camera/options.js
@@ -20,7 +20,7 @@ export class Options { /** * @param {!DeviceInfoUpdater} infoUpdater - * @param {!function()} doSwitchDevice Callback to trigger device switching. + * @param {function()} doSwitchDevice Callback to trigger device switching. */ constructor(infoUpdater, doSwitchDevice) { /** @@ -31,7 +31,7 @@ this.infoUpdater_ = infoUpdater; /** - * @type {!function()} + * @type {function()} * @private * @const */
diff --git a/chromeos/components/camera_app_ui/resources/src/js/views/camera/preview.js b/chromeos/components/camera_app_ui/resources/src/js/views/camera/preview.js index 94344ee2..e4785dd4 100644 --- a/chromeos/components/camera_app_ui/resources/src/js/views/camera/preview.js +++ b/chromeos/components/camera_app_ui/resources/src/js/views/camera/preview.js
@@ -2,22 +2,22 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +import {browserProxy} from '../../browser_proxy/browser_proxy.js'; import {assertInstanceof} from '../../chrome_util.js'; import {DeviceOperator, parseMetadata} from '../../mojo/device_operator.js'; import * as nav from '../../nav.js'; import * as state from '../../state.js'; -import * as util from '../../util.js'; /** * Creates a controller for the video preview of Camera view. */ export class Preview { /** - * @param {!function()} onNewStreamNeeded Callback to request new stream. + * @param {function()} onNewStreamNeeded Callback to request new stream. */ constructor(onNewStreamNeeded) { /** - * @type {!function()} + * @type {function()} * @private */ this.onNewStreamNeeded_ = onNewStreamNeeded; @@ -423,10 +423,10 @@ .then(() => { // Resize window by aspect ratio only if it's not maximized or // fullscreen. - if (util.isWindowFullSize()) { + if (browserProxy.isFullscreenOrMaximized()) { return; } - return util.fitWindow(); + return browserProxy.fitWindow(); }); }
diff --git a/chromeos/components/camera_app_ui/resources/src/js/views/camera_intent.js b/chromeos/components/camera_app_ui/resources/src/js/views/camera_intent.js index 45dcc64..bbbe9dd 100644 --- a/chromeos/components/camera_app_ui/resources/src/js/views/camera_intent.js +++ b/chromeos/components/camera_app_ui/resources/src/js/views/camera_intent.js
@@ -159,7 +159,7 @@ } })(); const result = this.photoResult_ || this.videoResult_; - metrics.log(metrics.Type.CAPTURE, { + metrics.sendCaptureEvent({ facing: this.facingMode_, duration: result.duration || undefined, resolution: result.resolution,
diff --git a/chromeos/components/camera_app_ui/resources/src/js/views/settings.js b/chromeos/components/camera_app_ui/resources/src/js/views/settings.js index 6428233..b3e945c9 100644 --- a/chromeos/components/camera_app_ui/resources/src/js/views/settings.js +++ b/chromeos/components/camera_app_ui/resources/src/js/views/settings.js
@@ -51,7 +51,7 @@ export class BaseSettings extends View { /** * @param {ViewName} name Name of the view. - * @param {!Object<string, !function(Event=)>=} itemHandlers Click-handlers + * @param {!Object<string, function(Event=)>=} itemHandlers Click-handlers * mapped by element ids. */ constructor(name, itemHandlers = {}) { @@ -60,7 +60,7 @@ this.root.querySelector('.menu-header button') .addEventListener('click', () => this.leave()); this.root.querySelectorAll('.menu-item').forEach((element) => { - /** @type {!function(Event=)|undefined} */ + /** @type {function(Event=)|undefined} */ const handler = itemHandlers[element.id]; if (handler) { element.addEventListener('click', handler); @@ -145,7 +145,7 @@ * @param {function(): ?DeviceSetting} getSetting * @param {function(): !HTMLElement} getElement * @param {boolean} isPhoto - * @return {!function()} + * @return {function()} */ const createOpenMenuHandler = (getSetting, getElement, isPhoto) => () => { const setting = getSetting(); @@ -423,7 +423,7 @@ * @param {!HTMLElement} item * @param {string} id * @param {!ResolutionConfig} config - * @param {!function(!Resolution, !ResolutionList): string} optTextTempl + * @param {function(!Resolution, !ResolutionList): string} optTextTempl */ const prepItem = (item, id, {prefResol, resols}, optTextTempl) => { item.dataset.deviceId = id; @@ -626,10 +626,10 @@ * Updates resolution menu with specified resolutions. * @param {!HTMLElement} resolItem DOM element holding selected resolution. * @param {!HTMLElement} menu Menu holding all resolution option elements. - * @param {!function(!Resolution, !ResolutionList): string} optTextTempl + * @param {function(!Resolution, !ResolutionList): string} optTextTempl * Template generating text content for each resolution option from its * width and height. - * @param {!function(!Resolution)} onChange Called when selected option + * @param {function(!Resolution)} onChange Called when selected option * changed with resolution of newly selected option. * @param {!ResolutionList} resolutions Resolutions of its width and height to * be updated with.
diff --git a/chromeos/constants/chromeos_features.cc b/chromeos/constants/chromeos_features.cc index 38261ca..0a83644 100644 --- a/chromeos/constants/chromeos_features.cc +++ b/chromeos/constants/chromeos_features.cc
@@ -128,6 +128,10 @@ const base::Feature kCrostiniWebUIUpgrader{"CrostiniWebUIUpgrader", base::FEATURE_ENABLED_BY_DEFAULT}; +// Enables or disables use of DLC instead of the component updater. +const base::Feature kCrostiniUseDlc{"CrostiniUseDlc", + base::FEATURE_DISABLED_BY_DEFAULT}; + // Enables or disables using Cryptauth's GetDevicesActivityStatus API. const base::Feature kCryptAuthV2DeviceActivityStatus{ "CryptAuthV2DeviceActivityStatus", base::FEATURE_DISABLED_BY_DEFAULT};
diff --git a/chromeos/constants/chromeos_features.h b/chromeos/constants/chromeos_features.h index b582291..411dcd7 100644 --- a/chromeos/constants/chromeos_features.h +++ b/chromeos/constants/chromeos_features.h
@@ -62,6 +62,8 @@ COMPONENT_EXPORT(CHROMEOS_CONSTANTS) extern const base::Feature kCrostiniShowMicSetting; COMPONENT_EXPORT(CHROMEOS_CONSTANTS) +extern const base::Feature kCrostiniUseDlc; +COMPONENT_EXPORT(CHROMEOS_CONSTANTS) extern const base::Feature kDisableCryptAuthV1DeviceSync; COMPONENT_EXPORT(CHROMEOS_CONSTANTS) extern const base::Feature kCryptAuthV2DeviceActivityStatus;
diff --git a/components/arc/mojom/notifications.mojom b/components/arc/mojom/notifications.mojom index 7a79608..248c550 100644 --- a/components/arc/mojom/notifications.mojom +++ b/components/arc/mojom/notifications.mojom
@@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. // -// Next MinVersion: 25 +// Next MinVersion: 26 module arc.mojom; @@ -201,6 +201,15 @@ bool expansion_animation; }; +// These values represent an visibility of MessageCenter on Chrome OS. +[Extensible, MinVersion=25] +enum MessageCenterVisibility { + // When nothing or just toast popups are being displayed. + VISIBILITY_TRANSIENT = 0, + // When the message center view is being displayed. + VISIBILITY_MESSAGE_CENTER = 1, +}; + // Next Method ID: 11 interface NotificationsHost { // Set the Do-Not-Disturb status on Chrome side from Android side. @@ -239,7 +248,7 @@ OnLockScreenSettingUpdated@10(ArcLockScreenNotificationSetting setting); }; -// Next Method ID: 13 +// Next Method ID: 14 // TODO(lhchavez): Migrate all request/response messages to Mojo. interface NotificationsInstance { // DEPRECATED: Please use Init@5 instead. @@ -302,4 +311,8 @@ // Set configuration variables for notifications. [MinVersion=22] SetNotificationConfiguration@12(NotificationConfiguration configuration); + + // Reports that Message Center visibility has changed. + [MinVersion=25] + OnMessageCenterVisibilityChanged@13(MessageCenterVisibility visibility); };
diff --git a/components/arc/test/fake_notifications_instance.cc b/components/arc/test/fake_notifications_instance.cc index 8af4b4c..ee093b4 100644 --- a/components/arc/test/fake_notifications_instance.cc +++ b/components/arc/test/fake_notifications_instance.cc
@@ -64,5 +64,7 @@ mojom::ArcLockScreenNotificationSettingPtr setting) {} void FakeNotificationsInstance::SetNotificationConfiguration( mojom::NotificationConfigurationPtr configuration) {} +void FakeNotificationsInstance::OnMessageCenterVisibilityChanged( + mojom::MessageCenterVisibility visibility) {} } // namespace arc
diff --git a/components/arc/test/fake_notifications_instance.h b/components/arc/test/fake_notifications_instance.h index 00a706e..144fd9b 100644 --- a/components/arc/test/fake_notifications_instance.h +++ b/components/arc/test/fake_notifications_instance.h
@@ -41,6 +41,8 @@ mojom::ArcLockScreenNotificationSettingPtr setting) override; void SetNotificationConfiguration( mojom::NotificationConfigurationPtr configuration) override; + void OnMessageCenterVisibilityChanged( + mojom::MessageCenterVisibility visibility) override; const std::vector<std::pair<std::string, mojom::ArcNotificationEvent>>& events() const;
diff --git a/components/favicon/content/BUILD.gn b/components/favicon/content/BUILD.gn index 389a29c4..99ea6d5 100644 --- a/components/favicon/content/BUILD.gn +++ b/components/favicon/content/BUILD.gn
@@ -17,7 +17,6 @@ deps = [ "//base", "//components/favicon_base", - "//components/history/core/browser", "//content/public/browser", "//content/public/common", "//url",
diff --git a/components/favicon/core/favicon_backend.cc b/components/favicon/core/favicon_backend.cc index 5458bc7..cd4c59c4 100644 --- a/components/favicon/core/favicon_backend.cc +++ b/components/favicon/core/favicon_backend.cc
@@ -182,7 +182,7 @@ std::vector<favicon_base::FaviconRawBitmapResult> bitmap_results = GetFaviconsFromDB(page_url, icon_types, desired_sizes, fallback_to_host); - if (desired_sizes.size() == 1) { + if (desired_sizes.size() == 1 && !bitmap_results.empty()) { bitmap_results.assign(1, favicon_base::ResizeFaviconBitmapResult( bitmap_results, desired_sizes[0])); } @@ -193,17 +193,13 @@ FaviconBackend::GetFaviconForId(favicon_base::FaviconID favicon_id, int desired_size) { TRACE_EVENT0("browser", "FaviconBackend::GetFaviconForID"); - std::vector<favicon_base::FaviconID> favicon_ids; - favicon_ids.push_back(favicon_id); - std::vector<int> desired_sizes; - desired_sizes.push_back(desired_size); - // Get results from DB. std::vector<favicon_base::FaviconRawBitmapResult> bitmap_results = - GetFaviconBitmapResultsForBestMatch(favicon_ids, desired_sizes); - - bitmap_results.assign( - 1, favicon_base::ResizeFaviconBitmapResult(bitmap_results, desired_size)); + GetFaviconBitmapResultsForBestMatch({favicon_id}, {desired_size}); + if (!bitmap_results.empty()) { + bitmap_results.assign(1, favicon_base::ResizeFaviconBitmapResult( + bitmap_results, desired_size)); + } return bitmap_results; }
diff --git a/components/favicon/core/favicon_backend_unittest.cc b/components/favicon/core/favicon_backend_unittest.cc index f71c985..a2b7aab 100644 --- a/components/favicon/core/favicon_backend_unittest.cc +++ b/components/favicon/core/favicon_backend_unittest.cc
@@ -43,14 +43,6 @@ const gfx::Size kSmallSize = gfx::Size(kSmallEdgeSize, kSmallEdgeSize); const gfx::Size kLargeSize = gfx::Size(kLargeEdgeSize, kLargeEdgeSize); -// Used to test if a vector of FaviconRawBitmapResult is empty. This is -// necessitated by GetFaviconsForUrl() potentially returning a non-empty -// vector with a single empty entry. -bool IsBitmapResultsEmpty( - const std::vector<favicon_base::FaviconRawBitmapResult>& results) { - return results.empty() || (results.size() == 1 && !results[0].is_valid()); -} - } // namespace class FaviconBackendTest : public testing::Test, public FaviconBackendDelegate { @@ -1171,7 +1163,7 @@ {IconType::kFavicon, IconType::kTouchIcon}, {kSmallEdgeSize}, false); - EXPECT_TRUE(IsBitmapResultsEmpty(bitmap_results_out)); + EXPECT_TRUE(bitmap_results_out.empty()); // Querying for the http URL with |fallback_to_host|=true should not return // the favicon associated with a different host, even when that host has the @@ -1180,7 +1172,7 @@ page_url_http, {IconType::kFavicon, IconType::kTouchIcon}, {kSmallEdgeSize}, true); - EXPECT_TRUE(IsBitmapResultsEmpty(bitmap_results_out)); + EXPECT_TRUE(bitmap_results_out.empty()); } SetFavicons({page_url_https}, IconType::kFavicon, icon_url3, @@ -1193,7 +1185,7 @@ {IconType::kFavicon, IconType::kTouchIcon}, {kSmallEdgeSize}, false); - EXPECT_TRUE(IsBitmapResultsEmpty(bitmap_results_out)); + EXPECT_TRUE(bitmap_results_out.empty()); // Querying for the http URL with |fallback_to_host|=true returns the // favicon associated with the https URL. @@ -1213,13 +1205,13 @@ {IconType::kFavicon, IconType::kTouchIcon}, {kSmallEdgeSize}, false); - EXPECT_TRUE(IsBitmapResultsEmpty(bitmap_results_out)); + EXPECT_TRUE(bitmap_results_out.empty()); bitmap_results_out = backend_->GetFaviconsForUrl( page_url_different_scheme, {IconType::kFavicon, IconType::kTouchIcon}, {kSmallEdgeSize}, true); - EXPECT_TRUE(IsBitmapResultsEmpty(bitmap_results_out)); + EXPECT_TRUE(bitmap_results_out.empty()); } }
diff --git a/components/policy/resources/policy_templates.json b/components/policy/resources/policy_templates.json index f60a5c4..709f3b6 100644 --- a/components/policy/resources/policy_templates.json +++ b/components/policy/resources/policy_templates.json
@@ -22570,6 +22570,33 @@ If the policy is enabled or unset, a full page warning will be shown when an insecure form is submitted. Additionally, a warning bubble will be shown next to the form fields when they are focused, and autofill will be disabled for those forms. If the policy is disabled, warnings will not be shown for insecure forms, and autofill will work normally.''', }, + { + 'name': 'CCTToSDialogEnabled', + 'owners': ['skym@chromium.org', 'wenyufu@chromium.org', 'twellington@chromium.org'], + 'type': 'main', + 'schema': { 'type': 'boolean' }, + 'supported_on': [], + 'future_on': ['android'], + 'features': { + 'dynamic_refresh': False, + 'per_profile': False, + }, + 'example_value': True, + 'id': 760, + 'caption': 'Enable ToS during first-run for CCT/PWA', + 'tags': [], + 'desc': '''By default the Terms of Service are shown when CCT/PWA is first-run. Setting this policy to Disabled will cause the Terms of Service dialog to not appear during the first-run-experience or subsequent runs. Setting this policy to Enabled or leaving it unset will cause the Terms of Service dialog to appear during the first-run-experience. The other caveats are: + + - This policy only works on fully managed Android devices that can be configured by Unified Endpoint Management vendors. + + - If this policy is Disabled the BrowserSignin policy will have no effect. + + - If this policy is Disabled metrics​ will not be sent to the server. + + - If this policy is Disabled the browser will have limited functionality. + + - If this policy is Disabled admins must communicate this to end users of the device.''', + }, ], 'messages': { @@ -23478,6 +23505,6 @@ ], 'placeholders': [], 'deleted_policy_ids': [412, 476, 546, 562, 569, 578], - 'highest_id_currently_used': 759, + 'highest_id_currently_used': 760, 'highest_atomic_group_id_currently_used': 39 }
diff --git a/components/send_tab_to_self/send_tab_to_self_bridge_unittest.cc b/components/send_tab_to_self/send_tab_to_self_bridge_unittest.cc index 021c38d..8f1d68eb 100644 --- a/components/send_tab_to_self/send_tab_to_self_bridge_unittest.cc +++ b/components/send_tab_to_self/send_tab_to_self_bridge_unittest.cc
@@ -77,7 +77,8 @@ sync_pb::SyncEnums_DeviceType_TYPE_LINUX, "scoped_id", "manufacturer", "model", last_updated_timestamp, syncer::DeviceInfoUtil::GetPulseInterval(), - send_tab_to_self_receiving_enabled, /*sharing_info=*/base::nullopt); + send_tab_to_self_receiving_enabled, /*sharing_info=*/base::nullopt, + /*fcm_registration_token=*/std::string()); } sync_pb::ModelTypeState StateWithEncryption(
diff --git a/components/send_tab_to_self/target_device_info_unittest.cc b/components/send_tab_to_self/target_device_info_unittest.cc index 728a252..51db823 100644 --- a/components/send_tab_to_self/target_device_info_unittest.cc +++ b/components/send_tab_to_self/target_device_info_unittest.cc
@@ -40,7 +40,8 @@ {"vapid_fcm_token", "vapid_p256dh", "vapid_auth_secret"}, {"sender_id_fcm_token", "sender_id_p256dh", "sender_id_auth_secret"}, std::set<sync_pb::SharingSpecificFields::EnabledFeatures>{ - sync_pb::SharingSpecificFields::CLICK_TO_CALL_V2})); + sync_pb::SharingSpecificFields::CLICK_TO_CALL_V2}), + /*fcm_registration_token=*/std::string()); } } // namespace
diff --git a/components/sync/driver/glue/sync_engine_backend.cc b/components/sync/driver/glue/sync_engine_backend.cc index 8741ca3..aa09eb9 100644 --- a/components/sync/driver/glue/sync_engine_backend.cc +++ b/components/sync/driver/glue/sync_engine_backend.cc
@@ -57,6 +57,31 @@ const base::FilePath::CharType kNigoriStorageFilename[] = FILE_PATH_LITERAL("Nigori.bin"); +class SyncInvalidationAdapter : public InvalidationInterface { + public: + explicit SyncInvalidationAdapter(const std::string& payload) + : payload_(payload) {} + ~SyncInvalidationAdapter() override = default; + + bool IsUnknownVersion() const override { return true; } + + const std::string& GetPayload() const override { return payload_; } + + int64_t GetVersion() const override { + // TODO(crbug.com/1102322): implement versions. This method is not called + // until IsUnknownVersion() returns true. + NOTREACHED(); + return 0; + } + + void Acknowledge() override { NOTIMPLEMENTED(); } + + void Drop() override { NOTIMPLEMENTED(); } + + private: + const std::string payload_; +}; + } // namespace SyncEngineBackend::SyncEngineBackend(const std::string& name, @@ -567,9 +592,8 @@ // TODO(crbug.com/1102322): use data types from active or from payload. const ModelTypeSet active_datatypes{ModelType::BOOKMARKS}; for (const ModelType type : active_datatypes) { - // TODO(crbug.com/1082122): implement new InvalidationInterface for new - // invalidations. - std::unique_ptr<InvalidationInterface> inv_adapter; + std::unique_ptr<InvalidationInterface> inv_adapter = + std::make_unique<SyncInvalidationAdapter>(payload); sync_manager_->OnIncomingInvalidation(type, std::move(inv_adapter)); } }
diff --git a/components/sync/invalidations/BUILD.gn b/components/sync/invalidations/BUILD.gn index c2d903f..1e18435 100644 --- a/components/sync/invalidations/BUILD.gn +++ b/components/sync/invalidations/BUILD.gn
@@ -8,6 +8,7 @@ sources = [ "fcm_handler.cc", "fcm_handler.h", + "fcm_registration_token_observer.h", "invalidations_listener.h", "switches.cc", "switches.h",
diff --git a/components/sync/invalidations/fcm_handler.cc b/components/sync/invalidations/fcm_handler.cc index f08feb8..e024f2e 100644 --- a/components/sync/invalidations/fcm_handler.cc +++ b/components/sync/invalidations/fcm_handler.cc
@@ -10,6 +10,7 @@ #include "base/time/time.h" #include "components/gcm_driver/gcm_driver.h" #include "components/gcm_driver/instance_id/instance_id_driver.h" +#include "components/sync/invalidations/fcm_registration_token_observer.h" #include "components/sync/invalidations/invalidations_listener.h" namespace syncer { @@ -27,7 +28,6 @@ FCMHandler::~FCMHandler() { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - DCHECK(listeners_.empty()); StopListening(); } @@ -63,19 +63,27 @@ void FCMHandler::AddListener(InvalidationsListener* listener) { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - DCHECK(listener); - listeners_.push_back(listener); + listeners_.AddObserver(listener); } void FCMHandler::RemoveListener(InvalidationsListener* listener) { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - base::Erase(listeners_, listener); + listeners_.RemoveObserver(listener); } void FCMHandler::OnStoreReset() { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - // Currently the FCM registration token is not stored and there is nothing to - // do. + // The FCM registration token is not stored by FCMHandler. +} + +void FCMHandler::AddTokenObserver(FCMRegistrationTokenObserver* observer) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + token_observers_.AddObserver(observer); +} + +void FCMHandler::RemoveTokenObserver(FCMRegistrationTokenObserver* observer) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + token_observers_.RemoveObserver(observer); } void FCMHandler::OnMessage(const std::string& app_id, @@ -89,8 +97,8 @@ payload = it->second; } - for (InvalidationsListener* listener : listeners_) { - listener->OnInvalidationReceived(payload); + for (InvalidationsListener& listener : listeners_) { + listener.OnInvalidationReceived(payload); } } @@ -123,11 +131,19 @@ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); // TODO(crbug.com/1108783): add a UMA histogram to monitor results. if (result == instance_id::InstanceID::SUCCESS) { + if (fcm_registration_token_ == subscription_token) { + // Nothing has changed, do not notify observers. + return; + } + fcm_registration_token_ = subscription_token; - return; + for (FCMRegistrationTokenObserver& token_observer : token_observers_) { + token_observer.OnFCMRegistrationTokenChanged(); + } + } else { + DLOG(WARNING) << "Messaging subscription failed: " << result; } - DLOG(WARNING) << "Messaging subscription failed: " << result; // TODO(crbug.com/1102336): schedule next token validation. }
diff --git a/components/sync/invalidations/fcm_handler.h b/components/sync/invalidations/fcm_handler.h index 6aef52cf..1f0672e 100644 --- a/components/sync/invalidations/fcm_handler.h +++ b/components/sync/invalidations/fcm_handler.h
@@ -8,6 +8,7 @@ #include <string> #include <vector> +#include "base/observer_list.h" #include "base/sequence_checker.h" #include "components/gcm_driver/gcm_app_handler.h" #include "components/gcm_driver/instance_id/instance_id.h" @@ -23,6 +24,7 @@ namespace syncer { +class FCMRegistrationTokenObserver; class InvalidationsListener; // This handler is used to register with FCM and to process incoming messages. @@ -51,6 +53,10 @@ void AddListener(InvalidationsListener* listener); void RemoveListener(InvalidationsListener* listener); + // Add or remove an FCM token change observer. |observer| must not be nullptr. + void AddTokenObserver(FCMRegistrationTokenObserver* observer); + void RemoveTokenObserver(FCMRegistrationTokenObserver* observer); + // Used to get an obtained FCM token. Returns empty string if it hasn't // received yet. const std::string& GetFCMRegistrationToken() const; @@ -83,7 +89,18 @@ // Contains an FCM registration token if not empty. std::string fcm_registration_token_; - std::vector<InvalidationsListener*> listeners_; + // Contains all listeners to notify about each incoming message in OnMessage + // method. + base::ObserverList<InvalidationsListener, + /*check_empty=*/true, + /*allow_reentrancy=*/false> + listeners_; + + // Contains all FCM token observers to notify about each token change. + base::ObserverList<FCMRegistrationTokenObserver, + /*check_empty=*/true, + /*allow_reentrancy=*/false> + token_observers_; base::WeakPtrFactory<FCMHandler> weak_ptr_factory_{this}; };
diff --git a/components/sync/invalidations/fcm_handler_unittest.cc b/components/sync/invalidations/fcm_handler_unittest.cc index 75c0649..158270d 100644 --- a/components/sync/invalidations/fcm_handler_unittest.cc +++ b/components/sync/invalidations/fcm_handler_unittest.cc
@@ -15,6 +15,7 @@ #include "components/gcm_driver/fake_gcm_driver.h" #include "components/gcm_driver/gcm_driver.h" #include "components/gcm_driver/instance_id/instance_id_driver.h" +#include "components/sync/invalidations/fcm_registration_token_observer.h" #include "components/sync/invalidations/invalidations_listener.h" #include "google_apis/gcm/engine/account_mapping.h" #include "testing/gmock/include/gmock/gmock.h" @@ -76,6 +77,11 @@ MOCK_METHOD1(OnInvalidationReceived, void(const std::string& payload)); }; +class MockTokenObserver : public FCMRegistrationTokenObserver { + public: + MOCK_METHOD0(OnFCMRegistrationTokenChanged, void()); +}; + class FCMHandlerTest : public testing::Test { public: FCMHandlerTest() @@ -123,5 +129,22 @@ fcm_handler_.RemoveListener(&mock_listener); } +TEST_F(FCMHandlerTest, ShouldNotifyOnTokenChange) { + NiceMock<MockTokenObserver> mock_token_observer; + fcm_handler_.AddTokenObserver(&mock_token_observer); + + // Check that the handler gets the token through GetToken. + ON_CALL(mock_instance_id_, GetToken(_, _, _, _, _, _)) + .WillByDefault( + WithArg<5>(Invoke([](InstanceID::GetTokenCallback callback) { + std::move(callback).Run("token", InstanceID::Result::SUCCESS); + }))); + + EXPECT_CALL(mock_token_observer, OnFCMRegistrationTokenChanged()); + fcm_handler_.StartListening(); + + fcm_handler_.RemoveTokenObserver(&mock_token_observer); +} + } // namespace } // namespace syncer
diff --git a/components/sync/invalidations/fcm_registration_token_observer.h b/components/sync/invalidations/fcm_registration_token_observer.h new file mode 100644 index 0000000..8474a6cb --- /dev/null +++ b/components/sync/invalidations/fcm_registration_token_observer.h
@@ -0,0 +1,21 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef COMPONENTS_SYNC_INVALIDATIONS_FCM_REGISTRATION_TOKEN_OBSERVER_H_ +#define COMPONENTS_SYNC_INVALIDATIONS_FCM_REGISTRATION_TOKEN_OBSERVER_H_ + +#include "base/observer_list.h" + +namespace syncer { + +// An interface to observe changes on FCM registration token. +class FCMRegistrationTokenObserver : public base::CheckedObserver { + public: + // Called on each change of FCM registration token. + virtual void OnFCMRegistrationTokenChanged() = 0; +}; + +} // namespace syncer + +#endif // COMPONENTS_SYNC_INVALIDATIONS_FCM_REGISTRATION_TOKEN_OBSERVER_H_
diff --git a/components/sync/invalidations/invalidations_listener.h b/components/sync/invalidations/invalidations_listener.h index ffde717..c61c61e7 100644 --- a/components/sync/invalidations/invalidations_listener.h +++ b/components/sync/invalidations/invalidations_listener.h
@@ -7,13 +7,13 @@ #include <string> +#include "base/observer_list.h" + namespace syncer { // This class provides an interface to handle received invalidations. -class InvalidationsListener { +class InvalidationsListener : public base::CheckedObserver { public: - virtual ~InvalidationsListener() = default; - // Called on each invalidation. |payload| is passed as is without any parsing. virtual void OnInvalidationReceived(const std::string& payload) = 0; };
diff --git a/components/sync/invalidations/mock_sync_invalidations_service.h b/components/sync/invalidations/mock_sync_invalidations_service.h index fd98d07..fa22a915a 100644 --- a/components/sync/invalidations/mock_sync_invalidations_service.h +++ b/components/sync/invalidations/mock_sync_invalidations_service.h
@@ -5,6 +5,8 @@ #ifndef COMPONENTS_SYNC_INVALIDATIONS_MOCK_SYNC_INVALIDATIONS_SERVICE_H_ #define COMPONENTS_SYNC_INVALIDATIONS_MOCK_SYNC_INVALIDATIONS_SERVICE_H_ +#include <string> + #include "components/sync/invalidations/sync_invalidations_service.h" #include "testing/gmock/include/gmock/gmock.h" @@ -17,6 +19,13 @@ MOCK_METHOD(void, AddListener, (InvalidationsListener * listener)); MOCK_METHOD(void, RemoveListener, (InvalidationsListener * listener)); + MOCK_METHOD(void, + AddTokenObserver, + (FCMRegistrationTokenObserver * observer)); + MOCK_METHOD(void, + RemoveTokenObserver, + (FCMRegistrationTokenObserver * observer)); + MOCK_METHOD(const std::string&, GetFCMRegistrationToken, (), (const)); }; } // namespace syncer
diff --git a/components/sync/invalidations/sync_invalidations_service.h b/components/sync/invalidations/sync_invalidations_service.h index 46137d9..faa3c28 100644 --- a/components/sync/invalidations/sync_invalidations_service.h +++ b/components/sync/invalidations/sync_invalidations_service.h
@@ -5,9 +5,12 @@ #ifndef COMPONENTS_SYNC_INVALIDATIONS_SYNC_INVALIDATIONS_SERVICE_H_ #define COMPONENTS_SYNC_INVALIDATIONS_SYNC_INVALIDATIONS_SERVICE_H_ +#include <string> + #include "components/keyed_service/core/keyed_service.h" namespace syncer { +class FCMRegistrationTokenObserver; class InvalidationsListener; // Service which is used to register with FCM. It is used to obtain an FCM token @@ -22,6 +25,14 @@ // |listener| then RemoveListener will do nothing. virtual void AddListener(InvalidationsListener* listener) = 0; virtual void RemoveListener(InvalidationsListener* listener) = 0; + + // Add or remove an FCM token change observer. |observer| must not be nullptr. + virtual void AddTokenObserver(FCMRegistrationTokenObserver* observer) = 0; + virtual void RemoveTokenObserver(FCMRegistrationTokenObserver* observer) = 0; + + // Used to get an obtained FCM token. Returns empty string if it hasn't been + // received yet. + virtual const std::string& GetFCMRegistrationToken() const = 0; }; } // namespace syncer
diff --git a/components/sync/invalidations/sync_invalidations_service_impl.cc b/components/sync/invalidations/sync_invalidations_service_impl.cc index 275a2ce7..52650155 100644 --- a/components/sync/invalidations/sync_invalidations_service_impl.cc +++ b/components/sync/invalidations/sync_invalidations_service_impl.cc
@@ -30,6 +30,21 @@ fcm_handler_->RemoveListener(listener); } +void SyncInvalidationsServiceImpl::AddTokenObserver( + FCMRegistrationTokenObserver* observer) { + fcm_handler_->AddTokenObserver(observer); +} + +void SyncInvalidationsServiceImpl::RemoveTokenObserver( + FCMRegistrationTokenObserver* observer) { + fcm_handler_->RemoveTokenObserver(observer); +} + +const std::string& SyncInvalidationsServiceImpl::GetFCMRegistrationToken() + const { + return fcm_handler_->GetFCMRegistrationToken(); +} + void SyncInvalidationsServiceImpl::Shutdown() { fcm_handler_.reset(); }
diff --git a/components/sync/invalidations/sync_invalidations_service_impl.h b/components/sync/invalidations/sync_invalidations_service_impl.h index 75c1c7c..e2d1a59 100644 --- a/components/sync/invalidations/sync_invalidations_service_impl.h +++ b/components/sync/invalidations/sync_invalidations_service_impl.h
@@ -35,6 +35,9 @@ // SyncInvalidationsService implementation. void AddListener(InvalidationsListener* listener) override; void RemoveListener(InvalidationsListener* listener) override; + void AddTokenObserver(FCMRegistrationTokenObserver* observer) override; + void RemoveTokenObserver(FCMRegistrationTokenObserver* observer) override; + const std::string& GetFCMRegistrationToken() const override; // KeyedService overrides. void Shutdown() override;
diff --git a/components/sync_device_info/BUILD.gn b/components/sync_device_info/BUILD.gn index 846f782..6f4a7d1 100644 --- a/components/sync_device_info/BUILD.gn +++ b/components/sync_device_info/BUILD.gn
@@ -41,6 +41,7 @@ public_deps = [ "//base", "//components/sync", + "//components/sync/invalidations", "//components/sync/protocol", ] deps = [ @@ -102,6 +103,7 @@ "//base/test:test_support", "//components/prefs:test_support", "//components/sync:test_support", + "//components/sync/invalidations:test_support", "//components/version_info:version_string", "//testing/gmock", "//testing/gtest",
diff --git a/components/sync_device_info/DEPS b/components/sync_device_info/DEPS index 4ec77d8..521b613 100644 --- a/components/sync_device_info/DEPS +++ b/components/sync_device_info/DEPS
@@ -4,6 +4,7 @@ "+components/metrics", "+components/prefs", "+components/sync/base", + "+components/sync/invalidations", "+components/sync/model", "+components/sync/model_impl", "+components/sync/protocol",
diff --git a/components/sync_device_info/device_info.cc b/components/sync_device_info/device_info.cc index 665fed5..3a780ba 100644 --- a/components/sync_device_info/device_info.cc +++ b/components/sync_device_info/device_info.cc
@@ -48,7 +48,8 @@ base::Time last_updated_timestamp, base::TimeDelta pulse_interval, bool send_tab_to_self_receiving_enabled, - const base::Optional<SharingInfo>& sharing_info) + const base::Optional<SharingInfo>& sharing_info, + const std::string& fcm_registration_token) : guid_(guid), client_name_(client_name), chrome_version_(chrome_version), @@ -60,7 +61,8 @@ last_updated_timestamp_(last_updated_timestamp), pulse_interval_(pulse_interval), send_tab_to_self_receiving_enabled_(send_tab_to_self_receiving_enabled), - sharing_info_(sharing_info) {} + sharing_info_(sharing_info), + fcm_registration_token_(fcm_registration_token) {} DeviceInfo::~DeviceInfo() {} @@ -153,6 +155,10 @@ } } +const std::string& DeviceInfo::fcm_registration_token() const { + return fcm_registration_token_; +} + bool DeviceInfo::Equals(const DeviceInfo& other) const { return this->guid() == other.guid() && this->client_name() == other.client_name() && @@ -164,7 +170,8 @@ this->model_name() == other.model_name() && this->send_tab_to_self_receiving_enabled() == other.send_tab_to_self_receiving_enabled() && - this->sharing_info() == other.sharing_info(); + this->sharing_info() == other.sharing_info() && + this->fcm_registration_token() == other.fcm_registration_token(); } std::unique_ptr<base::DictionaryValue> DeviceInfo::ToValue() const { @@ -198,4 +205,8 @@ client_name_ = client_name; } +void DeviceInfo::set_fcm_registration_token(const std::string& fcm_token) { + fcm_registration_token_ = fcm_token; +} + } // namespace syncer
diff --git a/components/sync_device_info/device_info.h b/components/sync_device_info/device_info.h index 3e41001b..47d3c7b 100644 --- a/components/sync_device_info/device_info.h +++ b/components/sync_device_info/device_info.h
@@ -73,7 +73,8 @@ base::Time last_updated_timestamp, base::TimeDelta pulse_interval, bool send_tab_to_self_receiving_enabled, - const base::Optional<SharingInfo>& sharing_info); + const base::Optional<SharingInfo>& sharing_info, + const std::string& fcm_registration_token); ~DeviceInfo(); // Sync specific unique identifier for the device. Note if a device @@ -123,6 +124,9 @@ // Returns Sharing related info of the device. const base::Optional<SharingInfo>& sharing_info() const; + // Returns the FCM registration token for sync invalidations. + const std::string& fcm_registration_token() const; + // Gets the OS in string form. std::string GetOSString() const; @@ -144,6 +148,8 @@ void set_client_name(const std::string& client_name); + void set_fcm_registration_token(const std::string& fcm_token); + // Converts the |DeviceInfo| values to a JS friendly DictionaryValue, // which extension APIs can expose to third party apps. std::unique_ptr<base::DictionaryValue> ToValue() const; @@ -179,6 +185,9 @@ base::Optional<SharingInfo> sharing_info_; + // An FCM registration token obtained by sync invalidations service. + std::string fcm_registration_token_; + DISALLOW_COPY_AND_ASSIGN(DeviceInfo); };
diff --git a/components/sync_device_info/device_info_sync_bridge.cc b/components/sync_device_info/device_info_sync_bridge.cc index 62430da..a62efcf 100644 --- a/components/sync_device_info/device_info_sync_bridge.cc +++ b/components/sync_device_info/device_info_sync_bridge.cc
@@ -95,7 +95,8 @@ ProtoTimeToTime(specifics.last_updated_timestamp()), GetPulseIntervalFromSpecifics(specifics), specifics.feature_fields().send_tab_to_self_receiving_enabled(), - SpecificsToSharingInfo(specifics)); + SpecificsToSharingInfo(specifics), + specifics.invalidation_fields().instance_id_token()); } // Allocate a EntityData and copies |specifics| into it. @@ -150,6 +151,12 @@ } } + // Set sync invalidations FCM registration token. + if (!info.fcm_registration_token().empty()) { + specifics->mutable_invalidation_fields()->set_instance_id_token( + info.fcm_registration_token()); + } + return specifics; }
diff --git a/components/sync_device_info/device_info_sync_bridge_unittest.cc b/components/sync_device_info/device_info_sync_bridge_unittest.cc index acf0ecc..39c86c3 100644 --- a/components/sync_device_info/device_info_sync_bridge_unittest.cc +++ b/components/sync_device_info/device_info_sync_bridge_unittest.cc
@@ -12,10 +12,12 @@ #include "base/run_loop.h" #include "base/strings/stringprintf.h" #include "base/test/bind_test_util.h" +#include "base/test/scoped_feature_list.h" #include "base/test/simple_test_clock.h" #include "base/test/task_environment.h" #include "components/prefs/testing_pref_service.h" #include "components/sync/base/time.h" +#include "components/sync/invalidations/switches.h" #include "components/sync/model/data_batch.h" #include "components/sync/model/data_type_activation_request.h" #include "components/sync/model/data_type_error_handler_mock.h" @@ -103,7 +105,9 @@ expected_specifics.manufacturer() == arg.manufacturer_name() && expected_specifics.feature_fields() .send_tab_to_self_receiving_enabled() == - arg.send_tab_to_self_receiving_enabled(); + arg.send_tab_to_self_receiving_enabled() && + expected_specifics.invalidation_fields().instance_id_token() == + arg.fcm_registration_token(); } Matcher<std::unique_ptr<EntityData>> HasSpecifics( @@ -129,6 +133,15 @@ return true; } +MATCHER(HasInstanceIdToken, "") { + const sync_pb::DeviceInfoSpecifics& specifics = arg.device_info(); + if (specifics.invalidation_fields().instance_id_token().empty()) { + *result_listener << "which is empty"; + return false; + } + return true; +} + std::string CacheGuidForSuffix(int suffix) { return base::StringPrintf("cache guid %d", suffix); } @@ -199,6 +212,13 @@ : sync_pb::SharingSpecificFields::SHARED_CLIPBOARD_V2; } +std::string SyncInvalidationsInstanceIdTokenForSuffix(int suffix) { + if (base::FeatureList::IsEnabled(switches::kSubscribeForSyncInvalidations)) { + return base::StringPrintf("instance id token %d", suffix); + } + return std::string(); +} + DataTypeActivationRequest TestDataTypeActivationRequest(SyncMode sync_mode) { DataTypeActivationRequest request; request.cache_guid = CacheGuidForSuffix(kLocalSuffix); @@ -235,6 +255,13 @@ SharingSenderIdAuthSecretForSuffix(suffix)); specifics.mutable_sharing_fields()->add_enabled_features( SharingEnabledFeaturesForSuffix(suffix)); + + const std::string sync_invalidations_instance_id_token = + SyncInvalidationsInstanceIdTokenForSuffix(suffix); + if (!sync_invalidations_instance_id_token.empty()) { + specifics.mutable_invalidation_fields()->set_instance_id_token( + sync_invalidations_instance_id_token); + } return specifics; } @@ -306,7 +333,8 @@ {SharingSenderIdFcmTokenForSuffix(kLocalSuffix), SharingSenderIdP256dhForSuffix(kLocalSuffix), SharingSenderIdAuthSecretForSuffix(kLocalSuffix)}, - sharing_enabled_features)); + sharing_enabled_features), + SyncInvalidationsInstanceIdTokenForSuffix(kLocalSuffix)); } void Clear() override { local_device_info_.reset(); } @@ -1233,6 +1261,15 @@ ASSERT_EQ(expected_device_name_full_sync, device->client_name()); } +TEST_F(DeviceInfoSyncBridgeTest, ShouldSendInvalidationFields) { + base::test::ScopedFeatureList override_features; + override_features.InitAndEnableFeature( + switches::kSubscribeForSyncInvalidations); + + EXPECT_CALL(*processor(), Put(_, HasSpecifics(HasInstanceIdToken()), _)); + InitializeAndMergeInitialData(SyncMode::kFull); +} + } // namespace } // namespace syncer
diff --git a/components/sync_device_info/fake_device_info_tracker.cc b/components/sync_device_info/fake_device_info_tracker.cc index 2b52570ca..66ce881 100644 --- a/components/sync_device_info/fake_device_info_tracker.cc +++ b/components/sync_device_info/fake_device_info_tracker.cc
@@ -20,7 +20,7 @@ device_info.manufacturer_name(), device_info.model_name(), device_info.last_updated_timestamp(), device_info.pulse_interval(), device_info.send_tab_to_self_receiving_enabled(), - device_info.sharing_info()); + device_info.sharing_info(), device_info.fcm_registration_token()); } } // namespace
diff --git a/components/sync_device_info/fake_local_device_info_provider.cc b/components/sync_device_info/fake_local_device_info_provider.cc index 67329e5..87a9237 100644 --- a/components/sync_device_info/fake_local_device_info_provider.cc +++ b/components/sync_device_info/fake_local_device_info_provider.cc
@@ -21,7 +21,8 @@ /*last_updated_timestamp=*/base::Time::Now(), DeviceInfoUtil::GetPulseInterval(), /*send_tab_to_self_receiving_enabled=*/false, - /*sharing_info=*/base::nullopt) {} + /*sharing_info=*/base::nullopt, + /*fcm_registration_token=*/std::string()) {} FakeLocalDeviceInfoProvider::~FakeLocalDeviceInfoProvider() = default;
diff --git a/components/sync_device_info/local_device_info_provider_impl.cc b/components/sync_device_info/local_device_info_provider_impl.cc index 0a8505e..9c5b93f 100644 --- a/components/sync_device_info/local_device_info_provider_impl.cc +++ b/components/sync_device_info/local_device_info_provider_impl.cc
@@ -7,6 +7,8 @@ #include "base/bind.h" #include "components/sync/base/sync_prefs.h" #include "components/sync/base/sync_util.h" +#include "components/sync/invalidations/switches.h" +#include "components/sync/invalidations/sync_invalidations_service.h" #include "components/sync_device_info/device_info_sync_client.h" #include "components/sync_device_info/device_info_util.h" #include "components/sync_device_info/local_device_info_util.h" @@ -16,13 +18,23 @@ LocalDeviceInfoProviderImpl::LocalDeviceInfoProviderImpl( version_info::Channel channel, const std::string& version, - const DeviceInfoSyncClient* sync_client) - : channel_(channel), version_(version), sync_client_(sync_client) { + const DeviceInfoSyncClient* sync_client, + SyncInvalidationsService* sync_invalidations_service) + : channel_(channel), + version_(version), + sync_client_(sync_client), + sync_invalidations_service_(sync_invalidations_service) { DCHECK(sync_client); + if (sync_invalidations_service_) { + sync_invalidations_service_->AddTokenObserver(this); + } } LocalDeviceInfoProviderImpl::~LocalDeviceInfoProviderImpl() { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + if (sync_invalidations_service_) { + sync_invalidations_service_->RemoveTokenObserver(this); + } } version_info::Channel LocalDeviceInfoProviderImpl::GetChannel() const { @@ -51,6 +63,18 @@ return callback_list_.Add(callback); } +void LocalDeviceInfoProviderImpl::OnFCMRegistrationTokenChanged() { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + DCHECK( + base::FeatureList::IsEnabled(switches::kSubscribeForSyncInvalidations)); + DCHECK(sync_invalidations_service_); + if (local_device_info_) { + local_device_info_->set_fcm_registration_token( + sync_invalidations_service_->GetFCMRegistrationToken()); + } + // TODO(crbug.com/1102336): nudge device info update. +} + void LocalDeviceInfoProviderImpl::Initialize( const std::string& cache_guid, const std::string& client_name, @@ -68,7 +92,7 @@ /*last_updated_timestamp=*/base::Time(), DeviceInfoUtil::GetPulseInterval(), sync_client_->GetSendTabToSelfReceivingEnabled(), - sync_client_->GetLocalSharingInfo()); + sync_client_->GetLocalSharingInfo(), GetFCMRegistrationToken()); // Notify observers. callback_list_.Notify(); @@ -85,4 +109,11 @@ local_device_info_->set_client_name(client_name); } +std::string LocalDeviceInfoProviderImpl::GetFCMRegistrationToken() const { + if (sync_invalidations_service_) { + return sync_invalidations_service_->GetFCMRegistrationToken(); + } + return std::string(); +} + } // namespace syncer
diff --git a/components/sync_device_info/local_device_info_provider_impl.h b/components/sync_device_info/local_device_info_provider_impl.h index d80bc9b..73a020c 100644 --- a/components/sync_device_info/local_device_info_provider_impl.h +++ b/components/sync_device_info/local_device_info_provider_impl.h
@@ -12,6 +12,7 @@ #include "base/macros.h" #include "base/memory/weak_ptr.h" #include "base/sequence_checker.h" +#include "components/sync/invalidations/fcm_registration_token_observer.h" #include "components/sync_device_info/device_info.h" #include "components/sync_device_info/local_device_info_provider.h" #include "components/version_info/version_info.h" @@ -19,12 +20,19 @@ namespace syncer { class DeviceInfoSyncClient; +class SyncInvalidationsService; -class LocalDeviceInfoProviderImpl : public MutableLocalDeviceInfoProvider { +class LocalDeviceInfoProviderImpl + : public MutableLocalDeviceInfoProvider, + public syncer::FCMRegistrationTokenObserver { public: - LocalDeviceInfoProviderImpl(version_info::Channel channel, - const std::string& version, - const DeviceInfoSyncClient* sync_client); + // |sync_invalidations_service| is used to get an FCM registration token. It + // may be nullptr if sync invalidations are disabled. + LocalDeviceInfoProviderImpl( + version_info::Channel channel, + const std::string& version, + const DeviceInfoSyncClient* sync_client, + SyncInvalidationsService* sync_invalidations_service); ~LocalDeviceInfoProviderImpl() override; // MutableLocalDeviceInfoProvider implementation. @@ -39,7 +47,12 @@ std::unique_ptr<Subscription> RegisterOnInitializedCallback( const base::RepeatingClosure& callback) override; + // syncer::FCMRegistrationTokenObserver implementation. + void OnFCMRegistrationTokenChanged() override; + private: + std::string GetFCMRegistrationToken() const; + // The channel (CANARY, DEV, BETA, etc.) of the current client. const version_info::Channel channel_; @@ -47,6 +60,7 @@ const std::string version_; const DeviceInfoSyncClient* const sync_client_; + SyncInvalidationsService* sync_invalidations_service_ = nullptr; std::unique_ptr<DeviceInfo> local_device_info_; base::CallbackList<void(void)> callback_list_;
diff --git a/components/sync_device_info/local_device_info_provider_impl_unittest.cc b/components/sync_device_info/local_device_info_provider_impl_unittest.cc index 297310e..6df3cf7 100644 --- a/components/sync_device_info/local_device_info_provider_impl_unittest.cc +++ b/components/sync_device_info/local_device_info_provider_impl_unittest.cc
@@ -5,7 +5,10 @@ #include "components/sync_device_info/local_device_info_provider_impl.h" #include "base/memory/ptr_util.h" +#include "base/test/scoped_feature_list.h" #include "components/sync/base/sync_util.h" +#include "components/sync/invalidations/mock_sync_invalidations_service.h" +#include "components/sync/invalidations/switches.h" #include "components/sync_device_info/device_info_sync_client.h" #include "components/version_info/version_string.h" #include "testing/gmock/include/gmock/gmock.h" @@ -19,18 +22,21 @@ const char kLocalDeviceManufacturerName[] = "manufacturer"; const char kLocalDeviceModelName[] = "model"; -const char kSharingVapidFCMToken[] = "test_vapid_fcm_token"; +const char kSharingVapidFCMRegistrationToken[] = "test_vapid_fcm_token"; const char kSharingVapidP256dh[] = "test_vapid_p256_dh"; const char kSharingVapidAuthSecret[] = "test_vapid_auth_secret"; -const char kSharingSenderIdFCMToken[] = "test_sender_id_fcm_token"; +const char kSharingSenderIdFCMRegistrationToken[] = "test_sender_id_fcm_token"; const char kSharingSenderIdP256dh[] = "test_sender_id_p256_dh"; const char kSharingSenderIdAuthSecret[] = "test_sender_id_auth_secret"; const sync_pb::SharingSpecificFields::EnabledFeatures kSharingEnabledFeatures[] = { sync_pb::SharingSpecificFields::CLICK_TO_CALL_V2}; +using testing::_; +using testing::NiceMock; using testing::NotNull; using testing::Return; +using testing::ReturnRef; class MockDeviceInfoSyncClient : public DeviceInfoSyncClient { public: @@ -55,12 +61,16 @@ provider_ = std::make_unique<LocalDeviceInfoProviderImpl>( version_info::Channel::UNKNOWN, version_info::GetVersionStringWithModifier("UNKNOWN"), - &device_info_sync_client_); + &device_info_sync_client_, GetSyncInvalidationsService()); } void TearDown() override { provider_.reset(); } protected: + virtual SyncInvalidationsService* GetSyncInvalidationsService() { + return nullptr; + } + void InitializeProvider() { InitializeProvider(kLocalDeviceGuid); } void InitializeProvider(const std::string& guid) { @@ -72,6 +82,27 @@ std::unique_ptr<LocalDeviceInfoProviderImpl> provider_; }; +class LocalDeviceInfoProviderImplWithSyncInvalidationsTest + : public LocalDeviceInfoProviderImplTest { + public: + LocalDeviceInfoProviderImplWithSyncInvalidationsTest() { + override_features_.InitAndEnableFeature( + switches::kSubscribeForSyncInvalidations); + ON_CALL(mock_sync_invalidations_service_, GetFCMRegistrationToken()) + .WillByDefault(ReturnRef(kEmptyToken)); + } + + protected: + SyncInvalidationsService* GetSyncInvalidationsService() override { + return &mock_sync_invalidations_service_; + } + + const std::string kEmptyToken; + + base::test::ScopedFeatureList override_features_; + NiceMock<MockSyncInvalidationsService> mock_sync_invalidations_service_; +}; + TEST_F(LocalDeviceInfoProviderImplTest, GetLocalDeviceInfo) { ASSERT_EQ(nullptr, provider_->GetLocalDeviceInfo()); @@ -135,10 +166,10 @@ std::begin(kSharingEnabledFeatures), std::end(kSharingEnabledFeatures)); base::Optional<DeviceInfo::SharingInfo> sharing_info = base::make_optional<DeviceInfo::SharingInfo>( - DeviceInfo::SharingTargetInfo{kSharingVapidFCMToken, + DeviceInfo::SharingTargetInfo{kSharingVapidFCMRegistrationToken, kSharingVapidP256dh, kSharingVapidAuthSecret}, - DeviceInfo::SharingTargetInfo{kSharingSenderIdFCMToken, + DeviceInfo::SharingTargetInfo{kSharingSenderIdFCMRegistrationToken, kSharingSenderIdP256dh, kSharingSenderIdAuthSecret}, enabled_features); @@ -149,12 +180,12 @@ const base::Optional<DeviceInfo::SharingInfo>& local_sharing_info = provider_->GetLocalDeviceInfo()->sharing_info(); ASSERT_TRUE(local_sharing_info); - EXPECT_EQ(kSharingVapidFCMToken, + EXPECT_EQ(kSharingVapidFCMRegistrationToken, local_sharing_info->vapid_target_info.fcm_token); EXPECT_EQ(kSharingVapidP256dh, local_sharing_info->vapid_target_info.p256dh); EXPECT_EQ(kSharingVapidAuthSecret, local_sharing_info->vapid_target_info.auth_secret); - EXPECT_EQ(kSharingSenderIdFCMToken, + EXPECT_EQ(kSharingSenderIdFCMRegistrationToken, local_sharing_info->sender_id_target_info.fcm_token); EXPECT_EQ(kSharingSenderIdP256dh, local_sharing_info->sender_id_target_info.p256dh); @@ -163,5 +194,21 @@ EXPECT_EQ(enabled_features, local_sharing_info->enabled_features); } +TEST_F(LocalDeviceInfoProviderImplWithSyncInvalidationsTest, + ShouldPopulateFCMRegistrationToken) { + InitializeProvider(); + ASSERT_THAT(provider_->GetLocalDeviceInfo(), NotNull()); + EXPECT_TRUE( + provider_->GetLocalDeviceInfo()->fcm_registration_token().empty()); + + const std::string kFCMRegistrationToken = "token"; + EXPECT_CALL(mock_sync_invalidations_service_, GetFCMRegistrationToken()) + .WillOnce(ReturnRef(kFCMRegistrationToken)); + + provider_->OnFCMRegistrationTokenChanged(); + EXPECT_EQ(provider_->GetLocalDeviceInfo()->fcm_registration_token(), + kFCMRegistrationToken); +} + } // namespace } // namespace syncer
diff --git a/components/web_package/web_bundle_parser.cc b/components/web_package/web_bundle_parser.cc index 4fcda57..66fcc324 100644 --- a/components/web_package/web_bundle_parser.cc +++ b/components/web_package/web_bundle_parser.cc
@@ -1314,9 +1314,10 @@ observer->OnDisconnect(); } -void WebBundleParser::SharedBundleDataSource::Read(uint64_t offset, - uint64_t length, - ReadCallback callback) { +void WebBundleParser::SharedBundleDataSource::Read( + uint64_t offset, + uint64_t length, + mojom::BundleDataSource::ReadCallback callback) { data_source_->Read(offset, length, std::move(callback)); }
diff --git a/components/web_package/web_bundle_parser.h b/components/web_package/web_bundle_parser.h index 5cfe22d..a1a3b1b7 100644 --- a/components/web_package/web_bundle_parser.h +++ b/components/web_package/web_bundle_parser.h
@@ -27,8 +27,7 @@ class ResponseParser; class SharedBundleDataSource - : public base::RefCounted<SharedBundleDataSource>, - public mojom::BundleDataSource { + : public base::RefCounted<SharedBundleDataSource> { public: class Observer; @@ -38,13 +37,14 @@ void AddObserver(Observer* observer); void RemoveObserver(Observer* observer); - // Implements mojom::BundleDataSource. - void Read(uint64_t offset, uint64_t length, ReadCallback callback) override; + void Read(uint64_t offset, + uint64_t length, + mojom::BundleDataSource::ReadCallback callback); private: friend class base::RefCounted<SharedBundleDataSource>; - ~SharedBundleDataSource() override; + ~SharedBundleDataSource(); void OnDisconnect();
diff --git a/content/browser/frame_host/render_frame_host_impl.cc b/content/browser/frame_host/render_frame_host_impl.cc index 3add8324..10a4849 100644 --- a/content/browser/frame_host/render_frame_host_impl.cc +++ b/content/browser/frame_host/render_frame_host_impl.cc
@@ -1036,6 +1036,9 @@ // the dtor has run. (It may also be null in tests.) unload_event_monitor_timeout_.reset(); + for (auto& iter : visual_state_callbacks_) + std::move(iter.second).Run(false); + // Delete this before destroying the widget, to guard against reentrancy // by in-process screen readers such as JAWS. browser_accessibility_manager_.reset(); @@ -1644,6 +1647,7 @@ IPC_BEGIN_MESSAGE_MAP(RenderFrameHostImpl, msg) IPC_MESSAGE_HANDLER(FrameHostMsg_Unload_ACK, OnUnloadACK) IPC_MESSAGE_HANDLER(FrameHostMsg_ContextMenu, OnContextMenu) + IPC_MESSAGE_HANDLER(FrameHostMsg_VisualStateResponse, OnVisualStateResponse) IPC_MESSAGE_HANDLER(FrameHostMsg_SelectionChanged, OnSelectionChanged) IPC_END_MESSAGE_MAP() @@ -1852,6 +1856,8 @@ smart_clip_callbacks_.Clear(); #endif // defined(OS_ANDROID) + visual_state_callbacks_.clear(); + // Ensure that future remote interface requests are associated with the new // process's channel. remote_associated_interfaces_.reset(); @@ -3120,6 +3126,16 @@ } #endif // defined(OS_ANDROID) +void RenderFrameHostImpl::OnVisualStateResponse(uint64_t id) { + auto it = visual_state_callbacks_.find(id); + if (it != visual_state_callbacks_.end()) { + std::move(it->second).Run(true); + visual_state_callbacks_.erase(it); + } else { + NOTREACHED() << "Received script response for unknown request"; + } +} + void RenderFrameHostImpl::RunModalAlertDialog( const base::string16& alert_message, RunModalAlertDialogCallback response_callback) { @@ -6658,7 +6674,10 @@ void RenderFrameHostImpl::InsertVisualStateCallback( VisualStateCallback callback) { - GetRenderWidgetHost()->InsertVisualStateCallback(std::move(callback)); + static uint64_t next_id = 1; + uint64_t key = next_id++; + Send(new FrameMsg_VisualStateRequest(routing_id_, key)); + visual_state_callbacks_.emplace(key, std::move(callback)); } bool RenderFrameHostImpl::IsRenderFrameCreated() {
diff --git a/content/browser/frame_host/render_frame_host_impl.h b/content/browser/frame_host/render_frame_host_impl.h index f34a297..10b4d3b2 100644 --- a/content/browser/frame_host/render_frame_host_impl.h +++ b/content/browser/frame_host/render_frame_host_impl.h
@@ -1887,6 +1887,8 @@ // IPC Message handlers. void OnUnloadACK(); void OnContextMenu(const UntrustworthyContextMenuParams& params); + void OnVisualStateResponse(uint64_t id); + void OnForwardResourceTimingToParent( const ResourceTimingInfo& resource_timing); void OnSelectionChanged(const base::string16& text, @@ -2506,6 +2508,8 @@ // The http status code of the last committed navigation. int last_http_status_code_ = 0; + std::map<uint64_t, VisualStateCallback> visual_state_callbacks_; + // Local root subframes directly own their RenderWidgetHost. // Please see comments about the GetLocalRenderWidgetHost() function. // TODO(kenrb): Later this will also be used on the top-level frame, when
diff --git a/content/browser/frame_host/render_frame_host_manager_browsertest.cc b/content/browser/frame_host/render_frame_host_manager_browsertest.cc index bafb295..4374d93 100644 --- a/content/browser/frame_host/render_frame_host_manager_browsertest.cc +++ b/content/browser/frame_host/render_frame_host_manager_browsertest.cc
@@ -6050,6 +6050,17 @@ // 3. Go back to A1 (should reuse A2's process). IN_PROC_BROWSER_TEST_P(ProactivelySwapBrowsingInstancesSameSiteTest, HistoryNavigationReusesProcess) { + if (IsBackForwardCacheEnabled()) { + // This test expects a renderer process to eventually get deleted when we + // navigate away from the page using it, which won't happen if the page is + // kept alive in the back-forward cache. So, we should disable back-forward + // cache for this test. + shell() + ->web_contents() + ->GetController() + .GetBackForwardCache() + .DisableForTesting(BackForwardCacheImpl::TEST_ASSUMES_NO_CACHING); + } ASSERT_TRUE(embedded_test_server()->Start()); GURL url_1(embedded_test_server()->GetURL("/title1.html")); GURL url_2(embedded_test_server()->GetURL("/title2.html")); @@ -6130,6 +6141,17 @@ // 3. Go forward to A2 (should reuse A1's process). IN_PROC_BROWSER_TEST_P(ProactivelySwapBrowsingInstancesSameSiteTest, HistoryNavigationReusesProcess_SkipSameSiteEntry) { + if (IsBackForwardCacheEnabled()) { + // This test expects a renderer process to eventually get deleted when we + // navigate away from the page using it, which won't happen if the page is + // kept alive in the back-forward cache. So, we should disable back-forward + // cache for this test. + shell() + ->web_contents() + ->GetController() + .GetBackForwardCache() + .DisableForTesting(BackForwardCacheImpl::TEST_ASSUMES_NO_CACHING); + } ASSERT_TRUE(embedded_test_server()->Start()); GURL url_1(embedded_test_server()->GetURL("/title1.html")); GURL url_2(embedded_test_server()->GetURL("/title2.html")); @@ -6211,6 +6233,17 @@ // 3. Go forward to B (should use new process). IN_PROC_BROWSER_TEST_P(ProactivelySwapBrowsingInstancesSameSiteTest, HistoryNavigationReusesProcess_SkipCrossSiteEntry) { + if (IsBackForwardCacheEnabled()) { + // This test expects a renderer process to eventually get deleted when we + // navigate away from the page using it, which won't happen if the page is + // kept alive in the back-forward cache. So, we should disable back-forward + // cache for this test. + shell() + ->web_contents() + ->GetController() + .GetBackForwardCache() + .DisableForTesting(BackForwardCacheImpl::TEST_ASSUMES_NO_CACHING); + } ASSERT_TRUE(embedded_test_server()->Start()); GURL url_1(embedded_test_server()->GetURL("/title1.html")); GURL cross_site_url(embedded_test_server()->GetURL("b.com", "/title2.html")); @@ -6280,6 +6313,17 @@ // used originally). IN_PROC_BROWSER_TEST_P(ProactivelySwapBrowsingInstancesSameSiteTest, HistoryNavigationReusesProcessThatIsStillAlive) { + if (IsBackForwardCacheEnabled()) { + // This test expects a renderer process to eventually get deleted when we + // navigate away from the page using it, which won't happen if the page is + // kept alive in the back-forward cache. So, we should disable back-forward + // cache for this test. + shell() + ->web_contents() + ->GetController() + .GetBackForwardCache() + .DisableForTesting(BackForwardCacheImpl::TEST_ASSUMES_NO_CACHING); + } ASSERT_TRUE(embedded_test_server()->Start()); GURL url_1(embedded_test_server()->GetURL("/title1.html")); GURL url_to_open(embedded_test_server()->GetURL("/empty.html")); @@ -6396,6 +6440,17 @@ // Note: This test is currently disabled due to crbug.com/1107814. IN_PROC_BROWSER_TEST_P(ProactivelySwapBrowsingInstancesSameSiteCoopTest, DISABLED_HistoryNavigationReusesProcess_COOP) { + if (IsBackForwardCacheEnabled()) { + // This test expects a renderer process to eventually get deleted when we + // navigate away from the page using it, which won't happen if the page is + // kept alive in the back-forward cache. So, we should disable back-forward + // cache for this test. + shell() + ->web_contents() + ->GetController() + .GetBackForwardCache() + .DisableForTesting(BackForwardCacheImpl::TEST_ASSUMES_NO_CACHING); + } GURL url_1(https_server()->GetURL("a.com", "/title1.html")); GURL url_2(https_server()->GetURL("a.com", "/title2.html")); GURL coop_url(
diff --git a/content/browser/renderer_host/frame_token_message_queue.cc b/content/browser/renderer_host/frame_token_message_queue.cc index 5bb83c8..72f07c45 100644 --- a/content/browser/renderer_host/frame_token_message_queue.cc +++ b/content/browser/renderer_host/frame_token_message_queue.cc
@@ -5,6 +5,7 @@ #include "content/browser/renderer_host/frame_token_message_queue.h" #include "base/bind.h" +#include "ipc/ipc_message.h" namespace content { @@ -62,10 +63,27 @@ callback_map_.insert(std::make_pair(frame_token, std::move(callback))); } +void FrameTokenMessageQueue::OnFrameSwapMessagesReceived( + uint32_t frame_token, + std::vector<IPC::Message> messages) { + EnqueueOrRunFrameTokenCallback( + frame_token, base::BindOnce(&FrameTokenMessageQueue::ProcessSwapMessages, + base::Unretained(this), std::move(messages))); +} + void FrameTokenMessageQueue::Reset() { last_received_frame_token_reset_ = last_received_frame_token_; last_received_frame_token_ = 0; callback_map_.clear(); } +void FrameTokenMessageQueue::ProcessSwapMessages( + std::vector<IPC::Message> messages) { + for (const IPC::Message& i : messages) { + client_->OnProcessSwapMessage(i); + if (i.dispatch_error()) + client_->OnMessageDispatchError(i); + } +} + } // namespace content
diff --git a/content/browser/renderer_host/frame_token_message_queue.h b/content/browser/renderer_host/frame_token_message_queue.h index 166ccd0..e8b256d 100644 --- a/content/browser/renderer_host/frame_token_message_queue.h +++ b/content/browser/renderer_host/frame_token_message_queue.h
@@ -12,6 +12,10 @@ #include "base/macros.h" #include "content/common/content_export.h" +namespace IPC { +class Message; +} // namespace IPC + namespace content { // The Renderer sends various messages which are not to be processed until after @@ -21,19 +25,29 @@ // Viz processes the frames, after which it notifies the Browser of which // FrameToken has completed processing. // -// Non-IPC callbacks can be enqueued with EnqueueOrRunFrameTokenCallback. +// This enqueues all IPC::Messages associated with a FrameToken. // -// Upon receipt of DidProcessFrame all Messages associated with the provided -// FrameToken are then dispatched, and all enqueued callbacks are ran. +// Additionally other callbacks can be enqueued with +// EnqueueOrRunFrameTokenCallback. +// +// Upon receipt of DidProcessFrame all IPC::Messages associated with the +// provided FrameToken are then dispatched, and all enqueued callbacks are ran. class CONTENT_EXPORT FrameTokenMessageQueue { public: - // Notified of errors in processing messages. + // Notified of errors in processing messages, as well as of the actual + // enqueued IPC::Messages which need processing. class Client { public: ~Client() {} // Notified when an invalid frame token was received. virtual void OnInvalidFrameToken(uint32_t frame_token) = 0; + + // Called when there are dispatching errors in OnMessageReceived(). + virtual void OnMessageDispatchError(const IPC::Message& message) = 0; + + // Process the enqueued message. + virtual void OnProcessSwapMessage(const IPC::Message& message) = 0; }; FrameTokenMessageQueue(); virtual ~FrameTokenMessageQueue(); @@ -52,12 +66,21 @@ void EnqueueOrRunFrameTokenCallback(uint32_t frame_token, base::OnceClosure callback); + // Enqueues the swap messages. + void OnFrameSwapMessagesReceived(uint32_t frame_token, + std::vector<IPC::Message> messages); + // Called when the renderer process is gone. This will reset our state to be // consistent incase a new renderer is created. void Reset(); size_t size() const { return callback_map_.size(); } + protected: + // Once both the frame and its swap messages arrive, we call this method to + // process the messages. Virtual for tests. + virtual void ProcessSwapMessages(std::vector<IPC::Message> messages); + private: // Not owned. Client* client_ = nullptr;
diff --git a/content/browser/renderer_host/frame_token_message_queue_unittest.cc b/content/browser/renderer_host/frame_token_message_queue_unittest.cc index 7943ecf9..da59028c 100644 --- a/content/browser/renderer_host/frame_token_message_queue_unittest.cc +++ b/content/browser/renderer_host/frame_token_message_queue_unittest.cc
@@ -8,6 +8,7 @@ #include "base/bind.h" #include "base/macros.h" +#include "ipc/ipc_message.h" #include "testing/gtest/include/gtest/gtest.h" namespace content { @@ -24,23 +25,46 @@ // Resets all method counters. void Reset(); + // All subsequent IPC::Messages received in OnProcessSwapMessage will be + // marked as having a dispatch error. + void SetErrorOnMessageProcess(); + // FrameTokenMessageQueue::Client: void OnInvalidFrameToken(uint32_t frame_token) override; + void OnMessageDispatchError(const IPC::Message& message) override; + void OnProcessSwapMessage(const IPC::Message& message) override; bool invalid_frame_token_called() const { return invalid_frame_token_called_; } uint32_t invalid_frame_token() const { return invalid_frame_token_; } + int on_message_dispatch_error_count() const { + return on_message_dispatch_error_count_; + } + int on_process_swap_message_count() const { + return on_process_swap_message_count_; + } private: + // If true the each IPC::Message received will be marked as having a dispatch + // error. + bool set_error_on_process_ = false; bool invalid_frame_token_called_ = false; uint32_t invalid_frame_token_ = 0u; + int on_message_dispatch_error_count_ = 0; + int on_process_swap_message_count_ = 0; DISALLOW_COPY_AND_ASSIGN(TestFrameTokenMessageQueueClient); }; void TestFrameTokenMessageQueueClient::Reset() { invalid_frame_token_called_ = false; invalid_frame_token_ = 0u; + on_message_dispatch_error_count_ = 0; + on_process_swap_message_count_ = 0; +} + +void TestFrameTokenMessageQueueClient::SetErrorOnMessageProcess() { + set_error_on_process_ = true; } void TestFrameTokenMessageQueueClient::OnInvalidFrameToken( @@ -49,6 +73,18 @@ invalid_frame_token_ = frame_token; } +void TestFrameTokenMessageQueueClient::OnMessageDispatchError( + const IPC::Message& message) { + ++on_message_dispatch_error_count_; +} + +void TestFrameTokenMessageQueueClient::OnProcessSwapMessage( + const IPC::Message& message) { + if (set_error_on_process_) + message.set_dispatch_error(); + ++on_process_swap_message_count_; +} + // Test class which provides FrameTokenCallback() to be used as a closure when // enqueueing non-IPC callbacks. This only tracks if the callback was called. class TestNonIPCMessageEnqueuer { @@ -97,6 +133,124 @@ frame_token_message_queue_.Init(&test_client_); } +// Tests that if a valid IPC::Message is enqueued, that it is processed when its +// matching frame token arrives. +TEST_F(FrameTokenMessageQueueTest, OnlyIPCMessageCorrectFrameToken) { + FrameTokenMessageQueue* queue = frame_token_message_queue(); + TestFrameTokenMessageQueueClient* client = test_client(); + ASSERT_EQ(0u, queue->size()); + + const uint32_t frame_token = 42; + IPC::Message msg(0, 1, IPC::Message::PRIORITY_NORMAL); + std::vector<IPC::Message> messages; + messages.push_back(msg); + + // Adding to the queue with a new frame token should not cause processing. + queue->OnFrameSwapMessagesReceived(frame_token, std::move(messages)); + EXPECT_EQ(1u, queue->size()); + EXPECT_EQ(0, client->on_process_swap_message_count()); + + queue->DidProcessFrame(frame_token); + EXPECT_EQ(0u, queue->size()); + EXPECT_FALSE(client->invalid_frame_token_called()); + EXPECT_EQ(1, client->on_process_swap_message_count()); + EXPECT_EQ(0, client->on_message_dispatch_error_count()); +} + +// Tests that if a valid IPC::Message is enqueued after its frame token has +// arrived that it is processed immediately. +TEST_F(FrameTokenMessageQueueTest, EnqueueAfterFrameTokenProcesses) { + FrameTokenMessageQueue* queue = frame_token_message_queue(); + TestFrameTokenMessageQueueClient* client = test_client(); + ASSERT_EQ(0u, queue->size()); + + const uint32_t frame_token = 42; + IPC::Message msg(0, 1, IPC::Message::PRIORITY_NORMAL); + std::vector<IPC::Message> messages; + messages.push_back(msg); + + queue->DidProcessFrame(frame_token); + EXPECT_EQ(0u, queue->size()); + EXPECT_FALSE(client->invalid_frame_token_called()); + EXPECT_EQ(0, client->on_process_swap_message_count()); + EXPECT_EQ(0, client->on_message_dispatch_error_count()); + + // Enqueuing after frame token arrival should immediately process. + queue->OnFrameSwapMessagesReceived(frame_token, std::move(messages)); + EXPECT_EQ(0u, queue->size()); + EXPECT_FALSE(client->invalid_frame_token_called()); + EXPECT_EQ(1, client->on_process_swap_message_count()); + EXPECT_EQ(0, client->on_message_dispatch_error_count()); +} + +// Tests that if a valid IPC::Message is enqueued and that subsequently a +// non-IPC callback is enqueued, that both get called once the frame token +// arrives. +TEST_F(FrameTokenMessageQueueTest, EnqueueBothIPCMessageAndNonIPCCallback) { + FrameTokenMessageQueue* queue = frame_token_message_queue(); + TestFrameTokenMessageQueueClient* client = test_client(); + TestNonIPCMessageEnqueuer* enqueuer = test_non_ipc_enqueuer(); + ASSERT_EQ(0u, queue->size()); + + const uint32_t frame_token = 42; + IPC::Message msg(0, 1, IPC::Message::PRIORITY_NORMAL); + std::vector<IPC::Message> messages; + messages.push_back(msg); + + // Adding to the queue with a new frame token should not cause processing. + queue->OnFrameSwapMessagesReceived(frame_token, std::move(messages)); + EXPECT_EQ(1u, queue->size()); + EXPECT_EQ(0, client->on_process_swap_message_count()); + + queue->EnqueueOrRunFrameTokenCallback( + frame_token, + base::BindOnce(&TestNonIPCMessageEnqueuer::FrameTokenCallback, + base::Unretained(enqueuer))); + EXPECT_FALSE(enqueuer->frame_token_callback_called()); + EXPECT_FALSE(client->invalid_frame_token_called()); + + queue->DidProcessFrame(frame_token); + EXPECT_EQ(0u, queue->size()); + EXPECT_FALSE(client->invalid_frame_token_called()); + EXPECT_EQ(1, client->on_process_swap_message_count()); + EXPECT_EQ(0, client->on_message_dispatch_error_count()); + EXPECT_TRUE(enqueuer->frame_token_callback_called()); +} + +// Tests that if a valid non-IPC callback is enqueued before an IPC::Message, +// that both get called once the frame token arrives. +TEST_F(FrameTokenMessageQueueTest, EnqueueNonIPCCallbackFirst) { + FrameTokenMessageQueue* queue = frame_token_message_queue(); + TestFrameTokenMessageQueueClient* client = test_client(); + TestNonIPCMessageEnqueuer* enqueuer = test_non_ipc_enqueuer(); + ASSERT_EQ(0u, queue->size()); + + const uint32_t frame_token = 42; + IPC::Message msg(0, 1, IPC::Message::PRIORITY_NORMAL); + std::vector<IPC::Message> messages; + messages.push_back(msg); + + queue->EnqueueOrRunFrameTokenCallback( + frame_token, + base::BindOnce(&TestNonIPCMessageEnqueuer::FrameTokenCallback, + base::Unretained(enqueuer))); + EXPECT_FALSE(enqueuer->frame_token_callback_called()); + EXPECT_EQ(0, client->on_process_swap_message_count()); + + // We should be able to enqueue even though it is for the same frame token. + queue->OnFrameSwapMessagesReceived(frame_token, std::move(messages)); + EXPECT_EQ(2u, queue->size()); + EXPECT_EQ(0, client->on_process_swap_message_count()); + EXPECT_FALSE(client->invalid_frame_token_called()); + + queue->DidProcessFrame(frame_token); + EXPECT_EQ(0u, queue->size()); + EXPECT_FALSE(client->invalid_frame_token_called()); + EXPECT_EQ(1, client->on_process_swap_message_count()); + EXPECT_EQ(0, client->on_message_dispatch_error_count()); + EXPECT_TRUE(enqueuer->frame_token_callback_called()); +} + // Tests that if we only have a non-IPC callback enqueued that it is called once // the frame token arrive. TEST_F(FrameTokenMessageQueueTest, EnqueueOnlyNonIPC) { @@ -110,16 +264,80 @@ frame_token, base::BindOnce(&TestNonIPCMessageEnqueuer::FrameTokenCallback, base::Unretained(enqueuer))); - EXPECT_EQ(1u, queue->size()); EXPECT_FALSE(enqueuer->frame_token_callback_called()); - EXPECT_FALSE(client->invalid_frame_token_called()); + EXPECT_EQ(0, client->on_process_swap_message_count()); + EXPECT_EQ(1u, queue->size()); queue->DidProcessFrame(frame_token); EXPECT_EQ(0u, queue->size()); EXPECT_FALSE(client->invalid_frame_token_called()); + EXPECT_EQ(0, client->on_process_swap_message_count()); + EXPECT_EQ(0, client->on_message_dispatch_error_count()); EXPECT_TRUE(enqueuer->frame_token_callback_called()); } +// Tests that if we have messages enqueued, and receive a frame token that is +// larger, that we still process the messages. +TEST_F(FrameTokenMessageQueueTest, MessagesWhereFrameTokenSkipped) { + FrameTokenMessageQueue* queue = frame_token_message_queue(); + TestFrameTokenMessageQueueClient* client = test_client(); + TestNonIPCMessageEnqueuer* enqueuer = test_non_ipc_enqueuer(); + ASSERT_EQ(0u, queue->size()); + + const uint32_t frame_token = 42; + IPC::Message msg(0, 1, IPC::Message::PRIORITY_NORMAL); + std::vector<IPC::Message> messages; + messages.push_back(msg); + + queue->EnqueueOrRunFrameTokenCallback( + frame_token, + base::BindOnce(&TestNonIPCMessageEnqueuer::FrameTokenCallback, + base::Unretained(enqueuer))); + EXPECT_FALSE(enqueuer->frame_token_callback_called()); + EXPECT_EQ(0, client->on_process_swap_message_count()); + + // We should be able to enqueue even though it is for the same frame token. + queue->OnFrameSwapMessagesReceived(frame_token, std::move(messages)); + EXPECT_EQ(2u, queue->size()); + EXPECT_EQ(0, client->on_process_swap_message_count()); + EXPECT_FALSE(client->invalid_frame_token_called()); + + const uint32_t larger_frame_token = 1337; + queue->DidProcessFrame(larger_frame_token); + EXPECT_EQ(0u, queue->size()); + EXPECT_FALSE(client->invalid_frame_token_called()); + EXPECT_EQ(1, client->on_process_swap_message_count()); + EXPECT_EQ(0, client->on_message_dispatch_error_count()); + EXPECT_TRUE(enqueuer->frame_token_callback_called()); +} + +// Verifies that if there are multiple IPC::Messages that they are all +// processed. +TEST_F(FrameTokenMessageQueueTest, MultipleIPCMessages) { + FrameTokenMessageQueue* queue = frame_token_message_queue(); + TestFrameTokenMessageQueueClient* client = test_client(); + ASSERT_EQ(0u, queue->size()); + + const uint32_t frame_token = 42; + IPC::Message msg1(0, 1, IPC::Message::PRIORITY_NORMAL); + IPC::Message msg2(1, 2, IPC::Message::PRIORITY_LOW); + std::vector<IPC::Message> messages; + messages.push_back(msg1); + messages.push_back(msg2); + + // Adding to the queue with a new frame token should not cause processing. + queue->OnFrameSwapMessagesReceived(frame_token, std::move(messages)); + // All IPCs are enqueued as one. + EXPECT_EQ(1u, queue->size()); + EXPECT_EQ(0, client->on_process_swap_message_count()); + + queue->DidProcessFrame(frame_token); + EXPECT_EQ(0u, queue->size()); + EXPECT_FALSE(client->invalid_frame_token_called()); + EXPECT_EQ(2, client->on_process_swap_message_count()); + EXPECT_EQ(0, client->on_message_dispatch_error_count()); +} + // Verifies that if there are multiple non-IPC messages enqueued that they are // all called. TEST_F(FrameTokenMessageQueueTest, MultipleNonIPCMessages) { @@ -175,6 +393,52 @@ EXPECT_TRUE(enqueuer->frame_token_callback_called()); } +// Tests that if IPC::Messages are enqueued for different frame tokens, that +// we only process the messages associated with the arriving token, and keep the +// others enqueued. +TEST_F(FrameTokenMessageQueueTest, DifferentFrameTokensEnqueuedIPC) { + FrameTokenMessageQueue* queue = frame_token_message_queue(); + TestFrameTokenMessageQueueClient* client = test_client(); + ASSERT_EQ(0u, queue->size()); + + const uint32_t frame_token_1 = 42; + IPC::Message msg1(0, 1, IPC::Message::PRIORITY_NORMAL); + std::vector<IPC::Message> messages_1; + messages_1.push_back(msg1); + + queue->OnFrameSwapMessagesReceived(frame_token_1, std::move(messages_1)); + EXPECT_EQ(1u, queue->size()); + EXPECT_EQ(0, client->on_process_swap_message_count()); + + const uint32_t frame_token_2 = 1337; + IPC::Message msg2(1, 2, IPC::Message::PRIORITY_LOW); + std::vector<IPC::Message> messages_2; + messages_2.push_back(msg2); + + queue->OnFrameSwapMessagesReceived(frame_token_2, std::move(messages_2)); + // With no frame token yet the second set of IPC::Messages should be enqueud + // separately. + EXPECT_EQ(2u, queue->size()); + EXPECT_EQ(0, client->on_process_swap_message_count()); + + // We should only process the first IPC::Message. + queue->DidProcessFrame(frame_token_1); + EXPECT_EQ(1u, queue->size()); + EXPECT_FALSE(client->invalid_frame_token_called()); + EXPECT_EQ(1, client->on_process_swap_message_count()); + EXPECT_EQ(0, client->on_message_dispatch_error_count()); + + // Clear the counts from the first token. + client->Reset(); + + // The second IPC::Message should be processed. + queue->DidProcessFrame(frame_token_2); + EXPECT_EQ(0u, queue->size()); + EXPECT_FALSE(client->invalid_frame_token_called()); + EXPECT_EQ(1, client->on_process_swap_message_count()); + EXPECT_EQ(0, client->on_message_dispatch_error_count()); +} + // Test that if non-IPC callbacks are enqueued for different frame tokens, that // we only process the messages associated with the arriving token, and keep the // others enqueued. @@ -212,6 +476,37 @@ EXPECT_TRUE(second_enqueuer.frame_token_callback_called()); } + +// Tests that when adding an IPC::Message for an earlier frame token, that it is +// enqueued. +TEST_F(FrameTokenMessageQueueTest, EarlierTokenForIPCMessageIsNotRejected) { + FrameTokenMessageQueue* queue = frame_token_message_queue(); + TestFrameTokenMessageQueueClient* client = test_client(); + ASSERT_EQ(0u, queue->size()); + + const uint32_t valid_frame_token = 42; + IPC::Message msg1(0, 1, IPC::Message::PRIORITY_NORMAL); + std::vector<IPC::Message> messages_1; + messages_1.push_back(msg1); + + // Adding to the queue with a new frame token should not cause processing. + queue->OnFrameSwapMessagesReceived(valid_frame_token, std::move(messages_1)); + EXPECT_EQ(1u, queue->size()); + EXPECT_EQ(0, client->on_process_swap_message_count()); + + const uint32_t earlier_frame_token = 1; + IPC::Message msg2(1, 2, IPC::Message::PRIORITY_NORMAL); + std::vector<IPC::Message> messages_2; + messages_2.push_back(msg1); + + // Adding an earlier frame token should be enqueued. + queue->OnFrameSwapMessagesReceived(earlier_frame_token, + std::move(messages_2)); + EXPECT_EQ(2u, queue->size()); + EXPECT_FALSE(client->invalid_frame_token_called()); + EXPECT_EQ(0, client->on_process_swap_message_count()); +} + // Tests that if DidProcessFrame is called with an invalid token, that it is // rejected, and that no callbacks are processed. TEST_F(FrameTokenMessageQueueTest, InvalidDidProcessFrameTokenNotProcessed) { @@ -221,6 +516,14 @@ ASSERT_EQ(0u, queue->size()); const uint32_t frame_token = 42; + IPC::Message msg(0, 1, IPC::Message::PRIORITY_NORMAL); + std::vector<IPC::Message> messages; + messages.push_back(msg); + + queue->OnFrameSwapMessagesReceived(frame_token, std::move(messages)); + EXPECT_EQ(1u, queue->size()); + EXPECT_EQ(0, client->on_process_swap_message_count()); + queue->EnqueueOrRunFrameTokenCallback( frame_token, base::BindOnce(&TestNonIPCMessageEnqueuer::FrameTokenCallback, @@ -231,9 +534,11 @@ // Empty token should be invalid even with no process frames processed. const uint32_t invalid_frame_token = 0; queue->DidProcessFrame(invalid_frame_token); - EXPECT_EQ(1u, queue->size()); + EXPECT_EQ(2u, queue->size()); EXPECT_TRUE(client->invalid_frame_token_called()); EXPECT_EQ(invalid_frame_token, client->invalid_frame_token()); + EXPECT_EQ(0, client->on_process_swap_message_count()); + EXPECT_EQ(0, client->on_message_dispatch_error_count()); EXPECT_FALSE(enqueuer->frame_token_callback_called()); } @@ -250,24 +555,58 @@ queue->DidProcessFrame(earlier_frame_token); const uint32_t frame_token = 1337; + IPC::Message msg(0, 1, IPC::Message::PRIORITY_NORMAL); + std::vector<IPC::Message> messages; + messages.push_back(msg); + + queue->OnFrameSwapMessagesReceived(frame_token, std::move(messages)); + EXPECT_EQ(1u, queue->size()); + EXPECT_EQ(0, client->on_process_swap_message_count()); + queue->EnqueueOrRunFrameTokenCallback( frame_token, base::BindOnce(&TestNonIPCMessageEnqueuer::FrameTokenCallback, base::Unretained(enqueuer))); EXPECT_FALSE(enqueuer->frame_token_callback_called()); EXPECT_FALSE(client->invalid_frame_token_called()); - EXPECT_EQ(1u, queue->size()); // Using a frame token that is earlier than the last received should be // rejected. const uint32_t invalid_frame_token = earlier_frame_token - 1; queue->DidProcessFrame(invalid_frame_token); - EXPECT_EQ(1u, queue->size()); + EXPECT_EQ(2u, queue->size()); EXPECT_TRUE(client->invalid_frame_token_called()); EXPECT_EQ(invalid_frame_token, client->invalid_frame_token()); + EXPECT_EQ(0, client->on_process_swap_message_count()); + EXPECT_EQ(0, client->on_message_dispatch_error_count()); EXPECT_FALSE(enqueuer->frame_token_callback_called()); } +// Tests that if an IPC::Message has a dispatch error that the client is +// notified. +TEST_F(FrameTokenMessageQueueTest, DispatchError) { + FrameTokenMessageQueue* queue = frame_token_message_queue(); + TestFrameTokenMessageQueueClient* client = test_client(); + ASSERT_EQ(0u, queue->size()); + + const uint32_t frame_token = 42; + IPC::Message msg(0, 1, IPC::Message::PRIORITY_NORMAL); + std::vector<IPC::Message> messages; + messages.push_back(msg); + + queue->OnFrameSwapMessagesReceived(frame_token, std::move(messages)); + EXPECT_EQ(1u, queue->size()); + EXPECT_EQ(0, client->on_process_swap_message_count()); + + // Dispatch error should be notified during processing. + client->SetErrorOnMessageProcess(); + queue->DidProcessFrame(frame_token); + EXPECT_EQ(0u, queue->size()); + EXPECT_FALSE(client->invalid_frame_token_called()); + EXPECT_EQ(1, client->on_process_swap_message_count()); + EXPECT_EQ(1, client->on_message_dispatch_error_count()); +} + // Tests that if we have already enqueued a callback for a frame token, that if // a request for an earlier frame token arrives, that it is still enqueued. Then // once the large frame token arrives, both are processed. @@ -286,10 +625,23 @@ EXPECT_FALSE(enqueuer->frame_token_callback_called()); EXPECT_FALSE(client->invalid_frame_token_called()); + const uint32_t smaller_frame_token = 42; + IPC::Message msg(0, 1, IPC::Message::PRIORITY_NORMAL); + std::vector<IPC::Message> messages; + messages.push_back(msg); + + // Enqueuing for a smaller token, which has not yet arrived, should still + // enqueue. + queue->OnFrameSwapMessagesReceived(smaller_frame_token, std::move(messages)); + EXPECT_EQ(2u, queue->size()); + EXPECT_EQ(0, client->on_process_swap_message_count()); + // Process both with the larger frame token arriving. queue->DidProcessFrame(larger_frame_token); EXPECT_EQ(0u, queue->size()); EXPECT_FALSE(client->invalid_frame_token_called()); + EXPECT_EQ(1, client->on_process_swap_message_count()); + EXPECT_EQ(0, client->on_message_dispatch_error_count()); EXPECT_TRUE(enqueuer->frame_token_callback_called()); }
diff --git a/content/browser/renderer_host/mock_render_widget_host.cc b/content/browser/renderer_host/mock_render_widget_host.cc index cd0b249..fb78b02 100644 --- a/content/browser/renderer_host/mock_render_widget_host.cc +++ b/content/browser/renderer_host/mock_render_widget_host.cc
@@ -7,6 +7,28 @@ #include "components/viz/test/mock_compositor_frame_sink_client.h" #include "content/browser/renderer_host/frame_token_message_queue.h" +namespace { +class TestFrameTokenMessageQueue : public content::FrameTokenMessageQueue { + public: + TestFrameTokenMessageQueue() = default; + ~TestFrameTokenMessageQueue() override = default; + + uint32_t processed_frame_messages_count() { + return processed_frame_messages_count_; + } + + protected: + void ProcessSwapMessages(std::vector<IPC::Message> messages) override { + processed_frame_messages_count_++; + } + + private: + uint32_t processed_frame_messages_count_ = 0; + + DISALLOW_COPY_AND_ASSIGN(TestFrameTokenMessageQueue); +}; +} // namespace + namespace content { MockRenderWidgetHost::~MockRenderWidgetHost() {} @@ -37,6 +59,13 @@ input_router_.reset(new MockInputRouter(this)); } +uint32_t MockRenderWidgetHost::processed_frame_messages_count() { + CHECK(frame_token_message_queue_); + return static_cast<TestFrameTokenMessageQueue*>( + frame_token_message_queue_.get()) + ->processed_frame_messages_count(); +} + // static MockRenderWidgetHost* MockRenderWidgetHost::Create( RenderWidgetHostDelegate* delegate, @@ -77,7 +106,7 @@ process, routing_id, /*hidden=*/false, - std::make_unique<FrameTokenMessageQueue>()), + std::make_unique<TestFrameTokenMessageQueue>()), new_content_rendering_timeout_fired_(false), fling_scheduler_(std::make_unique<FlingScheduler>(this)) { acked_touch_event_type_ = blink::WebInputEvent::Type::kUndefined;
diff --git a/content/browser/renderer_host/mock_render_widget_host.h b/content/browser/renderer_host/mock_render_widget_host.h index c0ac15d..8e65251 100644 --- a/content/browser/renderer_host/mock_render_widget_host.h +++ b/content/browser/renderer_host/mock_render_widget_host.h
@@ -58,6 +58,8 @@ InputRouter* input_router() { return input_router_.get(); } + uint32_t processed_frame_messages_count(); + static MockRenderWidgetHost* Create(RenderWidgetHostDelegate* delegate, RenderProcessHost* process, int32_t routing_id);
diff --git a/content/browser/renderer_host/render_widget_host_impl.cc b/content/browser/renderer_host/render_widget_host_impl.cc index ab5991c..de4fd2c 100644 --- a/content/browser/renderer_host/render_widget_host_impl.cc +++ b/content/browser/renderer_host/render_widget_host_impl.cc
@@ -95,7 +95,6 @@ #include "gpu/GLES2/gl2extchromium.h" #include "gpu/command_buffer/service/gpu_switches.h" #include "gpu/ipc/common/gpu_messages.h" -#include "mojo/public/cpp/bindings/callback_helpers.h" #include "mojo/public/cpp/bindings/pending_receiver.h" #include "mojo/public/cpp/system/platform_handle.h" #include "net/base/filename_util.h" @@ -633,7 +632,6 @@ blink_frame_widget_host_receiver_.reset(); blink_frame_widget_.reset(); frame_widget_input_handler_.reset(); - widget_compositor_.reset(); return std::make_pair( blink_frame_widget_host_receiver_.BindNewEndpointAndPassRemote(), blink_frame_widget_.BindNewEndpointAndPassReceiver()); @@ -648,7 +646,6 @@ blink_frame_widget_host_receiver_.reset(); blink_frame_widget_.reset(); frame_widget_input_handler_.reset(); - widget_compositor_.reset(); blink_frame_widget_host_receiver_.Bind(std::move(frame_widget_host)); blink_frame_widget_.Bind(std::move(frame_widget)); } @@ -704,6 +701,8 @@ IPC_MESSAGE_HANDLER(WidgetHostMsg_RequestSetBounds, OnRequestSetBounds) IPC_MESSAGE_HANDLER(DragHostMsg_StartDragging, OnStartDragging) IPC_MESSAGE_HANDLER(DragHostMsg_UpdateDragCursor, OnUpdateDragCursor) + IPC_MESSAGE_HANDLER(WidgetHostMsg_FrameSwapMessages, + OnFrameSwapMessagesReceived) IPC_MESSAGE_UNHANDLED(handled = false) IPC_END_MESSAGE_MAP() @@ -2042,6 +2041,13 @@ view->UpdateDragCursor(current_op); } +void RenderWidgetHostImpl::OnFrameSwapMessagesReceived( + uint32_t frame_token, + std::vector<IPC::Message> messages) { + frame_token_message_queue_->OnFrameSwapMessagesReceived(frame_token, + std::move(messages)); +} + void RenderWidgetHostImpl::RendererExited() { if (!renderer_initialized_) return; @@ -2707,6 +2713,16 @@ bad_message::RWH_INVALID_FRAME_TOKEN); } +void RenderWidgetHostImpl::OnMessageDispatchError(const IPC::Message& message) { + RenderProcessHost* rph = GetProcess(); + rph->OnBadMessageReceived(message); +} + +void RenderWidgetHostImpl::OnProcessSwapMessage(const IPC::Message& message) { + RenderProcessHost* rph = GetProcess(); + rph->OnMessageReceived(message); +} + bool RenderWidgetHostImpl::RequestKeyboardLock( base::Optional<base::flat_set<ui::DomCode>> codes) { if (!delegate_) { @@ -2878,23 +2894,6 @@ return false; } -void RenderWidgetHostImpl::InsertVisualStateCallback( - VisualStateCallback callback) { - if (!blink_frame_widget_) { - std::move(callback).Run(false); - return; - } - - if (!widget_compositor_) { - blink_frame_widget_->BindWidgetCompositor( - widget_compositor_.BindNewPipeAndPassReceiver()); - } - - widget_compositor_->VisualStateRequest(base::BindOnce( - [](VisualStateCallback callback) { std::move(callback).Run(true); }, - mojo::WrapCallbackWithDefaultInvokeIfNotRun(std::move(callback), false))); -} - const mojo::AssociatedRemote<blink::mojom::FrameWidget>& RenderWidgetHostImpl::GetAssociatedFrameWidget() { return blink_frame_widget_;
diff --git a/content/browser/renderer_host/render_widget_host_impl.h b/content/browser/renderer_host/render_widget_host_impl.h index cbd65d4..8236256 100644 --- a/content/browser/renderer_host/render_widget_host_impl.h +++ b/content/browser/renderer_host/render_widget_host_impl.h
@@ -16,7 +16,6 @@ #include <vector> #include "base/callback.h" -#include "base/containers/flat_map.h" #include "base/containers/flat_set.h" #include "base/containers/queue.h" #include "base/gtest_prod_util.h" @@ -728,6 +727,8 @@ // FrameTokenMessageQueue::Client: void OnInvalidFrameToken(uint32_t frame_token) override; + void OnMessageDispatchError(const IPC::Message& message) override; + void OnProcessSwapMessage(const IPC::Message& message) override; void ProgressFlingIfNeeded(base::TimeTicks current_time); void StopFling(); @@ -784,14 +785,6 @@ // Add/ClearPendingUserActivation() for details. bool RemovePendingUserActivationIfAvailable(); - // Roundtrips through the renderer and compositor pipeline to ensure that any - // changes to the contents resulting from operations executed prior to this - // call are visible on screen. The call completes asynchronously by running - // the supplied |callback| with a value of true upon successful completion and - // false otherwise when the widget is destroyed. - using VisualStateCallback = base::OnceCallback<void(bool)>; - void InsertVisualStateCallback(VisualStateCallback callback); - const mojo::AssociatedRemote<blink::mojom::FrameWidget>& GetAssociatedFrameWidget(); @@ -917,6 +910,8 @@ const gfx::Vector2d& bitmap_offset_in_dip, const DragEventSourceInfo& event_info); void OnUpdateDragCursor(blink::WebDragOperation current_op); + void OnFrameSwapMessagesReceived(uint32_t frame_token, + std::vector<IPC::Message> messages); // blink::mojom::FrameWidgetHost overrides. void AnimateDoubleTapZoomInMainFrame(const gfx::Point& tap_point, @@ -1331,8 +1326,6 @@ blink_widget_host_receiver_{this}; mojo::AssociatedRemote<blink::mojom::Widget> blink_widget_; - mojo::Remote<blink::mojom::WidgetCompositor> widget_compositor_; - base::WeakPtrFactory<RenderWidgetHostImpl> weak_factory_{this}; DISALLOW_COPY_AND_ASSIGN(RenderWidgetHostImpl);
diff --git a/content/browser/renderer_host/render_widget_host_unittest.cc b/content/browser/renderer_host/render_widget_host_unittest.cc index e91f614..be9220a 100644 --- a/content/browser/renderer_host/render_widget_host_unittest.cc +++ b/content/browser/renderer_host/render_widget_host_unittest.cc
@@ -2060,6 +2060,137 @@ ASSERT_FALSE(host_->input_router()->HasPendingEvents()); } +// Check that if messages of a frame arrive earlier than the frame itself, we +// queue the messages until the frame arrives and then process them. +TEST_F(RenderWidgetHostTest, FrameToken_MessageThenFrame) { + constexpr uint32_t frame_token = 1; + std::vector<IPC::Message> messages; + messages.push_back(WidgetHostMsg_Close(5)); + + EXPECT_EQ(0u, host_->frame_token_message_queue_->size()); + EXPECT_EQ(0u, host_->processed_frame_messages_count()); + + host_->OnMessageReceived( + WidgetHostMsg_FrameSwapMessages(0, frame_token, messages)); + EXPECT_EQ(1u, host_->frame_token_message_queue_->size()); + EXPECT_EQ(0u, host_->processed_frame_messages_count()); + + view_->OnFrameTokenChanged(frame_token); + EXPECT_EQ(0u, host_->frame_token_message_queue_->size()); + EXPECT_EQ(1u, host_->processed_frame_messages_count()); +} + +// Check that if a frame arrives earlier than its messages, we process the +// messages immedtiately. +TEST_F(RenderWidgetHostTest, FrameToken_FrameThenMessage) { + constexpr uint32_t frame_token = 1; + std::vector<IPC::Message> messages; + messages.push_back(WidgetHostMsg_Close(5)); + + EXPECT_EQ(0u, host_->frame_token_message_queue_->size()); + EXPECT_EQ(0u, host_->processed_frame_messages_count()); + + view_->OnFrameTokenChanged(frame_token); + EXPECT_EQ(0u, host_->frame_token_message_queue_->size()); + EXPECT_EQ(0u, host_->processed_frame_messages_count()); + + host_->OnMessageReceived( + WidgetHostMsg_FrameSwapMessages(0, frame_token, messages)); + EXPECT_EQ(0u, host_->frame_token_message_queue_->size()); + EXPECT_EQ(1u, host_->processed_frame_messages_count()); +} + +// Check that if messages of multiple frames arrive before the frames, we +// process each message once it frame arrives. +TEST_F(RenderWidgetHostTest, FrameToken_MultipleMessagesThenTokens) { + constexpr uint32_t frame_token1 = 1; + constexpr uint32_t frame_token2 = 2; + std::vector<IPC::Message> messages1; + std::vector<IPC::Message> messages2; + messages1.push_back(WidgetHostMsg_Close(5)); + messages2.push_back(WidgetHostMsg_Close(6)); + + EXPECT_EQ(0u, host_->frame_token_message_queue_->size()); + EXPECT_EQ(0u, host_->processed_frame_messages_count()); + + host_->OnMessageReceived( + WidgetHostMsg_FrameSwapMessages(0, frame_token1, messages1)); + EXPECT_EQ(1u, host_->frame_token_message_queue_->size()); + EXPECT_EQ(0u, host_->processed_frame_messages_count()); + + host_->OnMessageReceived( + WidgetHostMsg_FrameSwapMessages(0, frame_token2, messages2)); + EXPECT_EQ(2u, host_->frame_token_message_queue_->size()); + EXPECT_EQ(0u, host_->processed_frame_messages_count()); + + view_->OnFrameTokenChanged(frame_token1); + EXPECT_EQ(1u, host_->frame_token_message_queue_->size()); + EXPECT_EQ(1u, host_->processed_frame_messages_count()); + view_->OnFrameTokenChanged(frame_token2); + EXPECT_EQ(0u, host_->frame_token_message_queue_->size()); + EXPECT_EQ(2u, host_->processed_frame_messages_count()); +} + +// Check that if multiple frames arrive before their messages, each message is +// processed immediately as soon as it arrives. +TEST_F(RenderWidgetHostTest, FrameToken_MultipleTokensThenMessages) { + constexpr uint32_t frame_token1 = 1; + constexpr uint32_t frame_token2 = 2; + std::vector<IPC::Message> messages1; + std::vector<IPC::Message> messages2; + messages1.push_back(WidgetHostMsg_Close(5)); + messages2.push_back(WidgetHostMsg_Close(6)); + + EXPECT_EQ(0u, host_->frame_token_message_queue_->size()); + EXPECT_EQ(0u, host_->processed_frame_messages_count()); + + view_->OnFrameTokenChanged(frame_token1); + EXPECT_EQ(0u, host_->frame_token_message_queue_->size()); + EXPECT_EQ(0u, host_->processed_frame_messages_count()); + + view_->OnFrameTokenChanged(frame_token2); + EXPECT_EQ(0u, host_->frame_token_message_queue_->size()); + EXPECT_EQ(0u, host_->processed_frame_messages_count()); + + host_->OnMessageReceived( + WidgetHostMsg_FrameSwapMessages(0, frame_token1, messages1)); + EXPECT_EQ(0u, host_->frame_token_message_queue_->size()); + EXPECT_EQ(1u, host_->processed_frame_messages_count()); + + host_->OnMessageReceived( + WidgetHostMsg_FrameSwapMessages(0, frame_token2, messages2)); + EXPECT_EQ(0u, host_->frame_token_message_queue_->size()); + EXPECT_EQ(2u, host_->processed_frame_messages_count()); +} + +// Check that if one frame is lost but its messages arrive, we process the +// messages on the arrival of the next frame. +TEST_F(RenderWidgetHostTest, FrameToken_DroppedFrame) { + constexpr uint32_t frame_token1 = 1; + constexpr uint32_t frame_token2 = 2; + std::vector<IPC::Message> messages1; + std::vector<IPC::Message> messages2; + messages1.push_back(WidgetHostMsg_Close(5)); + messages2.push_back(WidgetHostMsg_Close(6)); + + EXPECT_EQ(0u, host_->frame_token_message_queue_->size()); + EXPECT_EQ(0u, host_->processed_frame_messages_count()); + + host_->OnMessageReceived( + WidgetHostMsg_FrameSwapMessages(0, frame_token1, messages1)); + EXPECT_EQ(1u, host_->frame_token_message_queue_->size()); + EXPECT_EQ(0u, host_->processed_frame_messages_count()); + + host_->OnMessageReceived( + WidgetHostMsg_FrameSwapMessages(0, frame_token2, messages2)); + EXPECT_EQ(2u, host_->frame_token_message_queue_->size()); + EXPECT_EQ(0u, host_->processed_frame_messages_count()); + + view_->OnFrameTokenChanged(frame_token2); + EXPECT_EQ(0u, host_->frame_token_message_queue_->size()); + EXPECT_EQ(2u, host_->processed_frame_messages_count()); +} + // If a navigation happens while the widget is hidden, we shouldn't show // contents of the previous page when we become visible. TEST_F(RenderWidgetHostTest, NavigateInBackgroundShowsBlank) {
diff --git a/content/browser/service_worker/service_worker_disk_cache.cc b/content/browser/service_worker/service_worker_disk_cache.cc index 723b197..1f4a64a 100644 --- a/content/browser/service_worker/service_worker_disk_cache.cc +++ b/content/browser/service_worker/service_worker_disk_cache.cc
@@ -11,46 +11,6 @@ namespace content { -namespace { - -void DidReadInfo(base::WeakPtr<AppCacheResponseIO> reader, - scoped_refptr<HttpResponseInfoIOBuffer> io_buffer, - ServiceWorkerResponseReader::ReadResponseHeadCallback callback, - int result) { - DCHECK(io_buffer); - if (result < 0) { - std::move(callback).Run(result, /*response_head=*/nullptr, - /*metadata=*/nullptr); - return; - } - - const net::HttpResponseInfo* http_info = io_buffer->http_info.get(); - DCHECK(http_info); - DCHECK(http_info->headers); - - auto head = network::mojom::URLResponseHead::New(); - head->request_start = base::TimeTicks(); - head->response_start = base::TimeTicks::Now(); - head->request_time = http_info->request_time; - head->response_time = http_info->response_time; - head->headers = http_info->headers; - head->headers->GetMimeType(&head->mime_type); - head->headers->GetCharset(&head->charset); - head->content_length = io_buffer->response_data_size; - head->was_fetched_via_spdy = http_info->was_fetched_via_spdy; - head->was_alpn_negotiated = http_info->was_alpn_negotiated; - head->connection_info = http_info->connection_info; - head->alpn_negotiated_protocol = http_info->alpn_negotiated_protocol; - head->remote_endpoint = http_info->remote_endpoint; - head->cert_status = http_info->ssl_info.cert_status; - head->ssl_info = http_info->ssl_info; - - std::move(callback).Run(result, std::move(head), - std::move(http_info->metadata)); -} - -} // namespace - ServiceWorkerDiskCache::ServiceWorkerDiskCache() : AppCacheDiskCache(/*use_simple_cache=*/true) {} @@ -59,40 +19,11 @@ base::WeakPtr<AppCacheDiskCache> disk_cache) : AppCacheResponseReader(resource_id, std::move(disk_cache)) {} -void ServiceWorkerResponseReader::ReadResponseHead( - ReadResponseHeadCallback callback) { - auto io_buffer = base::MakeRefCounted<HttpResponseInfoIOBuffer>(); - ReadInfo(io_buffer.get(), base::BindOnce(&DidReadInfo, GetWeakPtr(), - io_buffer, std::move(callback))); -} - ServiceWorkerResponseWriter::ServiceWorkerResponseWriter( int64_t resource_id, base::WeakPtr<AppCacheDiskCache> disk_cache) : AppCacheResponseWriter(resource_id, std::move(disk_cache)) {} -void ServiceWorkerResponseWriter::WriteResponseHead( - const network::mojom::URLResponseHead& response_head, - int response_data_size, - net::CompletionOnceCallback callback) { - auto response_info = std::make_unique<net::HttpResponseInfo>(); - response_info->headers = response_head.headers; - if (response_head.ssl_info.has_value()) - response_info->ssl_info = *response_head.ssl_info; - response_info->was_fetched_via_spdy = response_head.was_fetched_via_spdy; - response_info->was_alpn_negotiated = response_head.was_alpn_negotiated; - response_info->alpn_negotiated_protocol = - response_head.alpn_negotiated_protocol; - response_info->connection_info = response_head.connection_info; - response_info->remote_endpoint = response_head.remote_endpoint; - response_info->response_time = response_head.response_time; - - auto info_buffer = - base::MakeRefCounted<HttpResponseInfoIOBuffer>(std::move(response_info)); - info_buffer->response_data_size = response_data_size; - WriteInfo(info_buffer.get(), std::move(callback)); -} - ServiceWorkerResponseMetadataWriter::ServiceWorkerResponseMetadataWriter( int64_t resource_id, base::WeakPtr<AppCacheDiskCache> disk_cache)
diff --git a/content/browser/service_worker/service_worker_disk_cache.h b/content/browser/service_worker/service_worker_disk_cache.h index 0f4669e9..b23fbfd7 100644 --- a/content/browser/service_worker/service_worker_disk_cache.h +++ b/content/browser/service_worker/service_worker_disk_cache.h
@@ -30,15 +30,6 @@ // storage::mojom::ServiceWorkerResourceReader. class CONTENT_EXPORT ServiceWorkerResponseReader : public AppCacheResponseReader { - public: - // Reads response headers and metadata associated with this reader from - // storage. This is an adaptor method of ReadInfo(). - using ReadResponseHeadCallback = - base::OnceCallback<void(int result, - network::mojom::URLResponseHeadPtr response_head, - scoped_refptr<net::IOBufferWithSize> metadata)>; - void ReadResponseHead(ReadResponseHeadCallback callback); - protected: // Should only be constructed by the storage class. friend class ServiceWorkerStorage; @@ -51,16 +42,6 @@ // storage::mojom::ServiceWorkerResourceWriter. class CONTENT_EXPORT ServiceWorkerResponseWriter : public AppCacheResponseWriter { - public: - // Writes response headers for a service worker script to storage. Currently - // this just converts |response_head| to HttpResponseInfo and calls - // WriteInfo(). |response_head| must be examined by - // service_worker_loader_helpers::CheckResponseHead() before calling this - // method. - void WriteResponseHead(const network::mojom::URLResponseHead& response_head, - int response_data_size, - net::CompletionOnceCallback callback); - protected: // Should only be constructed by the storage class. friend class ServiceWorkerStorage;
diff --git a/content/browser/service_worker/service_worker_new_script_loader_unittest.cc b/content/browser/service_worker/service_worker_new_script_loader_unittest.cc index 8f8d0bb..a573e2ae 100644 --- a/content/browser/service_worker/service_worker_new_script_loader_unittest.cc +++ b/content/browser/service_worker/service_worker_new_script_loader_unittest.cc
@@ -94,11 +94,9 @@ mock_server_->Get(url_request.url); // Pass the response header to the client. - net::HttpResponseInfo info; - info.headers = base::MakeRefCounted<net::HttpResponseHeaders>( - net::HttpUtil::AssembleRawHeaders(response.headers)); auto response_head = network::mojom::URLResponseHead::New(); - response_head->headers = info.headers; + response_head->headers = base::MakeRefCounted<net::HttpResponseHeaders>( + net::HttpUtil::AssembleRawHeaders(response.headers)); response_head->headers->GetMimeType(&response_head->mime_type); response_head->network_accessed = access_network_; if (response.has_certificate_error) {
diff --git a/content/browser/service_worker/service_worker_registry.cc b/content/browser/service_worker/service_worker_registry.cc index 9f3fe82a..e09ce6a 100644 --- a/content/browser/service_worker/service_worker_registry.cc +++ b/content/browser/service_worker/service_worker_registry.cc
@@ -1390,7 +1390,7 @@ } if (!policy_updates.empty()) - storage()->ApplyPolicyUpdates(std::move(policy_updates)); + GetRemoteStorageControl()->ApplyPolicyUpdates(std::move(policy_updates)); } bool ServiceWorkerRegistry::ShouldPurgeOnShutdown(const url::Origin& origin) {
diff --git a/content/browser/service_worker/service_worker_resource_ops.cc b/content/browser/service_worker/service_worker_resource_ops.cc index bc2d6b95..a5070177 100644 --- a/content/browser/service_worker/service_worker_resource_ops.cc +++ b/content/browser/service_worker/service_worker_resource_ops.cc
@@ -53,12 +53,6 @@ return; } - // URLResponseHead fields filled here are the same as - // ServiceWorkerUtils::CreateResourceResponseHeadAndMetadata(). Once - // https://crbug.com/1060076 is done CreateResourceResponseHeadAndMetadata() - // will be removed, but we still need HttpResponseInfo -> URLResponseHead - // conversion to restore a response from the storage. - // TODO(bashi): Remove the above comment ater the issue is closed. auto head = network::mojom::URLResponseHead::New(); head->request_time = http_info->request_time; head->response_time = http_info->response_time;
diff --git a/content/browser/service_worker/service_worker_version.h b/content/browser/service_worker/service_worker_version.h index 726d5072..ef4f6f4 100644 --- a/content/browser/service_worker/service_worker_version.h +++ b/content/browser/service_worker/service_worker_version.h
@@ -1027,13 +1027,13 @@ // the subresource loader factories are updated. bool initialize_global_scope_after_main_script_loaded_ = false; - // Populated via net::HttpResponseInfo of the main script. + // Populated via network::mojom::URLResponseHead of the main script. std::unique_ptr<MainScriptResponse> main_script_response_; // DevTools requires each service worker's script receive time, even for // the ones that haven't started. However, a ServiceWorkerVersion's field // |main_script_http_info_| is not set until starting up. Rather than - // reading HttpResponseInfo for all service workers from disk cache and + // reading URLResponseHead for all service workers from disk cache and // populating |main_script_http_info_| just in order to expose that timestamp, // we provide that timestamp here. base::Time script_response_time_for_devtools_;
diff --git a/content/common/android/cpu_time_metrics.cc b/content/common/android/cpu_time_metrics.cc index ebd1b39b..86582b80 100644 --- a/content/common/android/cpu_time_metrics.cc +++ b/content/common/android/cpu_time_metrics.cc
@@ -13,6 +13,7 @@ #include "base/bind_helpers.h" #include "base/command_line.h" #include "base/containers/flat_map.h" +#include "base/cpu.h" #include "base/lazy_instance.h" #include "base/logging.h" #include "base/metrics/histogram_macros.h" @@ -91,9 +92,8 @@ } } -std::string GetPerCoreCpuTimeHistogramName( - ProcessTypeForUma process_type, - base::ProcessMetrics::CoreType core_type) { +std::string GetPerCoreCpuTimeHistogramName(ProcessTypeForUma process_type, + base::CPU::CoreType core_type) { std::string process_suffix; switch (process_type) { case ProcessTypeForUma::kBrowser: @@ -112,28 +112,28 @@ std::string cpu_suffix; switch (core_type) { - case base::ProcessMetrics::CoreType::kUnknown: + case base::CPU::CoreType::kUnknown: cpu_suffix = "Unknown"; break; - case base::ProcessMetrics::CoreType::kOther: + case base::CPU::CoreType::kOther: cpu_suffix = "Other"; break; - case base::ProcessMetrics::CoreType::kSymmetric: + case base::CPU::CoreType::kSymmetric: cpu_suffix = "Symmetric"; break; - case base::ProcessMetrics::CoreType::kBigLittle_Little: + case base::CPU::CoreType::kBigLittle_Little: cpu_suffix = "BigLittle.Little"; break; - case base::ProcessMetrics::CoreType::kBigLittle_Big: + case base::CPU::CoreType::kBigLittle_Big: cpu_suffix = "BigLittle.Big"; break; - case base::ProcessMetrics::CoreType::kBigLittleBigger_Little: + case base::CPU::CoreType::kBigLittleBigger_Little: cpu_suffix = "BigLittleBigger.Little"; break; - case base::ProcessMetrics::CoreType::kBigLittleBigger_Big: + case base::CPU::CoreType::kBigLittleBigger_Big: cpu_suffix = "BigLittleBigger.Big"; break; - case base::ProcessMetrics::CoreType::kBigLittleBigger_Bigger: + case base::CPU::CoreType::kBigLittleBigger_Bigger: cpu_suffix = "BigLittleBigger.Bigger"; break; } @@ -251,7 +251,7 @@ class TimeInStateReporter { public: TimeInStateReporter(ProcessTypeForUma process_type, - base::ProcessMetrics::CoreType core_type) + base::CPU::CoreType core_type) : histogram_(GetPerCoreCpuTimeHistogramName(process_type, core_type), 1, // ScaledLinearHistogram requires buckets of size 1. Each @@ -452,7 +452,7 @@ uint32_t last_updated_cycle = 0; CpuTimeMetricsThreadType type = CpuTimeMetricsThreadType::kOtherThread; - using ClusterFrequency = std::tuple<base::ProcessMetrics::CoreType, + using ClusterFrequency = std::tuple<base::CPU::CoreType, uint32_t /*cluster_core_index*/, uint32_t /*frequency_mhz*/>; base::flat_map<ClusterFrequency, base::TimeDelta /*time_in_state*/> @@ -499,8 +499,7 @@ base::TimeDelta reported_cpu_time_; base::flat_map<base::PlatformThreadId, ThreadDetails> thread_details_; std::array<std::unique_ptr<TimeInStateReporter>, - static_cast<size_t>(base::ProcessMetrics::CoreType::kMaxValue) + - 1u> + static_cast<size_t>(base::CPU::CoreType::kMaxValue) + 1u> time_in_state_reporters_ = {}; // Stored as instance variables to avoid allocation churn. base::ProcessMetrics::CPUUsagePerThread cumulative_thread_times_;
diff --git a/content/common/frame_messages.h b/content/common/frame_messages.h index eea8b6a..0973d39 100644 --- a/content/common/frame_messages.h +++ b/content/common/frame_messages.h
@@ -426,6 +426,10 @@ content::CustomContextMenuContext /* custom_context */, unsigned /* action */) +// Requests that the RenderFrame send back a response after waiting for the +// commit, activation and frame swap of the current DOM tree in blink. +IPC_MESSAGE_ROUTED1(FrameMsg_VisualStateRequest, uint64_t /* id */) + #if BUILDFLAG(ENABLE_PLUGINS) // Notifies the renderer of updates to the Plugin Power Saver origin allowlist. IPC_MESSAGE_ROUTED1(FrameMsg_UpdatePluginContentOriginAllowlist, @@ -630,6 +634,10 @@ uint32_t /* the offset of the text in the document */, gfx::Range /* selection range in the document */) +// Sent as a response to FrameMsg_VisualStateRequest. +// The message is delivered using RenderWidget::QueueMessage. +IPC_MESSAGE_ROUTED1(FrameHostMsg_VisualStateResponse, uint64_t /* id */) + // Adding a new message? Stick to the sort order above: first platform // independent FrameMsg, then ifdefs for platform specific FrameMsg, then // platform independent FrameHostMsg, then ifdefs for platform specific
diff --git a/content/common/widget_messages.h b/content/common/widget_messages.h index e8a1e83..43dd821 100644 --- a/content/common/widget_messages.h +++ b/content/common/widget_messages.h
@@ -71,6 +71,12 @@ IPC_MESSAGE_ROUTED1(WidgetMsg_SetViewportIntersection, blink::ViewportIntersectionState /* intersection_state */) + +// Sent by the browser to synchronize with the next compositor frame by +// requesting an ACK be queued. Used only for tests. +IPC_MESSAGE_ROUTED1(WidgetMsg_WaitForNextFrameForTests, + int /* main_frame_thread_observer_routing_id */) + // // Renderer -> Browser Messages. // @@ -86,8 +92,20 @@ // APIs, and the browser may ignore this message. IPC_MESSAGE_ROUTED1(WidgetHostMsg_RequestSetBounds, gfx::Rect /* bounds */) +// Sends a set of queued messages that were being held until the next +// CompositorFrame is being submitted from the renderer. These messages are +// sent before the OnRenderFrameMetadataChanged message is sent (via mojo) and +// before the CompositorFrame is sent to the viz service. The |frame_token| +// will match the token in the about-to-be-submitted CompositorFrame. +IPC_MESSAGE_ROUTED2(WidgetHostMsg_FrameSwapMessages, + uint32_t /* frame_token */, + std::vector<IPC::Message> /* messages */) + // Indicates that the render widget has been closed in response to a // Close message. IPC_MESSAGE_CONTROL1(WidgetHostMsg_Close_ACK, int /* old_route_id */) +// Sent in reply to WidgetMsg_WaitForNextFrameForTests. +IPC_MESSAGE_ROUTED0(WidgetHostMsg_WaitForNextFrameForTests_ACK) + #endif // CONTENT_COMMON_WIDGET_MESSAGES_H_
diff --git a/content/public/browser/render_frame_host.h b/content/public/browser/render_frame_host.h index eb08034..f285e96 100644 --- a/content/public/browser/render_frame_host.h +++ b/content/public/browser/render_frame_host.h
@@ -305,7 +305,7 @@ // changes to the contents resulting from operations executed prior to this // call are visible on screen. The call completes asynchronously by running // the supplied |callback| with a value of true upon successful completion and - // false otherwise when the widget is destroyed. + // false otherwise (when the frame is destroyed, detached, etc..). using VisualStateCallback = base::OnceCallback<void(bool)>; virtual void InsertVisualStateCallback(VisualStateCallback callback) = 0;
diff --git a/content/public/test/browser_test_utils.cc b/content/public/test/browser_test_utils.cc index d377d21..b8d0ab4 100644 --- a/content/public/test/browser_test_utils.cc +++ b/content/public/test/browser_test_utils.cc
@@ -2635,25 +2635,40 @@ MainThreadFrameObserver::MainThreadFrameObserver( RenderWidgetHost* render_widget_host) : render_widget_host_(render_widget_host), - routing_id_(render_widget_host_->GetProcess()->GetNextRoutingID()) {} + routing_id_(render_widget_host_->GetProcess()->GetNextRoutingID()) { + // TODO(lfg): We should look into adding a way to observe RenderWidgetHost + // messages similarly to what WebContentsObserver can do with RFH and RVW. + render_widget_host_->GetProcess()->AddRoute(routing_id_, this); +} -MainThreadFrameObserver::~MainThreadFrameObserver() = default; +MainThreadFrameObserver::~MainThreadFrameObserver() { + render_widget_host_->GetProcess()->RemoveRoute(routing_id_); +} void MainThreadFrameObserver::Wait() { DCHECK_CURRENTLY_ON(BrowserThread::UI); - static_cast<RenderWidgetHostImpl*>(render_widget_host_) - ->InsertVisualStateCallback(base::BindOnce(&MainThreadFrameObserver::Quit, - base::Unretained(this))); + render_widget_host_->Send(new WidgetMsg_WaitForNextFrameForTests( + render_widget_host_->GetRoutingID(), routing_id_)); base::RunLoop run_loop; quit_closure_ = run_loop.QuitClosure(); run_loop.Run(); } -void MainThreadFrameObserver::Quit(bool) { +void MainThreadFrameObserver::Quit() { if (quit_closure_) std::move(quit_closure_).Run(); } +bool MainThreadFrameObserver::OnMessageReceived(const IPC::Message& msg) { + if (msg.type() == WidgetHostMsg_WaitForNextFrameForTests_ACK::ID && + msg.routing_id() == routing_id_) { + GetUIThreadTaskRunner({})->PostTask( + FROM_HERE, + base::BindOnce(&MainThreadFrameObserver::Quit, base::Unretained(this))); + } + return true; +} + InputMsgWatcher::InputMsgWatcher(RenderWidgetHost* render_widget_host, blink::WebInputEvent::Type type) : render_widget_host_(render_widget_host),
diff --git a/content/public/test/browser_test_utils.h b/content/public/test/browser_test_utils.h index 2ef08b4..49ef8c989 100644 --- a/content/public/test/browser_test_utils.h +++ b/content/public/test/browser_test_utils.h
@@ -1308,17 +1308,20 @@ // So while the ACK can arrive before a CompositorFrame submission occurs. The // processing does not occur until after the FrameToken for that frame // submission arrives to the main thread. -class MainThreadFrameObserver { +class MainThreadFrameObserver : public IPC::Listener { public: explicit MainThreadFrameObserver(RenderWidgetHost* render_widget_host); - ~MainThreadFrameObserver(); + ~MainThreadFrameObserver() override; // Synchronizes the browser main thread with the renderer main thread and impl // thread. void Wait(); + // Overridden IPC::Listener methods. + bool OnMessageReceived(const IPC::Message& msg) override; + private: - void Quit(bool); + void Quit(); RenderWidgetHost* render_widget_host_; base::OnceClosure quit_closure_;
diff --git a/content/public/test/fake_frame_widget.h b/content/public/test/fake_frame_widget.h index 369d8fac..c52cc1ac 100644 --- a/content/public/test/fake_frame_widget.h +++ b/content/public/test/fake_frame_widget.h
@@ -63,9 +63,6 @@ void EnableDeviceEmulation( const blink::DeviceEmulationParams& parameters) override {} void DisableDeviceEmulation() override {} - void BindWidgetCompositor( - mojo::PendingReceiver<blink::mojom::WidgetCompositor> receiver) override { - } mojo::AssociatedReceiver<blink::mojom::FrameWidget> receiver_; base::i18n::TextDirection text_direction_ =
diff --git a/content/renderer/BUILD.gn b/content/renderer/BUILD.gn index fe96c236..484b5e0 100644 --- a/content/renderer/BUILD.gn +++ b/content/renderer/BUILD.gn
@@ -74,6 +74,8 @@ "frame_blame_context.h", "frame_owner_properties_converter.cc", "frame_owner_properties_converter.h", + "frame_swap_message_queue.cc", + "frame_swap_message_queue.h", "gpu_benchmarking_extension.cc", "gpu_benchmarking_extension.h", "history_entry.cc", @@ -191,6 +193,8 @@ "performance_manager/v8_per_frame_memory_reporter_impl.h", "peripheral_content_heuristic.cc", "peripheral_content_heuristic.h", + "queue_message_swap_promise.cc", + "queue_message_swap_promise.h", "render_frame_impl.cc", "render_frame_impl.h", "render_frame_metadata_observer_impl.cc",
diff --git a/content/renderer/android/synchronous_layer_tree_frame_sink_impl.cc b/content/renderer/android/synchronous_layer_tree_frame_sink_impl.cc index e84687d4..2883b6e 100644 --- a/content/renderer/android/synchronous_layer_tree_frame_sink_impl.cc +++ b/content/renderer/android/synchronous_layer_tree_frame_sink_impl.cc
@@ -34,6 +34,7 @@ #include "content/common/android/sync_compositor_statics.h" #include "content/common/view_messages.h" #include "content/public/common/content_switches.h" +#include "content/renderer/frame_swap_message_queue.h" #include "content/renderer/render_thread_impl.h" #include "gpu/command_buffer/client/context_support.h" #include "gpu/command_buffer/client/gles2_interface.h" @@ -153,6 +154,7 @@ uint32_t layer_tree_frame_sink_id, std::unique_ptr<viz::BeginFrameSource> synthetic_begin_frame_source, blink::SynchronousCompositorRegistry* registry, + scoped_refptr<FrameSwapMessageQueue> frame_swap_message_queue, mojo::PendingRemote<viz::mojom::CompositorFrameSink> compositor_frame_sink_remote, mojo::PendingReceiver<viz::mojom::CompositorFrameSinkClient> @@ -165,6 +167,7 @@ registry_(registry), sender_(sender), memory_policy_(0u), + frame_swap_message_queue_(frame_swap_message_queue), unbound_compositor_frame_sink_(std::move(compositor_frame_sink_remote)), unbound_client_(std::move(client_receiver)), synthetic_begin_frame_source_(std::move(synthetic_begin_frame_source)), @@ -552,6 +555,17 @@ DCHECK(CalledOnValidThread()); if (sync_client_) sync_client_->DidActivatePendingTree(); + DeliverMessages(); +} + +void SynchronousLayerTreeFrameSinkImpl::DeliverMessages() { + std::vector<std::unique_ptr<IPC::Message>> messages; + std::unique_ptr<FrameSwapMessageQueue::SendMessageScope> send_message_scope = + frame_swap_message_queue_->AcquireSendMessageScope(); + frame_swap_message_queue_->DrainMessages(&messages); + for (auto& msg : messages) { + Send(msg.release()); + } } bool SynchronousLayerTreeFrameSinkImpl::Send(IPC::Message* message) {
diff --git a/content/renderer/android/synchronous_layer_tree_frame_sink_impl.h b/content/renderer/android/synchronous_layer_tree_frame_sink_impl.h index cae9af0e..f640c139 100644 --- a/content/renderer/android/synchronous_layer_tree_frame_sink_impl.h +++ b/content/renderer/android/synchronous_layer_tree_frame_sink_impl.h
@@ -54,6 +54,8 @@ namespace content { +class FrameSwapMessageQueue; + // Specialization of the output surface that adapts it to implement the // content::SynchronousCompositor public API. This class effects an "inversion // of control" - enabling drawing to be orchestrated by the embedding @@ -76,6 +78,7 @@ uint32_t layer_tree_frame_sink_id, std::unique_ptr<viz::BeginFrameSource> begin_frame_source, blink::SynchronousCompositorRegistry* registry, + scoped_refptr<FrameSwapMessageQueue> frame_swap_message_queue, mojo::PendingRemote<viz::mojom::CompositorFrameSink> compositor_frame_sink_remote, mojo::PendingReceiver<viz::mojom::CompositorFrameSinkClient> @@ -154,6 +157,7 @@ cc::ManagedMemoryPolicy memory_policy_; bool in_software_draw_ = false; bool did_submit_frame_ = false; + scoped_refptr<FrameSwapMessageQueue> frame_swap_message_queue_; mojo::PendingRemote<viz::mojom::CompositorFrameSink> unbound_compositor_frame_sink_;
diff --git a/content/renderer/compositor/compositor_dependencies.h b/content/renderer/compositor/compositor_dependencies.h index 766f795..c820413 100644 --- a/content/renderer/compositor/compositor_dependencies.h +++ b/content/renderer/compositor/compositor_dependencies.h
@@ -35,6 +35,7 @@ } // namespace blink namespace content { +class FrameSwapMessageQueue; class RenderWidget; class CONTENT_EXPORT CompositorDependencies { @@ -56,6 +57,7 @@ std::unique_ptr<cc::RenderFrameMetadataObserver>)>; virtual void RequestNewLayerTreeFrameSink( RenderWidget* render_widget, + scoped_refptr<FrameSwapMessageQueue> frame_swap_message_queue, const GURL& url, LayerTreeFrameSinkCallback callback, const char* client_name) = 0;
diff --git a/content/renderer/frame_swap_message_queue.cc b/content/renderer/frame_swap_message_queue.cc new file mode 100644 index 0000000..d85fe70 --- /dev/null +++ b/content/renderer/frame_swap_message_queue.cc
@@ -0,0 +1,157 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "content/renderer/frame_swap_message_queue.h" + +#include <algorithm> +#include <limits> +#include <memory> +#include <utility> + +#include "base/check.h" +#include "base/macros.h" +#include "base/stl_util.h" +#include "ipc/ipc_message.h" + +namespace content { + +class FrameSwapMessageSubQueue { + public: + FrameSwapMessageSubQueue() {} + virtual ~FrameSwapMessageSubQueue() {} + virtual bool Empty() const = 0; + virtual void QueueMessage(int source_frame_number, + std::unique_ptr<IPC::Message> msg, + bool* is_first) = 0; + virtual void DrainMessages( + int source_frame_number, + std::vector<std::unique_ptr<IPC::Message>>* messages) = 0; + + private: + DISALLOW_COPY_AND_ASSIGN(FrameSwapMessageSubQueue); +}; + +namespace { + +class SendMessageScopeImpl : public FrameSwapMessageQueue::SendMessageScope { + public: + SendMessageScopeImpl(base::Lock* lock) : auto_lock_(*lock) {} + ~SendMessageScopeImpl() override {} + + private: + base::AutoLock auto_lock_; +}; + +class VisualStateQueue : public FrameSwapMessageSubQueue { + public: + VisualStateQueue() = default; + + ~VisualStateQueue() override = default; + + bool Empty() const override { return queue_.empty(); } + + void QueueMessage(int source_frame_number, + std::unique_ptr<IPC::Message> msg, + bool* is_first) override { + if (is_first) + *is_first = (queue_.count(source_frame_number) == 0); + + queue_[source_frame_number].push_back(std::move(msg)); + } + + void DrainMessages( + int source_frame_number, + std::vector<std::unique_ptr<IPC::Message>>* messages) override { + auto end = queue_.upper_bound(source_frame_number); + for (auto i = queue_.begin(); i != end; i++) { + DCHECK(i->first <= source_frame_number); + std::move(i->second.begin(), i->second.end(), + std::back_inserter(*messages)); + } + queue_.erase(queue_.begin(), end); + } + + private: + std::map<int, std::vector<std::unique_ptr<IPC::Message>>> queue_; + + DISALLOW_COPY_AND_ASSIGN(VisualStateQueue); +}; + +} // namespace + +FrameSwapMessageQueue::FrameSwapMessageQueue(int32_t routing_id) + : visual_state_queue_(new VisualStateQueue()), routing_id_(routing_id) { + DETACH_FROM_THREAD(impl_thread_checker_); +} + +FrameSwapMessageQueue::~FrameSwapMessageQueue() {} + +bool FrameSwapMessageQueue::Empty() const { + base::AutoLock lock(lock_); + return next_drain_messages_.empty() && visual_state_queue_->Empty(); +} + +void FrameSwapMessageQueue::QueueMessageForFrame( + int source_frame_number, + std::unique_ptr<IPC::Message> msg, + bool* is_first) { + base::AutoLock lock(lock_); + visual_state_queue_->QueueMessage(source_frame_number, std::move(msg), + is_first); +} + +void FrameSwapMessageQueue::DidActivate(int source_frame_number) { + base::AutoLock lock(lock_); + visual_state_queue_->DrainMessages(source_frame_number, + &next_drain_messages_); +} + +void FrameSwapMessageQueue::DidSwap(int source_frame_number) {} + +cc::SwapPromise::DidNotSwapAction FrameSwapMessageQueue::DidNotSwap( + int source_frame_number, + cc::SwapPromise::DidNotSwapReason reason, + std::vector<std::unique_ptr<IPC::Message>>* messages) { + base::AutoLock lock(lock_); + switch (reason) { + case cc::SwapPromise::SWAP_FAILS: + case cc::SwapPromise::COMMIT_NO_UPDATE: + DrainMessages(messages); + visual_state_queue_->DrainMessages(source_frame_number, messages); + return cc::SwapPromise::DidNotSwapAction::BREAK_PROMISE; + case cc::SwapPromise::COMMIT_FAILS: + return cc::SwapPromise::DidNotSwapAction::KEEP_ACTIVE; + case cc::SwapPromise::ACTIVATION_FAILS: + // Do not queue any responses or return KEEP_ALIVE here. If + // ACTIVATION_FAILS the renderer is shutting down, which will result + // in the RenderFrameHostImpl destructor firing the remaining + // response callbacks itself. + return cc::SwapPromise::DidNotSwapAction::BREAK_PROMISE; + } +} + +void FrameSwapMessageQueue::DrainMessages( + std::vector<std::unique_ptr<IPC::Message>>* messages) { + lock_.AssertAcquired(); + std::move(next_drain_messages_.begin(), next_drain_messages_.end(), + std::back_inserter(*messages)); + next_drain_messages_.clear(); +} + +std::unique_ptr<FrameSwapMessageQueue::SendMessageScope> +FrameSwapMessageQueue::AcquireSendMessageScope() { + return std::make_unique<SendMessageScopeImpl>(&lock_); +} + +// static +void FrameSwapMessageQueue::TransferMessages( + std::vector<std::unique_ptr<IPC::Message>>* source, + std::vector<IPC::Message>* dest) { + for (const auto& msg : *source) { + dest->push_back(*msg.get()); + } + source->clear(); +} + +} // namespace content
diff --git a/content/renderer/frame_swap_message_queue.h b/content/renderer/frame_swap_message_queue.h new file mode 100644 index 0000000..3220f22 --- /dev/null +++ b/content/renderer/frame_swap_message_queue.h
@@ -0,0 +1,116 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CONTENT_RENDERER_FRAME_SWAP_MESSAGE_QUEUE_H_ +#define CONTENT_RENDERER_FRAME_SWAP_MESSAGE_QUEUE_H_ + +#include <map> +#include <memory> +#include <vector> + +#include "base/auto_reset.h" +#include "base/macros.h" +#include "base/memory/ref_counted.h" +#include "base/synchronization/lock.h" +#include "base/threading/thread_checker.h" +#include "cc/trees/swap_promise.h" +#include "content/common/content_export.h" + +namespace IPC { +class Message; +} + +namespace content { + +class FrameSwapMessageSubQueue; + +// Queue used to keep track of which IPC::Messages should be sent after a +// particular compositor frame swap. The messages are guaranteed to be processed +// after the frame is processed, but there is no guarantee that nothing else +// happens between processing the frame and processing the messages. +class CONTENT_EXPORT FrameSwapMessageQueue + : public base::RefCountedThreadSafe<FrameSwapMessageQueue> { + public: + class CONTENT_EXPORT SendMessageScope { + public: + virtual ~SendMessageScope() {} + }; + + explicit FrameSwapMessageQueue(int32_t routing_id); + + // Queues message to be returned on a matching DrainMessages call. + // + // |source_frame_number| frame number to queue |msg| for. + // |msg| - message to queue. The method takes ownership of |msg|. + // |is_first| - output parameter. Set to true if this was the first message + // enqueued for the given source_frame_number. + void QueueMessageForFrame(int source_frame_number, + std::unique_ptr<IPC::Message> msg, + bool* is_first); + + // Returns true if there are no messages in the queue. + bool Empty() const; + + // Should be called when a successful activation occurs. The messages for + // that activation can be obtained by calling DrainMessages. + // + // |source_frame_number| frame number for which the activate occurred. + void DidActivate(int source_frame_number); + + // Should be called when a successful swap occurs. The messages for that + // swap can be obtained by calling DrainMessages. + // + // |source_frame_number| frame number for which the swap occurred. + void DidSwap(int source_frame_number); + + // Should be called when we know a swap will not occur. + // + // |source_frame_number| frame number for which the swap will not occur. + // |reason| reason for the which the swap will not occur. + // |messages| depending on |reason| it may make sense to deliver certain + // messages asynchronously. This vector will contain those + // messages. + cc::SwapPromise::DidNotSwapAction DidNotSwap( + int source_frame_number, + cc::SwapPromise::DidNotSwapReason reason, + std::vector<std::unique_ptr<IPC::Message>>* messages); + + // A SendMessageScope object must be held by the caller when this method is + // called. + // + // |messages| vector to store messages, it's not cleared, only appended to. + // The method will append messages queued for frame numbers lower + // or equal to |source_frame_number| + void DrainMessages(std::vector<std::unique_ptr<IPC::Message>>* messages); + + // SendMessageScope is used to make sure that messages sent from different + // threads (impl/main) are scheduled in the right order on the IO threads. + // + // Returns an object that must be kept in scope till an IPC message containing + // |messages| is sent. + std::unique_ptr<SendMessageScope> AcquireSendMessageScope(); + + static void TransferMessages( + std::vector<std::unique_ptr<IPC::Message>>* source, + std::vector<IPC::Message>* dest); + + int32_t routing_id() const { return routing_id_; } + + private: + friend class base::RefCountedThreadSafe<FrameSwapMessageQueue>; + + ~FrameSwapMessageQueue(); + + mutable base::Lock lock_; + std::unique_ptr<FrameSwapMessageSubQueue> visual_state_queue_; + std::vector<std::unique_ptr<IPC::Message>> next_drain_messages_; + int32_t routing_id_ = 0; + THREAD_CHECKER(impl_thread_checker_); + + DISALLOW_COPY_AND_ASSIGN(FrameSwapMessageQueue); +}; + +} // namespace content + +#endif // CONTENT_RENDERER_FRAME_SWAP_MESSAGE_QUEUE_H_
diff --git a/content/renderer/frame_swap_message_queue_unittest.cc b/content/renderer/frame_swap_message_queue_unittest.cc new file mode 100644 index 0000000..0282e46 --- /dev/null +++ b/content/renderer/frame_swap_message_queue_unittest.cc
@@ -0,0 +1,302 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "content/renderer/frame_swap_message_queue.h" + +#include <utility> + +#include "ipc/ipc_message.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace content { + +class FrameSwapMessageQueueTest : public testing::Test { + public: + FrameSwapMessageQueueTest() + : first_message_(41, 1, IPC::Message::PRIORITY_NORMAL), + second_message_(42, 2, IPC::Message::PRIORITY_NORMAL), + third_message_(43, 3, IPC::Message::PRIORITY_NORMAL), + queue_(new FrameSwapMessageQueue(0)) {} + + protected: + void QueueNextSwapMessage(std::unique_ptr<IPC::Message> msg) { + queue_->QueueMessageForFrame(0, std::move(msg), nullptr); + } + + void QueueNextSwapMessage(std::unique_ptr<IPC::Message> msg, bool* first) { + queue_->QueueMessageForFrame(0, std::move(msg), first); + } + + void QueueVisualStateMessage(int source_frame_number, + std::unique_ptr<IPC::Message> msg) { + queue_->QueueMessageForFrame(source_frame_number, std::move(msg), nullptr); + } + + void QueueVisualStateMessage(int source_frame_number, + std::unique_ptr<IPC::Message> msg, + bool* first) { + queue_->QueueMessageForFrame(source_frame_number, std::move(msg), first); + } + + void DrainMessages(int source_frame_number, + std::vector<std::unique_ptr<IPC::Message>>* messages) { + messages->clear(); + queue_->DidActivate(source_frame_number); + queue_->DidSwap(source_frame_number); + std::unique_ptr<FrameSwapMessageQueue::SendMessageScope> + send_message_scope = queue_->AcquireSendMessageScope(); + queue_->DrainMessages(messages); + } + + bool HasMessageForId( + const std::vector<std::unique_ptr<IPC::Message>>& messages, + int routing_id) { + for (const auto& msg : messages) { + if (msg->routing_id() == routing_id) + return true; + } + return false; + } + + std::unique_ptr<IPC::Message> CloneMessage(const IPC::Message& other) { + return std::make_unique<IPC::Message>(other); + } + + void TestDidNotSwap(cc::SwapPromise::DidNotSwapReason reason); + + IPC::Message first_message_; + IPC::Message second_message_; + IPC::Message third_message_; + scoped_refptr<FrameSwapMessageQueue> queue_; +}; + +TEST_F(FrameSwapMessageQueueTest, TestEmptyQueueDrain) { + std::vector<std::unique_ptr<IPC::Message>> messages; + + DrainMessages(0, &messages); + ASSERT_TRUE(messages.empty()); +} + +TEST_F(FrameSwapMessageQueueTest, TestEmpty) { + std::vector<std::unique_ptr<IPC::Message>> messages; + ASSERT_TRUE(queue_->Empty()); + QueueNextSwapMessage(CloneMessage(first_message_)); + ASSERT_FALSE(queue_->Empty()); + DrainMessages(0, &messages); + ASSERT_TRUE(queue_->Empty()); + QueueVisualStateMessage(1, CloneMessage(first_message_)); + ASSERT_FALSE(queue_->Empty()); + queue_->DidActivate(1); + queue_->DidSwap(1); + ASSERT_FALSE(queue_->Empty()); +} + +TEST_F(FrameSwapMessageQueueTest, TestQueueMessageFirst) { + std::vector<std::unique_ptr<IPC::Message>> messages; + bool visual_state_first = false; + bool next_swap_first = false; + + // Queuing the first time should result in true. + QueueVisualStateMessage(1, CloneMessage(first_message_), &visual_state_first); + ASSERT_TRUE(visual_state_first); + // Queuing the second time should result in true. + QueueVisualStateMessage(1, CloneMessage(second_message_), + &visual_state_first); + ASSERT_FALSE(visual_state_first); + // Queuing for a different frame should result in true. + QueueVisualStateMessage(2, CloneMessage(first_message_), &visual_state_first); + ASSERT_TRUE(visual_state_first); + + // Queuing for a different policy should result in true. + QueueNextSwapMessage(CloneMessage(first_message_), &next_swap_first); + ASSERT_TRUE(next_swap_first); + // Second time for the same policy is still false. + QueueNextSwapMessage(CloneMessage(first_message_), &next_swap_first); + ASSERT_FALSE(next_swap_first); + + DrainMessages(4, &messages); + // Queuing after all messages are drained is a true again. + QueueVisualStateMessage(4, CloneMessage(first_message_), &visual_state_first); + ASSERT_TRUE(visual_state_first); +} + +TEST_F(FrameSwapMessageQueueTest, TestNextSwapMessageSentWithNextFrame) { + std::vector<std::unique_ptr<IPC::Message>> messages; + + DrainMessages(1, &messages); + QueueNextSwapMessage(CloneMessage(first_message_)); + DrainMessages(2, &messages); + ASSERT_EQ(1u, messages.size()); + ASSERT_EQ(first_message_.routing_id(), messages.front()->routing_id()); + messages.clear(); + + DrainMessages(2, &messages); + ASSERT_TRUE(messages.empty()); +} + +TEST_F(FrameSwapMessageQueueTest, TestNextSwapMessageSentWithCurrentFrame) { + std::vector<std::unique_ptr<IPC::Message>> messages; + + DrainMessages(1, &messages); + QueueNextSwapMessage(CloneMessage(first_message_)); + DrainMessages(1, &messages); + ASSERT_EQ(1u, messages.size()); + ASSERT_EQ(first_message_.routing_id(), messages.front()->routing_id()); + messages.clear(); + + DrainMessages(1, &messages); + ASSERT_TRUE(messages.empty()); +} + +TEST_F(FrameSwapMessageQueueTest, + TestDrainsVisualStateMessagesForCorrespondingFrames) { + std::vector<std::unique_ptr<IPC::Message>> messages; + + QueueVisualStateMessage(1, CloneMessage(first_message_)); + QueueVisualStateMessage(2, CloneMessage(second_message_)); + QueueVisualStateMessage(3, CloneMessage(third_message_)); + DrainMessages(0, &messages); + ASSERT_TRUE(messages.empty()); + + DrainMessages(2, &messages); + ASSERT_EQ(2u, messages.size()); + ASSERT_TRUE(HasMessageForId(messages, first_message_.routing_id())); + ASSERT_TRUE(HasMessageForId(messages, second_message_.routing_id())); + messages.clear(); + + DrainMessages(2, &messages); + ASSERT_TRUE(messages.empty()); + + DrainMessages(5, &messages); + ASSERT_EQ(1u, messages.size()); + ASSERT_EQ(third_message_.routing_id(), messages.front()->routing_id()); +} + +TEST_F(FrameSwapMessageQueueTest, + TestQueueNextSwapMessagePreservesFifoOrdering) { + std::vector<std::unique_ptr<IPC::Message>> messages; + + QueueNextSwapMessage(CloneMessage(first_message_)); + QueueNextSwapMessage(CloneMessage(second_message_)); + DrainMessages(1, &messages); + ASSERT_EQ(2u, messages.size()); + ASSERT_EQ(first_message_.routing_id(), messages[0]->routing_id()); + ASSERT_EQ(second_message_.routing_id(), messages[1]->routing_id()); +} + +TEST_F(FrameSwapMessageQueueTest, + TestQueueVisualStateMessagePreservesFifoOrdering) { + std::vector<std::unique_ptr<IPC::Message>> messages; + + QueueVisualStateMessage(1, CloneMessage(first_message_)); + QueueVisualStateMessage(1, CloneMessage(second_message_)); + DrainMessages(1, &messages); + ASSERT_EQ(2u, messages.size()); + ASSERT_EQ(first_message_.routing_id(), messages[0]->routing_id()); + ASSERT_EQ(second_message_.routing_id(), messages[1]->routing_id()); +} + +void FrameSwapMessageQueueTest::TestDidNotSwap( + cc::SwapPromise::DidNotSwapReason reason) { + std::vector<std::unique_ptr<IPC::Message>> messages; + + QueueNextSwapMessage(CloneMessage(first_message_)); + QueueVisualStateMessage(2, CloneMessage(second_message_)); + QueueVisualStateMessage(3, CloneMessage(third_message_)); + const int rid[] = {first_message_.routing_id(), second_message_.routing_id(), + third_message_.routing_id()}; + + bool msg_delivered = reason != cc::SwapPromise::COMMIT_FAILS && + reason != cc::SwapPromise::ACTIVATION_FAILS; + + queue_->DidNotSwap(2, reason, &messages); + ASSERT_TRUE(msg_delivered == HasMessageForId(messages, rid[0])); + ASSERT_TRUE(msg_delivered == HasMessageForId(messages, rid[1])); + ASSERT_FALSE(HasMessageForId(messages, rid[2])); + messages.clear(); + + queue_->DidNotSwap(3, reason, &messages); + ASSERT_FALSE(HasMessageForId(messages, rid[0])); + ASSERT_FALSE(HasMessageForId(messages, rid[1])); + ASSERT_TRUE(msg_delivered == HasMessageForId(messages, rid[2])); + messages.clear(); + + // all undelivered messages should still be available for RenderFrameHostImpl + // to deliver. + DrainMessages(3, &messages); + ASSERT_TRUE(msg_delivered != HasMessageForId(messages, rid[0])); + ASSERT_TRUE(msg_delivered != HasMessageForId(messages, rid[1])); + ASSERT_TRUE(msg_delivered != HasMessageForId(messages, rid[2])); +} + +TEST_F(FrameSwapMessageQueueTest, TestDidNotSwapNoUpdate) { + TestDidNotSwap(cc::SwapPromise::COMMIT_NO_UPDATE); +} + +TEST_F(FrameSwapMessageQueueTest, TestDidNotSwapSwapFails) { + TestDidNotSwap(cc::SwapPromise::SWAP_FAILS); +} + +TEST_F(FrameSwapMessageQueueTest, TestDidNotSwapCommitFails) { + TestDidNotSwap(cc::SwapPromise::COMMIT_FAILS); +} + +TEST_F(FrameSwapMessageQueueTest, TestDidNotSwapActivationFails) { + TestDidNotSwap(cc::SwapPromise::ACTIVATION_FAILS); +} + +class NotifiesDeletionMessage : public IPC::Message { + public: + NotifiesDeletionMessage(bool* deleted, const IPC::Message& other) + : IPC::Message(other), deleted_(deleted) {} + ~NotifiesDeletionMessage() override { *deleted_ = true; } + + private: + bool* deleted_; +}; + +TEST_F(FrameSwapMessageQueueTest, TestDeletesNextSwapMessage) { + bool message_deleted = false; + QueueNextSwapMessage(std::make_unique<NotifiesDeletionMessage>( + &message_deleted, first_message_)); + queue_ = nullptr; + ASSERT_TRUE(message_deleted); +} + +TEST_F(FrameSwapMessageQueueTest, TestDeletesVisualStateMessage) { + bool message_deleted = false; + QueueVisualStateMessage(1, std::make_unique<NotifiesDeletionMessage>( + &message_deleted, first_message_)); + queue_ = nullptr; + ASSERT_TRUE(message_deleted); +} + +TEST_F(FrameSwapMessageQueueTest, TestDeletesQueuedVisualStateMessage) { + bool message_deleted = false; + QueueVisualStateMessage(1, std::make_unique<NotifiesDeletionMessage>( + &message_deleted, first_message_)); + queue_->DidActivate(1); + queue_->DidSwap(1); + queue_ = nullptr; + ASSERT_TRUE(message_deleted); +} + +TEST_F(FrameSwapMessageQueueTest, TestDrainsMessageOnActivationThanDidNotSwap) { + const int frame = 6; + std::unique_ptr<IPC::Message> msg = CloneMessage(first_message_); + IPC::Message* msgSent = msg.get(); + QueueVisualStateMessage(frame, std::move(msg)); + queue_->DidActivate(frame); + EXPECT_TRUE(!queue_->Empty()); + + std::vector<std::unique_ptr<IPC::Message>> messages; + queue_->DidNotSwap(frame, cc::SwapPromise::SWAP_FAILS, &messages); + CHECK_EQ(1UL, messages.size()); + EXPECT_EQ(messages[0].get(), msgSent); + msgSent = nullptr; + + queue_ = nullptr; +} + +} // namespace content
diff --git a/content/renderer/queue_message_swap_promise.cc b/content/renderer/queue_message_swap_promise.cc new file mode 100644 index 0000000..ccf8d41 --- /dev/null +++ b/content/renderer/queue_message_swap_promise.cc
@@ -0,0 +1,74 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "content/renderer/queue_message_swap_promise.h" + +#include <memory> + +#include "base/command_line.h" +#include "content/common/widget_messages.h" +#include "content/public/common/content_switches.h" +#include "content/public/renderer/render_thread.h" +#include "content/renderer/frame_swap_message_queue.h" +#include "ipc/ipc_sync_message_filter.h" + +namespace content { + +QueueMessageSwapPromise::QueueMessageSwapPromise( + scoped_refptr<IPC::SyncMessageFilter> message_sender, + scoped_refptr<content::FrameSwapMessageQueue> message_queue, + int source_frame_number) + : message_sender_(message_sender), + message_queue_(message_queue), + source_frame_number_(source_frame_number) +{ + DCHECK(message_sender_.get()); + DCHECK(message_queue_.get()); +} + +QueueMessageSwapPromise::~QueueMessageSwapPromise() { +} + +void QueueMessageSwapPromise::DidActivate() { + message_queue_->DidActivate(source_frame_number_); + // The OutputSurface will take care of the Drain+Send. +} + +void QueueMessageSwapPromise::WillSwap(viz::CompositorFrameMetadata* metadata) { + message_queue_->DidSwap(source_frame_number_); + + std::unique_ptr<FrameSwapMessageQueue::SendMessageScope> send_message_scope = + message_queue_->AcquireSendMessageScope(); + std::vector<std::unique_ptr<IPC::Message>> messages; + message_queue_->DrainMessages(&messages); + + std::vector<IPC::Message> messages_to_send; + FrameSwapMessageQueue::TransferMessages(&messages, &messages_to_send); + if (!messages_to_send.empty()) { + metadata->send_frame_token_to_embedder = true; + message_sender_->Send(new WidgetHostMsg_FrameSwapMessages( + message_queue_->routing_id(), metadata->frame_token, messages_to_send)); + } +} + +void QueueMessageSwapPromise::DidSwap() {} + +cc::SwapPromise::DidNotSwapAction QueueMessageSwapPromise::DidNotSwap( + DidNotSwapReason reason) { + // TODO(eseckler): Deliver messages with the next swap instead of sending + // them here directly. + std::vector<std::unique_ptr<IPC::Message>> messages; + cc::SwapPromise::DidNotSwapAction action = + message_queue_->DidNotSwap(source_frame_number_, reason, &messages); + for (auto& msg : messages) { + message_sender_->Send(msg.release()); + } + return action; +} + +int64_t QueueMessageSwapPromise::TraceId() const { + return 0; +} + +} // namespace content
diff --git a/content/renderer/queue_message_swap_promise.h b/content/renderer/queue_message_swap_promise.h new file mode 100644 index 0000000..2c31b90 --- /dev/null +++ b/content/renderer/queue_message_swap_promise.h
@@ -0,0 +1,44 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CONTENT_RENDERER_QUEUE_MESSAGE_SWAP_PROMISE_H_ +#define CONTENT_RENDERER_QUEUE_MESSAGE_SWAP_PROMISE_H_ + +#include <stdint.h> + +#include "base/memory/ref_counted.h" +#include "cc/trees/swap_promise.h" + +namespace IPC { +class SyncMessageFilter; +} + +namespace content { + +class FrameSwapMessageQueue; + +class QueueMessageSwapPromise : public cc::SwapPromise { + public: + QueueMessageSwapPromise(scoped_refptr<IPC::SyncMessageFilter> message_sender, + scoped_refptr<FrameSwapMessageQueue> message_queue, + int source_frame_number); + + ~QueueMessageSwapPromise() override; + + void DidActivate() override; + void WillSwap(viz::CompositorFrameMetadata* metadata) override; + void DidSwap() override; + DidNotSwapAction DidNotSwap(DidNotSwapReason reason) override; + + int64_t TraceId() const override; + + private: + scoped_refptr<IPC::SyncMessageFilter> message_sender_; + scoped_refptr<content::FrameSwapMessageQueue> message_queue_; + int source_frame_number_; +}; + +} // namespace content + +#endif // CONTENT_RENDERER_QUEUE_MESSAGE_SWAP_PROMISE_H_
diff --git a/content/renderer/queue_message_swap_promise_unittest.cc b/content/renderer/queue_message_swap_promise_unittest.cc new file mode 100644 index 0000000..f9cb7f1 --- /dev/null +++ b/content/renderer/queue_message_swap_promise_unittest.cc
@@ -0,0 +1,308 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "content/renderer/queue_message_swap_promise.h" + +#include <stddef.h> + +#include <memory> +#include <vector> + +#include "base/macros.h" +#include "base/memory/ptr_util.h" +#include "base/test/task_environment.h" +#include "cc/trees/swap_promise.h" +#include "content/common/render_frame_metadata.mojom.h" +#include "content/common/widget_messages.h" +#include "content/renderer/frame_swap_message_queue.h" +#include "content/renderer/render_widget.h" +#include "ipc/ipc_message.h" +#include "ipc/ipc_sync_message_filter.h" +#include "ipc/ipc_test_sink.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace content { + +class TestSyncMessageFilter : public IPC::SyncMessageFilter { + public: + TestSyncMessageFilter() : IPC::SyncMessageFilter(nullptr) {} + + bool Send(IPC::Message* message) override { + if (message->type() == WidgetHostMsg_FrameSwapMessages::ID) { + WidgetHostMsg_FrameSwapMessages::Param param; + WidgetHostMsg_FrameSwapMessages::Read(message, ¶m); + std::vector<IPC::Message> messages = std::get<1>(param); + last_swap_messages_.clear(); + for (const IPC::Message& message : messages) { + last_swap_messages_.push_back(std::make_unique<IPC::Message>(message)); + } + delete message; + } else { + direct_send_messages_.push_back(base::WrapUnique(message)); + } + return true; + } + + std::vector<std::unique_ptr<IPC::Message>>& last_swap_messages() { + return last_swap_messages_; + } + + const std::vector<std::unique_ptr<IPC::Message>>& direct_send_messages() { + return direct_send_messages_; + } + + private: + ~TestSyncMessageFilter() override {} + + std::vector<std::unique_ptr<IPC::Message>> direct_send_messages_; + std::vector<std::unique_ptr<IPC::Message>> last_swap_messages_; + + DISALLOW_COPY_AND_ASSIGN(TestSyncMessageFilter); +}; + +class QueueMessageSwapPromiseTest : public testing::Test { + public: + QueueMessageSwapPromiseTest() + : frame_swap_message_queue_(new FrameSwapMessageQueue(0)), + sync_message_filter_(new TestSyncMessageFilter()) {} + + ~QueueMessageSwapPromiseTest() override {} + + std::unique_ptr<cc::SwapPromise> QueueMessageImpl( + std::unique_ptr<IPC::Message> msg, + int source_frame_number) { + return RenderWidget::QueueMessageImpl( + std::move(msg), frame_swap_message_queue_.get(), sync_message_filter_, + source_frame_number); + } + + const std::vector<std::unique_ptr<IPC::Message>>& DirectSendMessages() { + return sync_message_filter_->direct_send_messages(); + } + + std::vector<std::unique_ptr<IPC::Message>>& LastSwapMessages() { + return sync_message_filter_->last_swap_messages(); + } + + std::vector<std::unique_ptr<IPC::Message>>& NextSwapMessages() { + next_swap_messages_.clear(); + std::unique_ptr<FrameSwapMessageQueue::SendMessageScope> + send_message_scope = + frame_swap_message_queue_->AcquireSendMessageScope(); + frame_swap_message_queue_->DrainMessages(&next_swap_messages_); + return next_swap_messages_; + } + + bool ContainsMessage( + const std::vector<std::unique_ptr<IPC::Message>>& messages, + const IPC::Message& message) { + if (messages.empty()) + return false; + for (const auto& msg : messages) { + if (msg->type() == message.type()) + return true; + } + return false; + } + + bool LastSwapHasMessage(const IPC::Message& message) { + return ContainsMessage(LastSwapMessages(), message); + } + + bool NextSwapHasMessage(const IPC::Message& message) { + return ContainsMessage(NextSwapMessages(), message); + } + + void QueueMessages(int source_frame_numbers[], size_t count) { + for (size_t i = 0; i < count; ++i) { + messages_.push_back( + IPC::Message(0, i + 1, IPC::Message::PRIORITY_NORMAL)); + promises_.push_back( + QueueMessageImpl(std::make_unique<IPC::Message>(messages_[i]), + source_frame_numbers[i])); + } + } + + void CleanupPromises() { + for (const auto& promise : promises_) { + if (promise.get()) { + promise->DidActivate(); + promise->WillSwap(&dummy_metadata_); + promise->DidSwap(); + } + } + } + + protected: + void VisualStateSwapPromiseDidNotSwap( + cc::SwapPromise::DidNotSwapReason reason); + + base::test::TaskEnvironment task_environment_; + scoped_refptr<FrameSwapMessageQueue> frame_swap_message_queue_; + scoped_refptr<TestSyncMessageFilter> sync_message_filter_; + std::vector<IPC::Message> messages_; + std::vector<std::unique_ptr<cc::SwapPromise>> promises_; + viz::CompositorFrameMetadata dummy_metadata_; + cc::RenderFrameMetadata dummy_render_frame_metadata_; + + private: + std::vector<std::unique_ptr<IPC::Message>> next_swap_messages_; + + DISALLOW_COPY_AND_ASSIGN(QueueMessageSwapPromiseTest); +}; + +TEST_F(QueueMessageSwapPromiseTest, NextSwapPolicySchedulesMessageForNextSwap) { + int source_frame_numbers[] = {1}; + QueueMessages(source_frame_numbers, base::size(source_frame_numbers)); + + ASSERT_TRUE(promises_[0].get()); + promises_[0]->DidActivate(); + promises_[0]->WillSwap(&dummy_metadata_); + promises_[0]->DidSwap(); + + EXPECT_TRUE(DirectSendMessages().empty()); + EXPECT_TRUE(frame_swap_message_queue_->Empty()); + EXPECT_TRUE(LastSwapHasMessage(messages_[0])); +} + +TEST_F(QueueMessageSwapPromiseTest, NextSwapPolicyNeedsAtMostOnePromise) { + int source_frame_numbers[] = {1, 1}; + QueueMessages(source_frame_numbers, base::size(source_frame_numbers)); + + ASSERT_TRUE(promises_[0].get()); + ASSERT_FALSE(promises_[1].get()); + + CleanupPromises(); +} + +TEST_F(QueueMessageSwapPromiseTest, NextSwapPolicySendsMessageOnNoUpdate) { + int source_frame_numbers[] = {1}; + QueueMessages(source_frame_numbers, base::size(source_frame_numbers)); + + promises_[0]->DidNotSwap(cc::SwapPromise::COMMIT_NO_UPDATE); + EXPECT_TRUE(ContainsMessage(DirectSendMessages(), messages_[0])); + EXPECT_TRUE(LastSwapMessages().empty()); + EXPECT_TRUE(frame_swap_message_queue_->Empty()); +} + +TEST_F(QueueMessageSwapPromiseTest, NextSwapPolicySendsMessageOnSwapFails) { + int source_frame_numbers[] = {1}; + QueueMessages(source_frame_numbers, base::size(source_frame_numbers)); + + promises_[0]->DidNotSwap(cc::SwapPromise::SWAP_FAILS); + EXPECT_TRUE(ContainsMessage(DirectSendMessages(), messages_[0])); + EXPECT_TRUE(LastSwapMessages().empty()); + EXPECT_TRUE(frame_swap_message_queue_->Empty()); +} + +TEST_F(QueueMessageSwapPromiseTest, + NextActivatePolicyRetainsMessageOnCommitFails) { + int source_frame_numbers[] = {1}; + QueueMessages(source_frame_numbers, base::size(source_frame_numbers)); + + promises_[0]->DidNotSwap(cc::SwapPromise::COMMIT_FAILS); + EXPECT_TRUE(DirectSendMessages().empty()); + EXPECT_TRUE(LastSwapMessages().empty()); + EXPECT_FALSE(frame_swap_message_queue_->Empty()); + frame_swap_message_queue_->DidActivate(2); + EXPECT_TRUE(NextSwapHasMessage(messages_[0])); +} + +TEST_F(QueueMessageSwapPromiseTest, + VisualStateQueuesMessageWhenCommitRequested) { + int source_frame_numbers[] = {1}; + QueueMessages(source_frame_numbers, base::size(source_frame_numbers)); + + ASSERT_TRUE(promises_[0].get()); + EXPECT_TRUE(DirectSendMessages().empty()); + EXPECT_FALSE(frame_swap_message_queue_->Empty()); + EXPECT_TRUE(NextSwapMessages().empty()); + + CleanupPromises(); +} + +TEST_F(QueueMessageSwapPromiseTest, + VisualStateQueuesMessageWhenOtherMessageAlreadyQueued) { + int source_frame_numbers[] = {1, 1}; + QueueMessages(source_frame_numbers, base::size(source_frame_numbers)); + + EXPECT_TRUE(DirectSendMessages().empty()); + EXPECT_FALSE(frame_swap_message_queue_->Empty()); + EXPECT_FALSE(NextSwapHasMessage(messages_[1])); + + CleanupPromises(); +} + +TEST_F(QueueMessageSwapPromiseTest, VisualStateSwapPromiseDidActivate) { + int source_frame_numbers[] = {1, 1, 2}; + QueueMessages(source_frame_numbers, base::size(source_frame_numbers)); + + promises_[0]->DidActivate(); + promises_[0]->WillSwap(&dummy_metadata_); + promises_[0]->DidSwap(); + ASSERT_FALSE(promises_[1].get()); + std::vector<std::unique_ptr<IPC::Message>> messages; + messages.swap(LastSwapMessages()); + EXPECT_EQ(2u, messages.size()); + EXPECT_TRUE(ContainsMessage(messages, messages_[0])); + EXPECT_TRUE(ContainsMessage(messages, messages_[1])); + EXPECT_FALSE(ContainsMessage(messages, messages_[2])); + + promises_[2]->DidActivate(); + promises_[2]->DidNotSwap(cc::SwapPromise::SWAP_FAILS); + messages.swap(NextSwapMessages()); + EXPECT_TRUE(messages.empty()); + + EXPECT_EQ(1u, DirectSendMessages().size()); + EXPECT_TRUE(ContainsMessage(DirectSendMessages(), messages_[2])); + + EXPECT_TRUE(NextSwapMessages().empty()); + EXPECT_TRUE(frame_swap_message_queue_->Empty()); +} + +void QueueMessageSwapPromiseTest::VisualStateSwapPromiseDidNotSwap( + cc::SwapPromise::DidNotSwapReason reason) { + int source_frame_numbers[] = {1, 1, 2}; + QueueMessages(source_frame_numbers, base::size(source_frame_numbers)); + + // If we fail to swap with COMMIT_FAILS or ACTIVATE_FAILS, then + // messages are delivered by the RenderFrameHostImpl destructor, + // rather than directly by the swap promise. + bool msg_delivered = reason != cc::SwapPromise::COMMIT_FAILS && + reason != cc::SwapPromise::ACTIVATION_FAILS; + + promises_[0]->DidNotSwap(reason); + ASSERT_FALSE(promises_[1].get()); + EXPECT_TRUE(NextSwapMessages().empty()); + EXPECT_EQ(msg_delivered, ContainsMessage(DirectSendMessages(), messages_[0])); + EXPECT_EQ(msg_delivered, ContainsMessage(DirectSendMessages(), messages_[1])); + EXPECT_FALSE(ContainsMessage(DirectSendMessages(), messages_[2])); + + promises_[2]->DidNotSwap(reason); + EXPECT_TRUE(NextSwapMessages().empty()); + EXPECT_EQ(msg_delivered, ContainsMessage(DirectSendMessages(), messages_[2])); + + EXPECT_TRUE(NextSwapMessages().empty()); + EXPECT_EQ(msg_delivered, frame_swap_message_queue_->Empty()); +} + +TEST_F(QueueMessageSwapPromiseTest, VisualStateSwapPromiseDidNotSwapNoUpdate) { + VisualStateSwapPromiseDidNotSwap(cc::SwapPromise::COMMIT_NO_UPDATE); +} + +TEST_F(QueueMessageSwapPromiseTest, + VisualStateSwapPromiseDidNotSwapCommitFails) { + VisualStateSwapPromiseDidNotSwap(cc::SwapPromise::COMMIT_FAILS); +} + +TEST_F(QueueMessageSwapPromiseTest, VisualStateSwapPromiseDidNotSwapSwapFails) { + VisualStateSwapPromiseDidNotSwap(cc::SwapPromise::SWAP_FAILS); +} + +TEST_F(QueueMessageSwapPromiseTest, + VisualStateSwapPromiseDidNotSwapActivationFails) { + VisualStateSwapPromiseDidNotSwap(cc::SwapPromise::ACTIVATION_FAILS); +} + +} // namespace content
diff --git a/content/renderer/render_frame_impl.cc b/content/renderer/render_frame_impl.cc index 6a515a15..cc5f3bb7 100644 --- a/content/renderer/render_frame_impl.cc +++ b/content/renderer/render_frame_impl.cc
@@ -2236,6 +2236,8 @@ #if BUILDFLAG(ENABLE_PLUGINS) IPC_MESSAGE_HANDLER(FrameMsg_SetPepperVolume, OnSetPepperVolume) #endif + IPC_MESSAGE_HANDLER(FrameMsg_VisualStateRequest, + OnVisualStateRequest) IPC_MESSAGE_HANDLER(FrameMsg_MixedContentFound, OnMixedContentFound) IPC_MESSAGE_HANDLER(UnfreezableFrameMsg_Delete, OnDeleteFrame) IPC_END_MESSAGE_MAP() @@ -2629,6 +2631,11 @@ return base::Value(); } +void RenderFrameImpl::OnVisualStateRequest(uint64_t id) { + GetLocalRootRenderWidget()->QueueMessage( + std::make_unique<FrameHostMsg_VisualStateResponse>(routing_id_, id)); +} + void RenderFrameImpl::OnPortalActivated( const blink::PortalToken& portal_token, mojo::PendingAssociatedRemote<blink::mojom::Portal> portal,
diff --git a/content/renderer/render_frame_impl.h b/content/renderer/render_frame_impl.h index 270adb5c..8bd0812e 100644 --- a/content/renderer/render_frame_impl.h +++ b/content/renderer/render_frame_impl.h
@@ -1004,6 +1004,7 @@ void OnMoveCaret(const gfx::Point& point); void OnScrollFocusedEditableNodeIntoRect(const gfx::Rect& rect); void OnSelectRange(const gfx::Point& base, const gfx::Point& extent); + void OnVisualStateRequest(uint64_t key); void OnSuppressFurtherDialogs(); void OnMixedContentFound(const FrameMsg_MixedContentFound_Params& params);
diff --git a/content/renderer/render_thread_impl.cc b/content/renderer/render_thread_impl.cc index ea3acb0..07c7e50 100644 --- a/content/renderer/render_thread_impl.cc +++ b/content/renderer/render_thread_impl.cc
@@ -77,6 +77,7 @@ #include "content/renderer/browser_exposed_renderer_interfaces.h" #include "content/renderer/categorized_worker_pool.h" #include "content/renderer/effective_connection_type_helper.h" +#include "content/renderer/frame_swap_message_queue.h" #include "content/renderer/loader/resource_dispatcher.h" #include "content/renderer/media/audio/audio_renderer_mixer_manager.h" #include "content/renderer/media/gpu/gpu_video_accelerator_factories_impl.h" @@ -1633,6 +1634,7 @@ void RenderThreadImpl::RequestNewLayerTreeFrameSink( RenderWidget* render_widget, + scoped_refptr<FrameSwapMessageQueue> frame_swap_message_queue, const GURL& url, LayerTreeFrameSinkCallback callback, const char* client_name) { @@ -1776,6 +1778,7 @@ sync_message_filter(), g_next_layer_tree_frame_sink_id++, std::move(params.synthetic_begin_frame_source), render_widget->GetWebWidget()->GetSynchronousCompositorRegistry(), + std::move(frame_swap_message_queue), std::move(params.pipes.compositor_frame_sink_remote), std::move(params.pipes.client_receiver)), std::move(render_frame_metadata_observer));
diff --git a/content/renderer/render_thread_impl.h b/content/renderer/render_thread_impl.h index 6721d865..f1b5dcc 100644 --- a/content/renderer/render_thread_impl.h +++ b/content/renderer/render_thread_impl.h
@@ -201,6 +201,7 @@ std::unique_ptr<cc::UkmRecorderFactory> CreateUkmRecorderFactory() override; void RequestNewLayerTreeFrameSink( RenderWidget* render_widget, + scoped_refptr<FrameSwapMessageQueue> frame_swap_message_queue, const GURL& url, LayerTreeFrameSinkCallback callback, const char* client_name) override;
diff --git a/content/renderer/render_widget.cc b/content/renderer/render_widget.cc index d5228c7b..31e8817 100644 --- a/content/renderer/render_widget.cc +++ b/content/renderer/render_widget.cc
@@ -55,7 +55,9 @@ #include "content/public/renderer/content_renderer_client.h" #include "content/public/renderer/render_thread.h" #include "content/renderer/drop_data_builder.h" +#include "content/renderer/frame_swap_message_queue.h" #include "content/renderer/pepper/pepper_plugin_instance_impl.h" +#include "content/renderer/queue_message_swap_promise.h" #include "content/renderer/render_frame_impl.h" #include "content/renderer/render_frame_proxy.h" #include "content/renderer/render_process.h" @@ -66,6 +68,8 @@ #include "gpu/command_buffer/service/gpu_switches.h" #include "gpu/config/gpu_finch_features.h" #include "ipc/ipc_message_start.h" +#include "ipc/ipc_sync_message.h" +#include "ipc/ipc_sync_message_filter.h" #include "media/base/media_switches.h" #include "mojo/public/cpp/bindings/pending_remote.h" #include "ppapi/buildflags/buildflags.h" @@ -321,7 +325,8 @@ : routing_id_(widget_routing_id), compositor_deps_(compositor_deps), is_hidden_(hidden), - never_composited_(never_composited) { + never_composited_(never_composited), + frame_swap_message_queue_(new FrameSwapMessageQueue(routing_id_)) { DCHECK_NE(routing_id_, MSG_ROUTING_NONE); DCHECK(RenderThread::IsMainThread()); DCHECK(compositor_deps_); @@ -430,6 +435,8 @@ IPC_MESSAGE_HANDLER(WidgetMsg_SetBounds_ACK, OnRequestSetBoundsAck) IPC_MESSAGE_HANDLER(WidgetMsg_SetViewportIntersection, OnSetViewportIntersection) + IPC_MESSAGE_HANDLER(WidgetMsg_WaitForNextFrameForTests, + OnWaitNextFrameForTests) IPC_MESSAGE_HANDLER(DragMsg_TargetDragEnter, OnDragTargetDragEnter) IPC_MESSAGE_UNHANDLED(handled = false) IPC_END_MESSAGE_MAP() @@ -824,7 +831,8 @@ // would also be used for other widgets such as popups. const char* client_name = for_child_local_root_frame_ ? kOOPIF : kRenderer; compositor_deps_->RequestNewLayerTreeFrameSink( - this, std::move(url), std::move(callback), client_name); + this, frame_swap_message_queue_, std::move(url), std::move(callback), + client_name); } void RenderWidget::DidCommitAndDrawCompositorFrame() { @@ -1042,10 +1050,48 @@ observer.DidMeaningfulLayout(layout_type); } +// static +std::unique_ptr<cc::SwapPromise> RenderWidget::QueueMessageImpl( + std::unique_ptr<IPC::Message> msg, + FrameSwapMessageQueue* frame_swap_message_queue, + scoped_refptr<IPC::SyncMessageFilter> sync_message_filter, + int source_frame_number) { + bool first_message_for_frame = false; + frame_swap_message_queue->QueueMessageForFrame( + source_frame_number, std::move(msg), &first_message_for_frame); + if (!first_message_for_frame) + return nullptr; + return std::make_unique<QueueMessageSwapPromise>( + sync_message_filter, frame_swap_message_queue, source_frame_number); +} + void RenderWidget::SetHandlingInputEvent(bool handling_input_event) { GetWebWidget()->SetHandlingInputEvent(handling_input_event); } +void RenderWidget::QueueMessage(std::unique_ptr<IPC::Message> msg) { + // RenderThreadImpl::current() is NULL in some tests. + if (!RenderThreadImpl::current()) { + Send(msg.release()); + return; + } + + std::unique_ptr<cc::SwapPromise> swap_promise = + QueueMessageImpl(std::move(msg), frame_swap_message_queue_.get(), + RenderThreadImpl::current()->sync_message_filter(), + layer_tree_host_->SourceFrameNumber()); + if (swap_promise) { + layer_tree_host_->QueueSwapPromise(std::move(swap_promise)); + + // Request a main frame if one is not already in progress. This might either + // A) request a commit ahead of time or B) request a commit which is not + // needed because there are not pending updates. If B) then the frame will + // be aborted early and the swap promises will be broken (see + // EarlyOut_NoUpdates). + layer_tree_host_->SetNeedsAnimateIfNotInsideMainFrame(); + } +} + // We are supposed to get a single call to Show for a newly created RenderWidget // that was created via RenderWidget::CreateWebView. So, we wait until this // point to dispatch the ShowWidget message. @@ -1605,6 +1651,13 @@ render_frames_.RemoveObserver(frame); } +void RenderWidget::OnWaitNextFrameForTests( + int main_frame_thread_observer_routing_id) { + // Sends an ACK to the browser process during the next compositor frame. + QueueMessage(std::make_unique<WidgetHostMsg_WaitForNextFrameForTests_ACK>( + main_frame_thread_observer_routing_id)); +} + const blink::ScreenInfo& RenderWidget::GetOriginalScreenInfo() const { if (device_emulator_) return device_emulator_->original_screen_info();
diff --git a/content/renderer/render_widget.h b/content/renderer/render_widget.h index 7fa2482..bf205cf 100644 --- a/content/renderer/render_widget.h +++ b/content/renderer/render_widget.h
@@ -66,6 +66,10 @@ #include "ui/gfx/range/range.h" #include "ui/surface/transport_dib.h" +namespace IPC { +class SyncMessageFilter; +} + namespace blink { struct VisualProperties; struct DeviceEmulationParams; @@ -77,6 +81,10 @@ class WebPagePopup; } // namespace blink +namespace cc { +class SwapPromise; +} + namespace gfx { class ColorSpace; struct PresentationFeedback; @@ -85,6 +93,7 @@ namespace content { class CompositorDependencies; +class FrameSwapMessageQueue; class PepperPluginInstanceImpl; class RenderFrameImpl; class RenderFrameProxy; @@ -332,6 +341,17 @@ cc::LayerTreeHost* layer_tree_host() { return layer_tree_host_; } void SetHandlingInputEvent(bool handling_input_event); + // Queues the IPC |message| to be sent to the browser, delaying sending until + // the next compositor frame submission. At that time they will be sent before + // any message from the compositor as part of submitting its frame. This is + // used for messages that need synchronization with the compositor, but in + // general you should use Send(). + // + // This mechanism is not a drop-in replacement for IPC: messages sent this way + // will not be automatically available to BrowserMessageFilter, for example. + // FIFO ordering is preserved between messages enqueued. + void QueueMessage(std::unique_ptr<IPC::Message> msg); + // Checks if the selection bounds have been changed. If they are changed, // the new value will be sent to the browser process. void UpdateSelectionBounds(); @@ -403,6 +423,7 @@ // For unit tests. friend class InteractiveRenderWidget; friend class PopupRenderWidget; + friend class QueueMessageSwapPromiseTest; friend class RenderWidgetTest; friend class RenderViewImplTest; @@ -458,6 +479,7 @@ const gfx::PointF& screen_point, blink::WebDragOperation drag_operation); void OnOrientationChange(); + void OnWaitNextFrameForTests(int routing_id); // Sets the "hidden" state of this widget. All modification of is_hidden_ // should use this method so that we can properly inform the RenderThread of @@ -469,6 +491,14 @@ // constrained to limit overdraw. gfx::Rect ViewportVisibleRect(); + // QueueMessage implementation extracted into a static method for easy + // testing. + static std::unique_ptr<cc::SwapPromise> QueueMessageImpl( + std::unique_ptr<IPC::Message> msg, + FrameSwapMessageQueue* frame_swap_message_queue, + scoped_refptr<IPC::SyncMessageFilter> sync_message_filter, + int source_frame_number); + // Set the pending window rect. // Because the real render_widget is hosted in another process, there is // a time period where we may have set a new window rect which has not yet @@ -621,6 +651,8 @@ // The time spent in input handlers this frame. Used to throttle input acks. base::TimeDelta total_input_handling_time_this_frame_; + scoped_refptr<FrameSwapMessageQueue> frame_swap_message_queue_; + // Lists of RenderFrameProxy objects for which this RenderWidget is their // local root. Each of these represents a child local root RenderWidget in // another RenderView frame tree. For values that are propagated from
diff --git a/content/test/BUILD.gn b/content/test/BUILD.gn index 921e9a4..2c853b5 100644 --- a/content/test/BUILD.gn +++ b/content/test/BUILD.gn
@@ -2025,6 +2025,7 @@ "../renderer/accessibility/ax_image_stopwords_unittest.cc", "../renderer/categorized_worker_pool_unittest.cc", "../renderer/child_frame_compositing_helper_unittest.cc", + "../renderer/frame_swap_message_queue_unittest.cc", "../renderer/loader/navigation_body_loader_unittest.cc", "../renderer/loader/resource_dispatcher_unittest.cc", "../renderer/loader/sync_load_context_unittest.cc", @@ -2044,6 +2045,7 @@ "../renderer/media/power_status_helper_impl_unittest.cc", "../renderer/media/renderer_webaudiodevice_impl_unittest.cc", "../renderer/peripheral_content_heuristic_unittest.cc", + "../renderer/queue_message_swap_promise_unittest.cc", "../renderer/render_frame_metadata_observer_impl_unittest.cc", "../renderer/render_thread_impl_unittest.cc", "../renderer/render_widget_unittest.cc",
diff --git a/content/test/fake_compositor_dependencies.cc b/content/test/fake_compositor_dependencies.cc index 3d1abaf..143fe975 100644 --- a/content/test/fake_compositor_dependencies.cc +++ b/content/test/fake_compositor_dependencies.cc
@@ -11,6 +11,7 @@ #include "cc/test/fake_layer_tree_frame_sink.h" #include "cc/test/test_ukm_recorder_factory.h" #include "cc/trees/render_frame_metadata_observer.h" +#include "content/renderer/frame_swap_message_queue.h" #include "third_party/khronos/GLES2/gl2.h" #include "ui/gfx/buffer_types.h" @@ -64,6 +65,7 @@ void FakeCompositorDependencies::RequestNewLayerTreeFrameSink( RenderWidget* render_widget, + scoped_refptr<FrameSwapMessageQueue> frame_swap_message_queue, const GURL& url, LayerTreeFrameSinkCallback callback, const char* client_name) {
diff --git a/content/test/fake_compositor_dependencies.h b/content/test/fake_compositor_dependencies.h index 01dc408..8507857 100644 --- a/content/test/fake_compositor_dependencies.h +++ b/content/test/fake_compositor_dependencies.h
@@ -36,6 +36,7 @@ std::unique_ptr<cc::UkmRecorderFactory> CreateUkmRecorderFactory() override; void RequestNewLayerTreeFrameSink( RenderWidget* render_widget, + scoped_refptr<FrameSwapMessageQueue> frame_swap_message_queue, const GURL& url, LayerTreeFrameSinkCallback callback, const char* client_name) override;
diff --git a/ios/chrome/browser/sync/device_info_sync_service_factory.mm b/ios/chrome/browser/sync/device_info_sync_service_factory.mm index 2f8a1e29b..6d814c2c 100644 --- a/ios/chrome/browser/sync/device_info_sync_service_factory.mm +++ b/ios/chrome/browser/sync/device_info_sync_service_factory.mm
@@ -109,7 +109,8 @@ std::make_unique<DeviceInfoSyncClient>(browser_state->GetPrefs()); auto local_device_info_provider = std::make_unique<syncer::LocalDeviceInfoProviderImpl>( - ::GetChannel(), ::GetVersionString(), device_info_sync_client.get()); + ::GetChannel(), ::GetVersionString(), device_info_sync_client.get(), + /*sync_invalidations_service=*/nullptr); auto device_prefs = std::make_unique<syncer::DeviceInfoPrefs>( browser_state->GetPrefs(), base::DefaultClock::GetInstance());
diff --git a/ios/chrome/browser/ui/content_suggestions/OWNERS b/ios/chrome/browser/ui/content_suggestions/OWNERS index f192143..6e1a31f 100644 --- a/ios/chrome/browser/ui/content_suggestions/OWNERS +++ b/ios/chrome/browser/ui/content_suggestions/OWNERS
@@ -1,4 +1,5 @@ gambard@chromium.org +sczs@chromium.org # TEAM: ios-directory-owners@chromium.org # OS: iOS
diff --git a/ios/web_view/internal/sync/web_view_device_info_sync_service_factory.mm b/ios/web_view/internal/sync/web_view_device_info_sync_service_factory.mm index 21d7bf23..32fa60b 100644 --- a/ios/web_view/internal/sync/web_view_device_info_sync_service_factory.mm +++ b/ios/web_view/internal/sync/web_view_device_info_sync_service_factory.mm
@@ -87,7 +87,8 @@ auto local_device_info_provider = std::make_unique<syncer::LocalDeviceInfoProviderImpl>( version_info::Channel::STABLE, version_info::GetVersionNumber(), - device_info_sync_client.get()); + device_info_sync_client.get(), + /*sync_invalidations_service=*/nullptr); auto device_prefs = std::make_unique<syncer::DeviceInfoPrefs>( browser_state->GetPrefs(), base::DefaultClock::GetInstance());
diff --git a/media/base/stream_parser_buffer.h b/media/base/stream_parser_buffer.h index 3ff7e8c..a41e678 100644 --- a/media/base/stream_parser_buffer.h +++ b/media/base/stream_parser_buffer.h
@@ -64,6 +64,7 @@ int64_t operator/(base::TimeDelta rhs) const = delete; int64_t IntDiv(base::TimeDelta rhs) const { return ts_.IntDiv(rhs); } + double FltDiv(base::TimeDelta rhs) const { return ts_.FltDiv(rhs); } static DecodeTimestamp FromSecondsD(double seconds) { return DecodeTimestamp(base::TimeDelta::FromSecondsD(seconds));
diff --git a/media/capture/video/chromeos/camera_app_device_provider_impl.cc b/media/capture/video/chromeos/camera_app_device_provider_impl.cc index c99e195..0cca2218 100644 --- a/media/capture/video/chromeos/camera_app_device_provider_impl.cc +++ b/media/capture/video/chromeos/camera_app_device_provider_impl.cc
@@ -21,6 +21,12 @@ CameraAppDeviceProviderImpl::~CameraAppDeviceProviderImpl() = default; +void CameraAppDeviceProviderImpl::Bind( + mojo::PendingReceiver<cros::mojom::CameraAppDeviceProvider> receiver) { + receiver_.reset(); + receiver_.Bind(std::move(receiver)); +} + void CameraAppDeviceProviderImpl::GetCameraAppDevice( const std::string& source_id, GetCameraAppDeviceCallback callback) {
diff --git a/media/capture/video/chromeos/camera_app_device_provider_impl.h b/media/capture/video/chromeos/camera_app_device_provider_impl.h index b7c13e3..615d6bdba 100644 --- a/media/capture/video/chromeos/camera_app_device_provider_impl.h +++ b/media/capture/video/chromeos/camera_app_device_provider_impl.h
@@ -25,6 +25,8 @@ mojo::PendingRemote<cros::mojom::CameraAppDeviceBridge> bridge, DeviceIdMappingCallback mapping_callback); ~CameraAppDeviceProviderImpl() override; + void Bind( + mojo::PendingReceiver<cros::mojom::CameraAppDeviceProvider> receiver); // cros::mojom::CameraAppDeviceProvider implementations. void GetCameraAppDevice(const std::string& source_id, @@ -40,6 +42,8 @@ DeviceIdMappingCallback mapping_callback_; + mojo::Receiver<cros::mojom::CameraAppDeviceProvider> receiver_{this}; + base::WeakPtrFactory<CameraAppDeviceProviderImpl> weak_ptr_factory_; DISALLOW_COPY_AND_ASSIGN(CameraAppDeviceProviderImpl);
diff --git a/testing/buildbot/chromium.ci.json b/testing/buildbot/chromium.ci.json index 20d646b..7d2e5b3 100644 --- a/testing/buildbot/chromium.ci.json +++ b/testing/buildbot/chromium.ci.json
@@ -151272,7 +151272,9 @@ }, { "args": [ - "--num-retries=3" + "--num-retries=3", + "--target", + "Release_x64" ], "isolate_name": "blink_web_tests", "merge": { @@ -151292,7 +151294,7 @@ } ], "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com", - "shards": 12 + "shards": 28 }, "test_id_prefix": "ninja://:blink_web_tests/" },
diff --git a/testing/buildbot/chromium.fyi.json b/testing/buildbot/chromium.fyi.json index 6098a87..f212524 100644 --- a/testing/buildbot/chromium.fyi.json +++ b/testing/buildbot/chromium.fyi.json
@@ -5957,7 +5957,9 @@ }, { "args": [ - "--num-retries=3" + "--num-retries=3", + "--target", + "Release_x64" ], "isolate_name": "blink_web_tests", "merge": { @@ -5977,7 +5979,7 @@ } ], "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com", - "shards": 12 + "shards": 28 }, "test_id_prefix": "ninja://:blink_web_tests/" },
diff --git a/testing/buildbot/test_suite_exceptions.pyl b/testing/buildbot/test_suite_exceptions.pyl index 36474d8..ae4b4862 100644 --- a/testing/buildbot/test_suite_exceptions.pyl +++ b/testing/buildbot/test_suite_exceptions.pyl
@@ -258,6 +258,15 @@ 'Debug_x64', ], }, + 'Win10 Tests x64 1909': { + 'args': [ + '--target', + 'Release_x64', + ], + 'swarming': { + "shards": 28 + }, + }, 'Win10 Tests x64 Code Coverage': { 'args': [ '--target',
diff --git a/testing/variations/fieldtrial_testing_config.json b/testing/variations/fieldtrial_testing_config.json index 0437e51..b2a71d98 100644 --- a/testing/variations/fieldtrial_testing_config.json +++ b/testing/variations/fieldtrial_testing_config.json
@@ -1107,7 +1107,7 @@ ] } ], - "BlinkSchedulerPrioritizeCompositingAfterInput": [ + "BlinkSchedulerPerAgentSchedulingStudy": [ { "platforms": [ "android", @@ -1120,17 +1120,15 @@ ], "experiments": [ { - "name": "ExplicitSignals_1task_high", + "name": "disable_timer-queues_until_onload", "params": { - "number_of_tasks": "1" + "delay_ms": "0", + "method": "disable", + "queues": "timer-queues", + "signal": "onload" }, "enable_features": [ - "BlinkSchedulerPrioritizeCompositingAfterInput", - "BlinkSchedulerUseExplicitSignalForTriggeringCompositingPrioritization", - "BlinkSchedulerUseWillBeginMainFrameForCompositingPrioritization" - ], - "disable_features": [ - "BlinkSchedulerHighestPriorityForCompostingAfterInput" + "BlinkSchedulerPerAgentSchedulingExperiments" ] } ]
diff --git a/third_party/blink/perf_tests/layout/culled-inline-bounding-rects.html b/third_party/blink/perf_tests/layout/culled-inline-bounding-rects.html new file mode 100644 index 0000000..71609be --- /dev/null +++ b/third_party/blink/perf_tests/layout/culled-inline-bounding-rects.html
@@ -0,0 +1,41 @@ +<!DOCTYPE html> +<title>Bounding box of culled inline</title> +<script src="../resources/runner.js"></script> +<body> +<div id="container" style="width: 800px"></div> +<script> +let span_count = 0; + +function createTree(container, children, depth) { + container.appendChild(document.createTextNode('text ')); + for (let i = 0; i < children; ++i) { + const span = document.createElement('span'); + if (depth) + createTree(span, children, depth - 1); + container.appendChild(span); + ++span_count; + } +} + +function setup() { + // Adjust the number of children of each span, and the depth of the tree to + // ensure we're linear to both of them. crbug.com/1111154 + createTree(container, 5, 5); +} + +function test() { + for (let element of document.getElementsByTagName('span')) + element.getBoundingClientRect(); +} + +function run() { + PerfTestRunner.measureTime({ + description: `Measures performance of bounding box of ${span_count} culled inline.`, + run: test + }); +} + +setup(); +run(); +</script> +</body>
diff --git a/third_party/blink/public/mojom/page/widget.mojom b/third_party/blink/public/mojom/page/widget.mojom index 5a68c0c..2010444 100644 --- a/third_party/blink/public/mojom/page/widget.mojom +++ b/third_party/blink/public/mojom/page/widget.mojom
@@ -91,9 +91,6 @@ // Disables device emulator. DisableDeviceEmulation(); - - // Binds an WidgetCompositor interface. - BindWidgetCompositor(pending_receiver<WidgetCompositor> host); }; // Implemented in Browser, this interface defines frame-widget-specific methods that @@ -189,10 +186,3 @@ mojo_base.mojom.TextDirection focus_dir, bool is_anchor_first); }; - -// This interface is bound on the compositor thread. -interface WidgetCompositor { - // Requests that the RenderWidget sends back a response after the next main - // frame is generated and presented in the display compositor. - VisualStateRequest() => (); -};
diff --git a/third_party/blink/renderer/bindings/scripts/bind_gen/__init__.py b/third_party/blink/renderer/bindings/scripts/bind_gen/__init__.py index 15f1aa2..7491aca3 100644 --- a/third_party/blink/renderer/bindings/scripts/bind_gen/__init__.py +++ b/third_party/blink/renderer/bindings/scripts/bind_gen/__init__.py
@@ -39,9 +39,10 @@ from .task_queue import TaskQueue -def init(root_src_dir, root_gen_dir, component_reldirs): +def init(web_idl_database_path, root_src_dir, root_gen_dir, component_reldirs): """ Args: + web_idl_database_path: File path to the web_idl.Database. root_src_dir: Project's root directory, which corresponds to "//" in GN. root_gen_dir: Root directory of generated files, which corresponds to "//out/Default/gen" in GN. @@ -49,7 +50,7 @@ """ from . import package_initializer - package_initializer.init( - root_src_dir=root_src_dir, - root_gen_dir=root_gen_dir, - component_reldirs=component_reldirs) + package_initializer.init(web_idl_database_path=web_idl_database_path, + root_src_dir=root_src_dir, + root_gen_dir=root_gen_dir, + component_reldirs=component_reldirs)
diff --git a/third_party/blink/renderer/bindings/scripts/bind_gen/dictionary.py b/third_party/blink/renderer/bindings/scripts/bind_gen/dictionary.py index 580fb03f..2acf926 100644 --- a/third_party/blink/renderer/bindings/scripts/bind_gen/dictionary.py +++ b/third_party/blink/renderer/bindings/scripts/bind_gen/dictionary.py
@@ -34,6 +34,7 @@ from .codegen_utils import make_header_include_directives from .codegen_utils import write_code_node_to_file from .mako_renderer import MakoRenderer +from .package_initializer import package_initializer from .path_manager import PathManager from .task_queue import TaskQueue @@ -985,8 +986,11 @@ return func_decl, func_def -def generate_dictionary(dictionary): - assert isinstance(dictionary, web_idl.Dictionary) +def generate_dictionary(dictionary_identifier): + assert isinstance(dictionary_identifier, web_idl.Identifier) + + web_idl_database = package_initializer().web_idl_database() + dictionary = web_idl_database.find(dictionary_identifier) assert len(dictionary.components) == 1, ( "We don't support partial dictionaries across components yet.") @@ -1176,9 +1180,10 @@ write_code_node_to_file(source_node, path_manager.gen_path_to(source_path)) -def generate_dictionaries(task_queue, web_idl_database): +def generate_dictionaries(task_queue): assert isinstance(task_queue, TaskQueue) - assert isinstance(web_idl_database, web_idl.Database) + + web_idl_database = package_initializer().web_idl_database() for dictionary in web_idl_database.dictionaries: - task_queue.post_task(generate_dictionary, dictionary) + task_queue.post_task(generate_dictionary, dictionary.identifier)
diff --git a/third_party/blink/renderer/bindings/scripts/bind_gen/enumeration.py b/third_party/blink/renderer/bindings/scripts/bind_gen/enumeration.py index 4e03be8e..d26b7da2 100644 --- a/third_party/blink/renderer/bindings/scripts/bind_gen/enumeration.py +++ b/third_party/blink/renderer/bindings/scripts/bind_gen/enumeration.py
@@ -24,6 +24,7 @@ from .codegen_utils import make_header_include_directives from .codegen_utils import write_code_node_to_file from .mako_renderer import MakoRenderer +from .package_initializer import package_initializer from .path_manager import PathManager from .task_queue import TaskQueue @@ -236,8 +237,11 @@ return decls, defs -def generate_enumeration(enumeration): - assert isinstance(enumeration, web_idl.Enumeration) +def generate_enumeration(enumeration_identifier): + assert isinstance(enumeration_identifier, web_idl.Identifier) + + web_idl_database = package_initializer().web_idl_database() + enumeration = web_idl_database.find(enumeration_identifier) path_manager = PathManager(enumeration) assert path_manager.api_component == path_manager.impl_component @@ -361,9 +365,10 @@ write_code_node_to_file(source_node, path_manager.gen_path_to(source_path)) -def generate_enumerations(task_queue, web_idl_database): +def generate_enumerations(task_queue): assert isinstance(task_queue, TaskQueue) - assert isinstance(web_idl_database, web_idl.Database) + + web_idl_database = package_initializer().web_idl_database() for enumeration in web_idl_database.enumerations: - task_queue.post_task(generate_enumeration, enumeration) + task_queue.post_task(generate_enumeration, enumeration.identifier)
diff --git a/third_party/blink/renderer/bindings/scripts/bind_gen/interface.py b/third_party/blink/renderer/bindings/scripts/bind_gen/interface.py index 2ba283b..f8d747de 100644 --- a/third_party/blink/renderer/bindings/scripts/bind_gen/interface.py +++ b/third_party/blink/renderer/bindings/scripts/bind_gen/interface.py
@@ -45,6 +45,7 @@ from .codegen_utils import make_header_include_directives from .codegen_utils import write_code_node_to_file from .mako_renderer import MakoRenderer +from .package_initializer import package_initializer from .path_manager import PathManager from .task_queue import TaskQueue @@ -6448,8 +6449,11 @@ return headers -def generate_interface(interface): - assert isinstance(interface, web_idl.Interface) +def generate_interface(interface_identifier): + assert isinstance(interface_identifier, web_idl.Identifier) + + web_idl_database = package_initializer().web_idl_database() + interface = web_idl_database.find(interface_identifier) path_manager = PathManager(interface) api_component = path_manager.api_component @@ -6975,15 +6979,15 @@ path_manager.gen_path_to(impl_source_path)) -def generate_install_properties_per_feature(web_idl_database, - function_name, +def generate_install_properties_per_feature(function_name, filepath_basename, for_testing=False): - assert isinstance(web_idl_database, web_idl.Database) assert isinstance(function_name, str) assert isinstance(filepath_basename, str) assert isinstance(for_testing, bool) + web_idl_database = package_initializer().web_idl_database() + # Filepaths header_path = PathManager.component_path("modules", "{}.h".format(filepath_basename)) @@ -7187,15 +7191,15 @@ write_code_node_to_file(source_node, path_manager.gen_path_to(source_path)) -def generate_init_idl_interfaces(web_idl_database, - function_name, +def generate_init_idl_interfaces(function_name, filepath_basename, for_testing=False): - assert isinstance(web_idl_database, web_idl.Database) assert isinstance(function_name, str) assert isinstance(filepath_basename, str) assert isinstance(for_testing, bool) + web_idl_database = package_initializer().web_idl_database() + # Filepaths header_path = PathManager.component_path("modules", "{}.h".format(filepath_basename)) @@ -7272,25 +7276,24 @@ write_code_node_to_file(source_node, path_manager.gen_path_to(source_path)) -def generate_interfaces(task_queue, web_idl_database): +def generate_interfaces(task_queue): assert isinstance(task_queue, TaskQueue) - assert isinstance(web_idl_database, web_idl.Database) + + web_idl_database = package_initializer().web_idl_database() for interface in web_idl_database.interfaces: - task_queue.post_task(generate_interface, interface) + task_queue.post_task(generate_interface, interface.identifier) task_queue.post_task(generate_install_properties_per_feature, - web_idl_database, "InstallPropertiesPerFeature", + "InstallPropertiesPerFeature", "properties_per_feature_installer") task_queue.post_task(generate_install_properties_per_feature, - web_idl_database, "InstallPropertiesPerFeatureForTesting", "properties_per_feature_installer_for_testing", for_testing=True) - task_queue.post_task(generate_init_idl_interfaces, web_idl_database, - "InitIDLInterfaces", "init_idl_interfaces") + task_queue.post_task(generate_init_idl_interfaces, "InitIDLInterfaces", + "init_idl_interfaces") task_queue.post_task(generate_init_idl_interfaces, - web_idl_database, "InitIDLInterfacesForTesting", "init_idl_interfaces_for_testing", for_testing=True)
diff --git a/third_party/blink/renderer/bindings/scripts/bind_gen/package_initializer.py b/third_party/blink/renderer/bindings/scripts/bind_gen/package_initializer.py index 26c7353e..863da3d 100644 --- a/third_party/blink/renderer/bindings/scripts/bind_gen/package_initializer.py +++ b/third_party/blink/renderer/bindings/scripts/bind_gen/package_initializer.py
@@ -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 web_idl + from . import style_format from .path_manager import PathManager @@ -48,13 +50,18 @@ # package. _the_instance = None + # The instance of web_idl.Database. + _the_web_idl_database = None + @classmethod def the_instance(cls): return cls._the_instance - def __init__(self, root_src_dir, root_gen_dir, component_reldirs): + def __init__(self, web_idl_database_path, root_src_dir, root_gen_dir, + component_reldirs): """ Args: + web_idl_database_path: File path to the web_idl.Database. root_src_dir: Project's root directory, which corresponds to "//" in GN. root_gen_dir: Root directory of generated files, which corresponds @@ -62,6 +69,7 @@ component_reldirs: Pairs of component and output directory. """ + self._web_idl_database_path = web_idl_database_path self._root_src_dir = root_src_dir self._root_gen_dir = root_gen_dir self._component_reldirs = component_reldirs @@ -74,9 +82,21 @@ self._init() def _init(self): + # Load the web_idl.Database as a global object so that every worker or + # every function running in a worker of 'multiprocessing' does not need + # to load it. + PackageInitializer._the_web_idl_database = ( + web_idl.Database.read_from_file(self._web_idl_database_path)) + style_format.init(self._root_src_dir) PathManager.init( root_src_dir=self._root_src_dir, root_gen_dir=self._root_gen_dir, component_reldirs=self._component_reldirs) + + def web_idl_database(self): + """Returns the global instance of web_idl.Database.""" + assert isinstance(PackageInitializer._the_web_idl_database, + web_idl.Database) + return PackageInitializer._the_web_idl_database
diff --git a/third_party/blink/renderer/bindings/scripts/bind_gen/task_queue.py b/third_party/blink/renderer/bindings/scripts/bind_gen/task_queue.py index 7143ede..0d8f4c0 100644 --- a/third_party/blink/renderer/bindings/scripts/bind_gen/task_queue.py +++ b/third_party/blink/renderer/bindings/scripts/bind_gen/task_queue.py
@@ -14,10 +14,7 @@ """ def __init__(self): - # More processes do not mean better performance. The pool size was - # chosen heuristically. - cpu_count = multiprocessing.cpu_count() - self._pool_size = max(2, cpu_count / 4) + self._pool_size = multiprocessing.cpu_count() self._pool = multiprocessing.Pool(self._pool_size, package_initializer().init) self._requested_tasks = [] # List of (func, args, kwargs) @@ -49,7 +46,7 @@ self._did_run = True num_of_requested_tasks = len(self._requested_tasks) - chunk_size = min(20, num_of_requested_tasks / (4 * self._pool_size)) + chunk_size = 1 i = 0 while i < num_of_requested_tasks: tasks = self._requested_tasks[i:i + chunk_size] @@ -58,7 +55,7 @@ self._pool.apply_async(_task_queue_run_tasks, [tasks])) self._pool.close() - timeout_in_sec = 2 + timeout_in_sec = 1 while True: self._report_worker_task_progress(report_progress) for worker_task in self._worker_tasks:
diff --git a/third_party/blink/renderer/bindings/scripts/generate_bindings.py b/third_party/blink/renderer/bindings/scripts/generate_bindings.py index 07de3a45..6e6bbc0 100644 --- a/third_party/blink/renderer/bindings/scripts/generate_bindings.py +++ b/third_party/blink/renderer/bindings/scripts/generate_bindings.py
@@ -65,22 +65,19 @@ if task not in dispatch_table: sys.exit("Unknown task: {}".format(task)) - web_idl_database = web_idl.Database.read_from_file( - options.web_idl_database) component_reldirs = { web_idl.Component('core'): options.output_core_reldir, web_idl.Component('modules'): options.output_modules_reldir, } - bind_gen.init( - root_src_dir=options.root_src_dir, - root_gen_dir=options.root_gen_dir, - component_reldirs=component_reldirs) + bind_gen.init(web_idl_database_path=options.web_idl_database, + root_src_dir=options.root_src_dir, + root_gen_dir=options.root_gen_dir, + component_reldirs=component_reldirs) task_queue = bind_gen.TaskQueue() for task in tasks: - dispatch_table[task](task_queue=task_queue, - web_idl_database=web_idl_database) + dispatch_table[task](task_queue) def print_to_console(message): out = sys.stdout
diff --git a/third_party/blink/renderer/core/frame/web_frame_widget_base.cc b/third_party/blink/renderer/core/frame/web_frame_widget_base.cc index 4387d2f2..a3b74a6 100644 --- a/third_party/blink/renderer/core/frame/web_frame_widget_base.cc +++ b/third_party/blink/renderer/core/frame/web_frame_widget_base.cc
@@ -353,11 +353,6 @@ } #endif -void WebFrameWidgetBase::BindWidgetCompositor( - mojo::PendingReceiver<mojom::blink::WidgetCompositor> receiver) { - widget_base_->BindWidgetCompositor(std::move(receiver)); -} - void WebFrameWidgetBase::CancelDrag() { // It's possible for this to be called while we're not doing a drag if // it's from a previous page that got unloaded.
diff --git a/third_party/blink/renderer/core/frame/web_frame_widget_base.h b/third_party/blink/renderer/core/frame/web_frame_widget_base.h index 944a5a8..d2f9f98 100644 --- a/third_party/blink/renderer/core/frame/web_frame_widget_base.h +++ b/third_party/blink/renderer/core/frame/web_frame_widget_base.h
@@ -385,9 +385,6 @@ base::Optional<gfx::Point> GetAndResetContextMenuLocation(); - void BindWidgetCompositor( - mojo::PendingReceiver<mojom::blink::WidgetCompositor> receiver) override; - // Called when the FrameView for this Widget's local root is created. virtual void DidCreateLocalRootView() {}
diff --git a/third_party/blink/renderer/core/layout/BUILD.gn b/third_party/blink/renderer/core/layout/BUILD.gn index 74381bb..104937e 100644 --- a/third_party/blink/renderer/core/layout/BUILD.gn +++ b/third_party/blink/renderer/core/layout/BUILD.gn
@@ -409,6 +409,7 @@ "ng/inline/ng_text_fragment.h", "ng/inline/ng_text_fragment_builder.cc", "ng/inline/ng_text_fragment_builder.h", + "ng/inline/ng_text_offset.cc", "ng/inline/ng_text_offset.h", "ng/inline/ng_text_type.h", "ng/layout_box_utils.cc", @@ -557,6 +558,8 @@ "ng/table/layout_ng_table_section_interface.h", "ng/table/ng_table_borders.cc", "ng/table/ng_table_borders.h", + "ng/table/ng_table_constraint_space_data.h", + "ng/table/ng_table_fragment_data.h", "ng/table/ng_table_layout_algorithm_helpers.cc", "ng/table/ng_table_layout_algorithm_helpers.h", "ng/table/ng_table_layout_algorithm_types.cc",
diff --git a/third_party/blink/renderer/core/layout/ng/inline/ng_inline_cursor.cc b/third_party/blink/renderer/core/layout/ng/inline/ng_inline_cursor.cc index dc53d4a..cd446119 100644 --- a/third_party/blink/renderer/core/layout/ng/inline/ng_inline_cursor.cc +++ b/third_party/blink/renderer/core/layout/ng/inline/ng_inline_cursor.cc
@@ -254,32 +254,6 @@ return !IsListMarker(); } -bool NGInlineCursorPosition::IsPartOfCulledInlineBox( - const LayoutInline& layout_inline) const { - DCHECK(!layout_inline.ShouldCreateBoxFragment()); - DCHECK(*this); - const LayoutObject* const layout_object = GetLayoutObject(); - // We use |IsInline()| to exclude floating and out-of-flow objects. - if (!layout_object || !layout_object->IsInline() || - layout_object->IsAtomicInlineLevel()) - return false; - DCHECK(!layout_object->IsFloatingOrOutOfFlowPositioned()); - DCHECK(!BoxFragment() || !BoxFragment()->IsFormattingContextRoot()); - for (const LayoutObject* parent = layout_object->Parent(); parent; - parent = parent->Parent()) { - // Children of culled inline should be included. - if (parent == &layout_inline) - return true; - // Grand children should be included only if children are also culled. - if (const auto* parent_layout_inline = ToLayoutInlineOrNull(parent)) { - if (!parent_layout_inline->ShouldCreateBoxFragment()) - continue; - } - return false; - } - return false; -} - bool NGInlineCursor::IsLastLineInInlineBlock() const { DCHECK(Current().IsLineBox()); if (!GetLayoutBlockFlow()->IsAtomicInlineLevel()) @@ -1503,33 +1477,7 @@ current_.Set(items_.begin() + item_index); } -void NGInlineCursor::MoveToIncludingCulledInline( - const LayoutObject& layout_object) { - DCHECK(layout_object.IsInLayoutNGInlineFormattingContext()) << layout_object; - MoveTo(layout_object); - if (*this || !HasRoot()) { - layout_inline_ = nullptr; - return; - } - - // Try to find ancestors if this is a culled inline. - layout_inline_ = ToLayoutInlineOrNull(&layout_object); - if (!layout_inline_) - return; - - MoveToFirst(); - while (Current() && !Current().IsPartOfCulledInlineBox(*layout_inline_)) - MoveToNext(); -} - -void NGInlineCursor::MoveToNextForSameLayoutObject() { - if (layout_inline_) { - // Move to next fragment in culled inline box undef |layout_inline_|. - do { - MoveToNext(); - } while (Current() && !Current().IsPartOfCulledInlineBox(*layout_inline_)); - return; - } +void NGInlineCursor::MoveToNextForSameLayoutObjectExceptCulledInline() { if (current_.paint_fragment_) { if (auto* paint_fragment = current_.paint_fragment_->NextForSameLayoutObject()) { @@ -1575,6 +1523,115 @@ MoveTo(last); } +// +// Functions to enumerate fragments that contribute to a culled inline. +// + +// Traverse the |LayoutObject| tree in pre-order DFS and find a |LayoutObject| +// that contributes to the culled inline. +const LayoutObject* NGInlineCursor::CulledInlineTraversal::SetCurrent( + const LayoutObject* child) { + while (child) { + if (UNLIKELY(child->IsFloatingOrOutOfFlowPositioned())) { + child = child->NextInPreOrderAfterChildren(layout_inline_); + continue; + } + + if (child->HasInlineFragments()) { + current_object_ = child; + return child; + } + + // A culled inline can be computed from its direct children, but when the + // child is also culled, traverse its grand children. + if (const LayoutInline* child_layout_inline = ToLayoutInlineOrNull(child)) { + DCHECK(!child_layout_inline->ShouldCreateBoxFragment()); + if (const LayoutObject* grand_child = child_layout_inline->FirstChild()) { + child = grand_child; + continue; + } + } + child = child->NextInPreOrderAfterChildren(layout_inline_); + } + current_object_ = nullptr; + return nullptr; +} + +const LayoutObject* NGInlineCursor::CulledInlineTraversal::MoveToFirstFor( + const LayoutInline& layout_inline) { + layout_inline_ = &layout_inline; + return SetCurrent(layout_inline.FirstChild()); +} + +const LayoutObject* NGInlineCursor::CulledInlineTraversal::MoveToNext() { + return SetCurrent( + current_object_->NextInPreOrderAfterChildren(layout_inline_)); +} + +void NGInlineCursor::MoveToFirstForCulledInline( + const LayoutInline& layout_inline) { + if (const LayoutObject* layout_object = + culled_inline_.MoveToFirstFor(layout_inline)) { + MoveTo(*layout_object); + // This |MoveTo| may fail if |this| is a descendant cursor. Try the next + // |LayoutObject|. + MoveToNextCulledInlineDescendantIfNeeded(); + } +} + +void NGInlineCursor::MoveToNextForCulledInline() { + DCHECK(culled_inline_); + MoveToNextForSameLayoutObjectExceptCulledInline(); + // If we're at the end of fragments for the current |LayoutObject| that + // contributes to the current culled inline, find the next |LayoutObject|. + MoveToNextCulledInlineDescendantIfNeeded(); +} + +void NGInlineCursor::MoveToNextCulledInlineDescendantIfNeeded() { + DCHECK(culled_inline_); + if (Current()) + return; + + while (const LayoutObject* layout_object = culled_inline_.MoveToNext()) { + MoveTo(*layout_object); + if (Current()) + return; + + // This |MoveTo| may fail if |this| is a descendant cursor. Try the next + // |LayoutObject|. + DCHECK(IsDescendantsCursor()); + } +} + +void NGInlineCursor::MoveToIncludingCulledInline( + const LayoutObject& layout_object) { + DCHECK(layout_object.IsInLayoutNGInlineFormattingContext()) << layout_object; + + culled_inline_.Reset(); + MoveTo(layout_object); + if (Current() || !HasRoot()) + return; + + // If this is a culled inline, find fragments for descendant |LayoutObject|s + // that contribute to the culled inline. + if (const LayoutInline* layout_inline = + ToLayoutInlineOrNull(&layout_object)) { + if (!layout_inline->ShouldCreateBoxFragment()) + MoveToFirstForCulledInline(*layout_inline); + } +} + +void NGInlineCursor::MoveToNextForSameLayoutObject() { + if (UNLIKELY(culled_inline_)) { + MoveToNextForCulledInline(); + return; + } + MoveToNextForSameLayoutObjectExceptCulledInline(); +} + +// +// |NGInlineBackwardCursor| functions. +// NGInlineBackwardCursor::NGInlineBackwardCursor(const NGInlineCursor& cursor) : cursor_(cursor) { if (cursor.root_paint_fragment_) {
diff --git a/third_party/blink/renderer/core/layout/ng/inline/ng_inline_cursor.h b/third_party/blink/renderer/core/layout/ng/inline/ng_inline_cursor.h index 5a7546c..6f8eef3 100644 --- a/third_party/blink/renderer/core/layout/ng/inline/ng_inline_cursor.h +++ b/third_party/blink/renderer/core/layout/ng/inline/ng_inline_cursor.h
@@ -195,9 +195,6 @@ item_ = nullptr; } - // True if current position is part of culled inline box |layout_inline|. - bool IsPartOfCulledInlineBox(const LayoutInline& layout_inline) const; - const NGPaintFragment* paint_fragment_ = nullptr; const NGFragmentItem* item_ = nullptr; ItemsSpan::iterator item_iter_; @@ -416,10 +413,20 @@ // Returns true if the current position moves to last child. bool TryToMoveToLastChild(); + // + // Functions to enumerate fragments for a |LayoutObject|. + // + // Move to first |NGFragmentItem| or |NGPaintFragment| associated to // |layout_object|. When |layout_object| has no associated fragments, this // cursor points nothing. void MoveTo(const LayoutObject& layout_object); + + // Same as |MoveTo|, except that this enumerates fragments for descendants + // if |layout_object| is a culled inline. + // + // Note, for a culled inline, fragments may not be in the visual order in + // the inline direction if RTL or mixed bidi for a performance reason. void MoveToIncludingCulledInline(const LayoutObject& layout_object); // Move the current position to next fragment on same layout object. @@ -435,6 +442,16 @@ #endif private: + // Returns true if |this| is only for a part of an inline formatting context; + // in other words, if |this| is created by |CursorForDescendants|. + bool IsDescendantsCursor() const { + if (fragment_items_) + return !fragment_items_->Equals(items_); + if (root_paint_fragment_) + return root_paint_fragment_->Parent(); + return false; + } + // True if the current position is a last line in inline block. It is error // to call at end or the current position is not line. bool IsLastLineInInlineBlock() const; @@ -471,6 +488,35 @@ wtf_size_t SpanBeginItemIndex() const; wtf_size_t SpanIndexFromItemIndex(unsigned index) const; + // |MoveToNextForSameLayoutObject| that doesn't check |culled_inline_|. + void MoveToNextForSameLayoutObjectExceptCulledInline(); + + // A helper class to enumerate |LayoutObject|s that contribute to a culled + // inline. + class CulledInlineTraversal { + STACK_ALLOCATED(); + + public: + CulledInlineTraversal() = default; + + explicit operator bool() const { return current_object_; } + void Reset() { current_object_ = nullptr; } + + // Returns first/next |LayoutObject| that contribute to |layout_inline|. + const LayoutObject* MoveToFirstFor(const LayoutInline& layout_inline); + const LayoutObject* MoveToNext(); + + private: + const LayoutObject* SetCurrent(const LayoutObject* child); + + const LayoutObject* current_object_ = nullptr; + const LayoutInline* layout_inline_ = nullptr; + }; + + void MoveToFirstForCulledInline(const LayoutInline& layout_inline); + void MoveToNextForCulledInline(); + void MoveToNextCulledInlineDescendantIfNeeded(); + NGInlineCursorPosition current_; ItemsSpan items_; @@ -478,8 +524,7 @@ const NGPaintFragment* root_paint_fragment_ = nullptr; - // Used in |MoveToNextForSameLayoutObject()| to support culled inline. - const LayoutInline* layout_inline_ = nullptr; + CulledInlineTraversal culled_inline_; friend class NGInlineBackwardCursor; };
diff --git a/third_party/blink/renderer/core/layout/ng/inline/ng_text_offset.cc b/third_party/blink/renderer/core/layout/ng/inline/ng_text_offset.cc new file mode 100644 index 0000000..448b4b8 --- /dev/null +++ b/third_party/blink/renderer/core/layout/ng/inline/ng_text_offset.cc
@@ -0,0 +1,15 @@ +// 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/core/layout/ng/inline/ng_text_offset.h" + +#include <ostream> + +namespace blink { + +std::ostream& operator<<(std::ostream& ostream, const NGTextOffset& offset) { + return ostream << "{" << offset.start << ", " << offset.end << "}"; +} + +} // namespace blink
diff --git a/third_party/blink/renderer/core/layout/ng/inline/ng_text_offset.h b/third_party/blink/renderer/core/layout/ng/inline/ng_text_offset.h index 8e196f9..ce8fd77 100644 --- a/third_party/blink/renderer/core/layout/ng/inline/ng_text_offset.h +++ b/third_party/blink/renderer/core/layout/ng/inline/ng_text_offset.h
@@ -25,10 +25,19 @@ void AssertValid() const { DCHECK_GE(end, start); } void AssertNotEmpty() const { DCHECK_GT(end, start); } + bool operator==(const NGTextOffset& other) const { + return start == other.start && end == other.end; + } + bool operator!=(const NGTextOffset& other) const { + return !operator==(other); + } + unsigned start; unsigned end; }; +CORE_EXPORT std::ostream& operator<<(std::ostream&, const NGTextOffset&); + } // namespace blink #endif // THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_NG_INLINE_NG_TEXT_OFFSET_H_
diff --git a/third_party/blink/renderer/core/layout/ng/layout_ng_mixin.cc b/third_party/blink/renderer/core/layout/ng/layout_ng_mixin.cc index 82ab18a..fca6537c 100644 --- a/third_party/blink/renderer/core/layout/ng/layout_ng_mixin.cc +++ b/third_party/blink/renderer/core/layout/ng/layout_ng_mixin.cc
@@ -15,6 +15,7 @@ #include "third_party/blink/renderer/core/layout/ng/ng_layout_result.h" #include "third_party/blink/renderer/core/layout/ng/ng_length_utils.h" #include "third_party/blink/renderer/core/layout/ng/ng_out_of_flow_layout_part.h" +#include "third_party/blink/renderer/core/layout/ng/ng_physical_box_fragment.h" #include "third_party/blink/renderer/core/paint/ng/ng_box_fragment_painter.h" #include "third_party/blink/renderer/core/paint/paint_layer_scrollable_area.h" @@ -128,7 +129,8 @@ // Table cells borders may be collapsed, we can't calculate these directly // from the style. if (Base::IsTableCell()) { - builder.SetIsTableCell(true); + DCHECK(Base::IsTableCellLegacy()); + builder.SetIsTableCell(true, /* is_legacy_table_cell */ true); builder.SetTableCellBorders({Base::BorderStart(), Base::BorderEnd(), Base::BorderBefore(), Base::BorderAfter()}); }
diff --git a/third_party/blink/renderer/core/layout/ng/ng_box_fragment_builder.h b/third_party/blink/renderer/core/layout/ng/ng_box_fragment_builder.h index ca5a2ea2..3bf2360 100644 --- a/third_party/blink/renderer/core/layout/ng/ng_box_fragment_builder.h +++ b/third_party/blink/renderer/core/layout/ng/ng_box_fragment_builder.h
@@ -16,6 +16,8 @@ #include "third_party/blink/renderer/core/layout/ng/ng_container_fragment_builder.h" #include "third_party/blink/renderer/core/layout/ng/ng_layout_result.h" #include "third_party/blink/renderer/core/layout/ng/ng_length_utils.h" +#include "third_party/blink/renderer/core/layout/ng/table/ng_table_borders.h" +#include "third_party/blink/renderer/core/layout/ng/table/ng_table_fragment_data.h" #include "third_party/blink/renderer/core/style/computed_style_constants.h" #include "third_party/blink/renderer/platform/wtf/allocator/allocator.h" #include "third_party/blink/renderer/platform/wtf/hash_map.h" @@ -85,7 +87,6 @@ void AdjustBorderScrollbarPaddingForTableCell() { if (!space_->IsTableCell()) return; - border_scrollbar_padding_ += ComputeIntrinsicPadding(*space_, *style_, Scrollbar()); } @@ -133,6 +134,7 @@ #endif size_.block_size = block_size; } + LayoutUnit FragmentBlockSize() const { #if DCHECK_IS_ON() if (has_block_fragmentation_) @@ -440,6 +442,34 @@ // circumstances. This function updates |LastBaseline| in such cases. void SetLastBaselineToBlockEndMarginEdgeIfNeeded(); + void SetTableGridRect(const PhysicalRect& table_grid_rect) { + table_grid_rect_ = table_grid_rect; + } + + void SetTableColumnGeometry( + const NGTableFragmentData::ColumnGeometries& table_column_geometries) { + table_column_geometries_ = table_column_geometries; + } + + void SetTableCollapsedBorders(const NGTableBorders& table_collapsed_borders) { + table_collapsed_borders_ = &table_collapsed_borders; + } + + void SetTableCollapsedBordersGeometry( + std::unique_ptr<NGTableFragmentData::CollapsedBordersGeometry> + table_collapsed_borders_geometry) { + table_collapsed_borders_geometry_ = + std::move(table_collapsed_borders_geometry); + } + + void SetTableColumnCount(wtf_size_t table_column_count) { + table_column_count_ = table_column_count; + } + + void SetTableCellColumnIndex(wtf_size_t table_cell_column_index) { + table_cell_column_index_ = table_cell_column_index; + } + // The |NGFragmentItemsBuilder| for the inline formatting context of this box. NGFragmentItemsBuilder* ItemsBuilder() { return items_builder_; } void SetItemsBuilder(NGFragmentItemsBuilder* builder) { @@ -529,6 +559,19 @@ base::Optional<LayoutUnit> baseline_; base::Optional<LayoutUnit> last_baseline_; + + // Table specific types. + base::Optional<PhysicalRect> table_grid_rect_; + base::Optional<NGTableFragmentData::ColumnGeometries> + table_column_geometries_; + scoped_refptr<const NGTableBorders> table_collapsed_borders_; + std::unique_ptr<NGTableFragmentData::CollapsedBordersGeometry> + table_collapsed_borders_geometry_; + base::Optional<wtf_size_t> table_column_count_; + + // Table cell specific types. + base::Optional<wtf_size_t> table_cell_column_index_; + NGBorderEdges border_edges_; scoped_refptr<SerializedScriptValue> custom_layout_data_;
diff --git a/third_party/blink/renderer/core/layout/ng/ng_constraint_space.cc b/third_party/blink/renderer/core/layout/ng/ng_constraint_space.cc index 5fdcbccc..e50b6a5 100644 --- a/third_party/blink/renderer/core/layout/ng/ng_constraint_space.cc +++ b/third_party/blink/renderer/core/layout/ng/ng_constraint_space.cc
@@ -92,7 +92,8 @@ const ComputedStyle& cell_style = cell.ToLayoutObject()->StyleRef(); const ComputedStyle& table_style = cell.TableInterface()->ToLayoutObject()->StyleRef(); - builder.SetIsTableCell(true); + DCHECK(block.IsTableCellLegacy()); + builder.SetIsTableCell(true, /* is_table_cell_legacy */ true); builder.SetIsRestrictedBlockSizeTableCell( !cell_style.LogicalHeight().IsAuto() || !table_style.LogicalHeight().IsAuto());
diff --git a/third_party/blink/renderer/core/layout/ng/ng_constraint_space.h b/third_party/blink/renderer/core/layout/ng/ng_constraint_space.h index 6d0b8193..10c0a9a 100644 --- a/third_party/blink/renderer/core/layout/ng/ng_constraint_space.h +++ b/third_party/blink/renderer/core/layout/ng/ng_constraint_space.h
@@ -15,6 +15,7 @@ #include "third_party/blink/renderer/core/layout/ng/geometry/ng_margin_strut.h" #include "third_party/blink/renderer/core/layout/ng/ng_break_appeal.h" #include "third_party/blink/renderer/core/layout/ng/ng_floats_utils.h" +#include "third_party/blink/renderer/core/layout/ng/table/ng_table_constraint_space_data.h" #include "third_party/blink/renderer/platform/text/text_direction.h" #include "third_party/blink/renderer/platform/text/writing_mode.h" #include "third_party/blink/renderer/platform/wtf/ref_counted.h" @@ -298,12 +299,35 @@ return HasRareData() ? rare_data_->TableCellBorders() : NGBoxStrut(); } + wtf_size_t TableCellColumnIndex() const { + return HasRareData() ? rare_data_->TableCellColumnIndex() : 0; + } + // Return the "intrinsic" padding for a table-cell. NGBoxStrut TableCellIntrinsicPadding() const { return HasRareData() ? rare_data_->TableCellIntrinsicPadding() : NGBoxStrut(); } + // Return the baseline offset which the table-cell children should align + // their baseline to. + base::Optional<LayoutUnit> TableCellAlignmentBaseline() const { + return HasRareData() ? rare_data_->TableCellAlignmentBaseline() + : base::nullopt; + } + + const NGTableConstraintSpaceData* TableData() const { + return HasRareData() ? rare_data_->TableData() : nullptr; + } + + wtf_size_t TableRowIndex() const { + return HasRareData() ? rare_data_->TableRowIndex() : kNotFound; + } + + wtf_size_t TableSectionIndex() const { + return HasRareData() ? rare_data_->TableSectionIndex() : kNotFound; + } + LayoutUnit FragmentainerBlockSize() const { return HasRareData() ? rare_data_->fragmentainer_block_size : kIndefiniteSize; @@ -347,6 +371,7 @@ // Whether the current node is a table-cell. bool IsTableCell() const { return bitfields_.is_table_cell; } + bool IsLegacyTableCell() const { return bitfields_.is_legacy_table_cell; } // Whether the table-cell fragment should be hidden (not painted) if it has // no children. @@ -670,10 +695,12 @@ // They fall into the following categories: enum DataUnionType { kNone, - kBlockData, // An inflow block which doesn't establish a new FC. - kTableCellData, // A table-cell (display: table-cell). - kCustomData, // A custom layout (display: layout(foo)). - kStretchData // The target inline/block stretch sizes for MathML. + kBlockData, // An inflow block which doesn't establish a new FC. + kTableCellData, // A table-cell (display: table-cell). + kTableRowData, // A table-row (display: table-row). + kTableSectionData, // A table-section (display: table-section). + kCustomData, // A custom layout (display: layout(foo)). + kStretchData // The target inline/block stretch sizes for MathML. }; explicit RareData(const NGBfcOffset bfc_offset) @@ -712,6 +739,13 @@ case kTableCellData: new (&table_cell_data_) TableCellData(other.table_cell_data_); break; + case kTableRowData: + new (&table_row_data_) TableRowData(other.table_row_data_); + break; + case kTableSectionData: + new (&table_section_data_) + TableSectionData(other.table_section_data_); + break; case kCustomData: new (&custom_data_) CustomData(other.custom_data_); break; @@ -732,6 +766,12 @@ case kTableCellData: table_cell_data_.~TableCellData(); break; + case kTableRowData: + table_row_data_.~TableRowData(); + break; + case kTableSectionData: + table_section_data_.~TableSectionData(); + break; case kCustomData: custom_data_.~CustomData(); break; @@ -757,20 +797,24 @@ early_break_appeal != other.early_break_appeal) return false; - if (data_union_type == kNone) - return true; - - if (data_union_type == kBlockData) - return block_data_.MaySkipLayout(other.block_data_); - - if (data_union_type == kTableCellData) - return table_cell_data_.MaySkipLayout(other.table_cell_data_); - - if (data_union_type == kCustomData) - return custom_data_.MaySkipLayout(other.custom_data_); - - DCHECK_EQ(data_union_type, kStretchData); - return stretch_data_.MaySkipLayout(other.stretch_data_); + switch (data_union_type) { + case kNone: + return true; + case kBlockData: + return block_data_.MaySkipLayout(other.block_data_); + case kTableCellData: + return table_cell_data_.MaySkipLayout(other.table_cell_data_); + case kTableRowData: + return table_row_data_.MaySkipLayout(other.table_row_data_); + case kTableSectionData: + return table_section_data_.MaySkipLayout(other.table_section_data_); + case kCustomData: + return custom_data_.MaySkipLayout(other.custom_data_); + case kStretchData: + return stretch_data_.MaySkipLayout(other.stretch_data_); + } + NOTREACHED(); + return false; } // Must be kept in sync with members checked within |MaySkipLayout|. @@ -783,20 +827,24 @@ early_break_appeal != kBreakAppealLastResort) return false; - if (data_union_type == kNone) - return true; - - if (data_union_type == kBlockData) - return block_data_.IsInitialForMaySkipLayout(); - - if (data_union_type == kTableCellData) - return table_cell_data_.IsInitialForMaySkipLayout(); - - if (data_union_type == kCustomData) - return custom_data_.IsInitialForMaySkipLayout(); - - DCHECK_EQ(data_union_type, kStretchData); - return stretch_data_.IsInitialForMaySkipLayout(); + switch (data_union_type) { + case kNone: + return true; + case kBlockData: + return block_data_.IsInitialForMaySkipLayout(); + case kTableCellData: + return table_cell_data_.IsInitialForMaySkipLayout(); + case kTableRowData: + return table_row_data_.IsInitialForMaySkipLayout(); + case kTableSectionData: + return table_section_data_.IsInitialForMaySkipLayout(); + case kCustomData: + return custom_data_.IsInitialForMaySkipLayout(); + case kStretchData: + return stretch_data_.IsInitialForMaySkipLayout(); + } + NOTREACHED(); + return false; } LayoutUnit BlockStartAnnotationSpace() const { @@ -882,6 +930,61 @@ table_cell_intrinsic_padding.block_end; } + wtf_size_t TableCellColumnIndex() const { + return data_union_type == kTableCellData + ? table_cell_data_.table_cell_column_index + : 0; + } + + void SetTableCellColumnIndex(wtf_size_t table_cell_column_index) { + EnsureTableCellData()->table_cell_column_index = table_cell_column_index; + } + + base::Optional<LayoutUnit> TableCellAlignmentBaseline() const { + return data_union_type == kTableCellData + ? table_cell_data_.table_cell_alignment_baseline + : base::nullopt; + } + + void SetTableCellAlignmentBaseline( + LayoutUnit table_cell_alignment_baseline) { + EnsureTableCellData()->table_cell_alignment_baseline = + table_cell_alignment_baseline; + } + + void SetTableRowData( + scoped_refptr<const NGTableConstraintSpaceData> table_data, + wtf_size_t row_index) { + EnsureTableRowData()->table_data = std::move(table_data); + EnsureTableRowData()->row_index = row_index; + } + + void SetTableSectionData( + scoped_refptr<const NGTableConstraintSpaceData> table_data, + wtf_size_t section_index) { + EnsureTableSectionData()->table_data = std::move(table_data); + EnsureTableSectionData()->section_index = section_index; + } + + const NGTableConstraintSpaceData* TableData() { + if (data_union_type == kTableRowData) + return EnsureTableRowData()->table_data.get(); + if (data_union_type == kTableSectionData) + return EnsureTableSectionData()->table_data.get(); + return nullptr; + } + + wtf_size_t TableRowIndex() { + return data_union_type == kTableRowData ? EnsureTableRowData()->row_index + : kNotFound; + } + + wtf_size_t TableSectionIndex() { + return data_union_type == kTableSectionData + ? EnsureTableSectionData()->section_index + : kNotFound; + } + SerializedScriptValue* CustomLayoutData() const { return data_union_type == kCustomData ? custom_data_.data.get() : nullptr; } @@ -964,18 +1067,52 @@ table_cell_intrinsic_padding_block_start == other.table_cell_intrinsic_padding_block_start && table_cell_intrinsic_padding_block_end == - other.table_cell_intrinsic_padding_block_end; + other.table_cell_intrinsic_padding_block_end && + table_cell_alignment_baseline == + other.table_cell_alignment_baseline && + table_cell_column_index == other.table_cell_column_index; } bool IsInitialForMaySkipLayout() const { return table_cell_borders == NGBoxStrut() && table_cell_intrinsic_padding_block_start == LayoutUnit() && - table_cell_intrinsic_padding_block_end == LayoutUnit(); + table_cell_intrinsic_padding_block_end == LayoutUnit() && + table_cell_column_index == kNotFound && + table_cell_alignment_baseline == base::nullopt; } NGBoxStrut table_cell_borders; LayoutUnit table_cell_intrinsic_padding_block_start; LayoutUnit table_cell_intrinsic_padding_block_end; + wtf_size_t table_cell_column_index = kNotFound; + base::Optional<LayoutUnit> table_cell_alignment_baseline; + }; + + struct TableRowData { + bool MaySkipLayout(const TableRowData& other) const { + return table_data->EqualTableSpecificData(other.table_data.get()) && + table_data->MaySkipRowLayout(other.table_data.get(), row_index); + } + bool IsInitialForMaySkipLayout() const { + return !table_data && row_index == kNotFound; + } + + scoped_refptr<const NGTableConstraintSpaceData> table_data; + wtf_size_t row_index = kNotFound; + }; + + struct TableSectionData { + bool MaySkipLayout(const TableSectionData& other) const { + return table_data->EqualTableSpecificData(other.table_data.get()) && + table_data->MaySkipSectionLayout(other.table_data.get(), + section_index); + } + bool IsInitialForMaySkipLayout() const { + return !table_data && section_index == kNotFound; + } + + scoped_refptr<const NGTableConstraintSpaceData> table_data; + wtf_size_t section_index = kNotFound; }; struct CustomData { @@ -1024,6 +1161,24 @@ return &table_cell_data_; } + TableRowData* EnsureTableRowData() { + DCHECK(data_union_type == kNone || data_union_type == kTableRowData); + if (data_union_type != kTableRowData) { + data_union_type = kTableRowData; + new (&table_row_data_) TableRowData(); + } + return &table_row_data_; + } + + TableSectionData* EnsureTableSectionData() { + DCHECK(data_union_type == kNone || data_union_type == kTableSectionData); + if (data_union_type != kTableSectionData) { + data_union_type = kTableSectionData; + new (&table_section_data_) TableSectionData(); + } + return &table_section_data_; + } + CustomData* EnsureCustomData() { DCHECK(data_union_type == kNone || data_union_type == kCustomData); if (data_union_type != kCustomData) { @@ -1045,6 +1200,8 @@ union { BlockData block_data_; TableCellData table_cell_data_; + TableRowData table_row_data_; + TableSectionData table_section_data_; CustomData custom_data_; StretchData stretch_data_; }; @@ -1065,6 +1222,7 @@ writing_mode(static_cast<unsigned>(writing_mode)), direction(static_cast<unsigned>(TextDirection::kLtr)), is_table_cell(false), + is_legacy_table_cell(false), is_anonymous(false), is_new_formatting_context(false), is_orthogonal_writing_mode_root(false), @@ -1090,6 +1248,7 @@ writing_mode == other.writing_mode && direction == other.direction && is_table_cell == other.is_table_cell && + is_legacy_table_cell == other.is_legacy_table_cell && is_anonymous == other.is_anonymous && is_new_formatting_context == other.is_new_formatting_context && is_orthogonal_writing_mode_root == @@ -1117,6 +1276,8 @@ unsigned direction : 1; unsigned is_table_cell : 1; + unsigned is_legacy_table_cell : 1; + unsigned is_anonymous : 1; unsigned is_new_formatting_context : 1; unsigned is_orthogonal_writing_mode_root : 1;
diff --git a/third_party/blink/renderer/core/layout/ng/ng_constraint_space_builder.h b/third_party/blink/renderer/core/layout/ng/ng_constraint_space_builder.h index 5da0ad3..96f1f9e 100644 --- a/third_party/blink/renderer/core/layout/ng/ng_constraint_space_builder.h +++ b/third_party/blink/renderer/core/layout/ng/ng_constraint_space_builder.h
@@ -172,7 +172,11 @@ space_.EnsureRareData()->early_break_appeal = appeal; } - void SetIsTableCell(bool b) { space_.bitfields_.is_table_cell = b; } + // is_legacy_table_cell must always be assigned if is_table_cell is true. + void SetIsTableCell(bool is_table_cell, bool is_legacy_table_cell) { + space_.bitfields_.is_table_cell = is_table_cell; + space_.bitfields_.is_legacy_table_cell = is_legacy_table_cell; + } void SetIsRestrictedBlockSizeTableCell(bool b) { DCHECK(space_.bitfields_.is_table_cell); @@ -182,7 +186,6 @@ } void SetHideTableCellIfEmpty(bool b) { - DCHECK(space_.bitfields_.is_table_cell); if (!b && !space_.rare_data_) return; space_.EnsureRareData()->hide_table_cell_if_empty = b; @@ -300,6 +303,23 @@ } } + void SetTableCellAlignmentBaseline(LayoutUnit table_cell_alignment_baseline) { +#if DCHECK_IS_ON() + DCHECK(!is_table_cell_alignment_baseline_set_); + is_table_cell_alignment_baseline_set_ = true; +#endif + space_.EnsureRareData()->SetTableCellAlignmentBaseline( + table_cell_alignment_baseline); + } + + void SetTableCellColumnIndex(wtf_size_t column_index) { +#if DCHECK_IS_ON() + DCHECK(!is_table_cell_column_index_set_); + is_table_cell_column_index_set_ = true; +#endif + space_.EnsureRareData()->SetTableCellColumnIndex(column_index); + } + void SetTableCellChildLayoutMode( NGTableCellChildLayoutMode table_cell_child_layout_mode) { space_.bitfields_.table_cell_child_layout_mode = @@ -323,6 +343,26 @@ } } + void SetTableRowData(const NGTableConstraintSpaceData* table_data, + wtf_size_t row_index) { +#if DCHECK_IS_ON() + DCHECK(!is_table_row_data_set_); + is_table_row_data_set_ = true; +#endif + space_.EnsureRareData()->SetTableRowData(std::move(table_data), row_index); + } + + void SetTableSectionData( + scoped_refptr<const NGTableConstraintSpaceData> table_data, + wtf_size_t section_index) { +#if DCHECK_IS_ON() + DCHECK(!is_table_section_data_set_); + is_table_section_data_set_ = true; +#endif + space_.EnsureRareData()->SetTableSectionData(std::move(table_data), + section_index); + } + void SetLinesUntilClamp(const base::Optional<int>& clamp) { #if DCHECK_IS_ON() DCHECK(!is_lines_until_clamp_set_); @@ -395,8 +435,12 @@ bool is_clearance_offset_set_ = false; bool is_table_cell_borders_set_ = false; bool is_table_cell_intrinsic_padding_set_ = false; + bool is_table_cell_alignment_baseline_set_ = false; + bool is_table_cell_column_index_set_ = false; bool is_custom_layout_data_set_ = false; bool is_lines_until_clamp_set_ = false; + bool is_table_row_data_set_ = false; + bool is_table_section_data_set_ = false; bool to_constraint_space_called_ = false; #endif
diff --git a/third_party/blink/renderer/core/layout/ng/ng_fieldset_layout_algorithm.cc b/third_party/blink/renderer/core/layout/ng/ng_fieldset_layout_algorithm.cc index 82012a0..f57b41b 100644 --- a/third_party/blink/renderer/core/layout/ng/ng_fieldset_layout_algorithm.cc +++ b/third_party/blink/renderer/core/layout/ng/ng_fieldset_layout_algorithm.cc
@@ -325,18 +325,45 @@ // of the fieldset. As a paint effect, the block-start border will be // pushed so that the center of the border will be flush with the center // of the border-box of the legend. - // TODO(mstensho): inline alignment - // + + LayoutUnit legend_inline_start = ComputeLegendInlineOffset( + legend.Style(), + NGFragment(writing_mode_, result->PhysicalFragment()).InlineSize(), + legend_margins, BorderScrollbarPadding().inline_start, + ChildAvailableSize().inline_size); + // NOTE: For painting purposes, this must be kept in sync with: // NGFieldsetPainter::PaintFieldsetDecorationBackground - LogicalOffset legend_offset = { - BorderScrollbarPadding().inline_start + legend_margins.inline_start, - block_offset}; + LogicalOffset legend_offset = {legend_inline_start, block_offset}; container_builder_.AddResult(*result, legend_offset); return NGBreakStatus::kContinue; } +LayoutUnit NGFieldsetLayoutAlgorithm::ComputeLegendInlineOffset( + const ComputedStyle& legend_style, + LayoutUnit legend_border_box_inline_size, + const NGBoxStrut& legend_margins, + LayoutUnit fieldset_border_padding_inline_start, + LayoutUnit fieldset_content_inline_size) { + const ETextAlign align = legend_style.GetTextAlign(); + const ETextAlign align_end = legend_style.IsLeftToRightDirection() + ? ETextAlign::kRight + : ETextAlign::kLeft; + LayoutUnit legend_inline_start = fieldset_border_padding_inline_start; + if (align == ETextAlign::kCenter) { + legend_inline_start += + (fieldset_content_inline_size - legend_border_box_inline_size) / 2; + } else if (align == align_end) { + legend_inline_start += fieldset_content_inline_size - + legend_border_box_inline_size - + legend_margins.inline_end; + } else { + legend_inline_start += legend_margins.inline_start; + } + return legend_inline_start; +} + NGBreakStatus NGFieldsetLayoutAlgorithm::LayoutFieldsetContent( NGBlockNode& fieldset_content, scoped_refptr<const NGBlockBreakToken> content_break_token,
diff --git a/third_party/blink/renderer/core/layout/ng/ng_fieldset_layout_algorithm.h b/third_party/blink/renderer/core/layout/ng/ng_fieldset_layout_algorithm.h index 043c052..ac41aa7c 100644 --- a/third_party/blink/renderer/core/layout/ng/ng_fieldset_layout_algorithm.h +++ b/third_party/blink/renderer/core/layout/ng/ng_fieldset_layout_algorithm.h
@@ -27,6 +27,13 @@ MinMaxSizesResult ComputeMinMaxSizes(const MinMaxSizesInput&) const override; + static LayoutUnit ComputeLegendInlineOffset( + const ComputedStyle& legend_style, + LayoutUnit legend_border_box_inline_size, + const NGBoxStrut& legend_margins, + LayoutUnit fieldset_border_padding_inline_start, + LayoutUnit fieldset_content_inline_size); + private: NGBreakStatus LayoutChildren(); NGBreakStatus LayoutLegend(
diff --git a/third_party/blink/renderer/core/layout/ng/ng_layout_result.cc b/third_party/blink/renderer/core/layout/ng/ng_layout_result.cc index f250c4e2..fed1b4f 100644 --- a/third_party/blink/renderer/core/layout/ng/ng_layout_result.cc +++ b/third_party/blink/renderer/core/layout/ng/ng_layout_result.cc
@@ -96,6 +96,8 @@ static_cast<unsigned>(builder->previous_break_after_); bitfields_.has_forced_break = builder->has_forced_break_; } + if (builder->table_column_count_) + EnsureRareData()->table_column_count_ = *builder->table_column_count_; } NGLayoutResult::NGLayoutResult(
diff --git a/third_party/blink/renderer/core/layout/ng/ng_layout_result.h b/third_party/blink/renderer/core/layout/ng/ng_layout_result.h index 4674ca5..f0609c8e3 100644 --- a/third_party/blink/renderer/core/layout/ng/ng_layout_result.h +++ b/third_party/blink/renderer/core/layout/ng/ng_layout_result.h
@@ -216,6 +216,10 @@ return HasRareData() ? rare_data_->custom_layout_data.get() : nullptr; } + wtf_size_t TableColumnCount() const { + return HasRareData() ? rare_data_->table_column_count_ : 0; + } + // The break-before value on the first child needs to be propagated to the // container, in search of a valid class A break point. EBreakBetween InitialBreakBefore() const { @@ -400,14 +404,17 @@ }; NGExclusionSpace exclusion_space; scoped_refptr<SerializedScriptValue> custom_layout_data; + LayoutUnit overflow_block_size = kIndefiniteSize; LayoutUnit annotation_overflow; LayoutUnit block_end_annotation_space; + #if DCHECK_IS_ON() bool has_tallest_unbreakable_block_size = false; #endif bool is_single_use = false; int lines_until_clamp = 0; + wtf_size_t table_column_count_ = 0; }; bool HasRareData() const { return bitfields_.has_rare_data; }
diff --git a/third_party/blink/renderer/core/layout/ng/ng_physical_box_fragment.cc b/third_party/blink/renderer/core/layout/ng/ng_physical_box_fragment.cc index 2cfed6f..6f29cf2 100644 --- a/third_party/blink/renderer/core/layout/ng/ng_physical_box_fragment.cc +++ b/third_party/blink/renderer/core/layout/ng/ng_physical_box_fragment.cc
@@ -53,7 +53,11 @@ builder->GetWritingMode(), builder->Direction()); bool has_rare_data = builder->mathml_paint_info_ || - !builder->oof_positioned_fragmentainer_descendants_.IsEmpty(); + !builder->oof_positioned_fragmentainer_descendants_.IsEmpty() || + builder->table_grid_rect_ || builder->table_column_geometries_ || + builder->table_collapsed_borders_.get() || + builder->table_collapsed_borders_geometry_ || + builder->table_cell_column_index_; size_t byte_size = sizeof(NGPhysicalBoxFragment) + sizeof(NGLink) * builder->children_.size() + (borders.IsZero() ? 0 : sizeof(borders)) + @@ -171,6 +175,18 @@ : PhysicalSize()), descendant.containing_block_fragment); } + if (builder->table_grid_rect_) + table_grid_rect_ = *builder->table_grid_rect_; + if (builder->table_column_geometries_) + table_column_geometries_ = *builder->table_column_geometries_; + if (builder->table_collapsed_borders_.get()) + table_collapsed_borders_ = builder->table_collapsed_borders_.get(); + if (builder->table_collapsed_borders_geometry_) { + table_collapsed_borders_geometry_ = + std::move(builder->table_collapsed_borders_geometry_); + } + if (builder->table_cell_column_index_) + table_cell_column_index_ = *builder->table_cell_column_index_; } scoped_refptr<const NGLayoutResult> @@ -501,12 +517,12 @@ NGPixelSnappedPhysicalBoxStrut NGPhysicalBoxFragment::BorderWidths() const { unsigned edges = BorderEdges(); - NGPhysicalBoxStrut box_strut( - BorderWidth(edges, NGBorderEdges::kTop, Style().BorderTopWidth()), - BorderWidth(edges, NGBorderEdges::kRight, Style().BorderRightWidth()), - BorderWidth(edges, NGBorderEdges::kBottom, Style().BorderBottomWidth()), - BorderWidth(edges, NGBorderEdges::kLeft, Style().BorderLeftWidth())); - return box_strut.SnapToDevicePixels(); + NGPhysicalBoxStrut borders = Borders(); + borders.top = BorderWidth(edges, NGBorderEdges::kTop, borders.top); + borders.right = BorderWidth(edges, NGBorderEdges::kRight, borders.right); + borders.bottom = BorderWidth(edges, NGBorderEdges::kBottom, borders.bottom); + borders.left = BorderWidth(edges, NGBorderEdges::kLeft, borders.left); + return borders.SnapToDevicePixels(); } #if DCHECK_IS_ON()
diff --git a/third_party/blink/renderer/core/layout/ng/ng_physical_box_fragment.h b/third_party/blink/renderer/core/layout/ng/ng_physical_box_fragment.h index ddd755b5..29e8511a 100644 --- a/third_party/blink/renderer/core/layout/ng/ng_physical_box_fragment.h +++ b/third_party/blink/renderer/core/layout/ng/ng_physical_box_fragment.h
@@ -10,6 +10,8 @@ #include "third_party/blink/renderer/core/layout/ng/inline/ng_fragment_items.h" #include "third_party/blink/renderer/core/layout/ng/mathml/ng_mathml_paint_info.h" #include "third_party/blink/renderer/core/layout/ng/ng_physical_container_fragment.h" +#include "third_party/blink/renderer/core/layout/ng/table/ng_table_borders.h" +#include "third_party/blink/renderer/core/layout/ng/table/ng_table_fragment_data.h" #include "third_party/blink/renderer/platform/graphics/scroll_types.h" #include "third_party/blink/renderer/platform/wtf/casting.h" @@ -62,6 +64,31 @@ return base::nullopt; } + PhysicalRect TableGridRect() const { + return ComputeRareDataAddress()->table_grid_rect_; + } + + const NGTableFragmentData::ColumnGeometries* TableColumnGeometries() const { + return &ComputeRareDataAddress()->table_column_geometries_; + } + + const NGTableBorders* TableCollapsedBorders() const { + return ComputeRareDataAddress()->table_collapsed_borders_.get(); + } + + const NGTableFragmentData::CollapsedBordersGeometry* + TableCollapsedBordersGeometry() const { + auto& table_collapsed_borders_geometry = + ComputeRareDataAddress()->table_collapsed_borders_geometry_; + if (!table_collapsed_borders_geometry) + return nullptr; + return table_collapsed_borders_geometry.get(); + } + + wtf_size_t TableCellColumnIndex() const { + return ComputeRareDataAddress()->table_cell_column_index_; + } + const NGPhysicalBoxStrut Borders() const { if (!has_borders_) return NGPhysicalBoxStrut(); @@ -198,6 +225,14 @@ Vector<NGPhysicalOutOfFlowPositionedNode> oof_positioned_fragmentainer_descendants; const std::unique_ptr<NGMathMLPaintInfo> mathml_paint_info; + + // TablesNG rare data. + PhysicalRect table_grid_rect_; + NGTableFragmentData::ColumnGeometries table_column_geometries_; + scoped_refptr<const NGTableBorders> table_collapsed_borders_; + std::unique_ptr<NGTableFragmentData::CollapsedBordersGeometry> + table_collapsed_borders_geometry_; + wtf_size_t table_cell_column_index_; }; const NGFragmentItems* ComputeItemsAddress() const {
diff --git a/third_party/blink/renderer/core/layout/ng/ng_physical_fragment.h b/third_party/blink/renderer/core/layout/ng/ng_physical_fragment.h index 9eadb09..6f7077e 100644 --- a/third_party/blink/renderer/core/layout/ng/ng_physical_fragment.h +++ b/third_party/blink/renderer/core/layout/ng/ng_physical_fragment.h
@@ -158,6 +158,8 @@ layout_object_->IsRubyBase(); } + bool IsTable() const { return IsBox() && layout_object_->IsTable(); } + // Return true if this fragment is a container established by a fieldset // element. Such a fragment contains an optional rendered legend fragment and // an optional fieldset contents wrapper fragment (which holds everything
diff --git a/third_party/blink/renderer/core/layout/ng/table/ng_table_borders.cc b/third_party/blink/renderer/core/layout/ng/table/ng_table_borders.cc index 00c99e3..5790411 100644 --- a/third_party/blink/renderer/core/layout/ng/table/ng_table_borders.cc +++ b/third_party/blink/renderer/core/layout/ng/table/ng_table_borders.cc
@@ -23,13 +23,13 @@ if (!edge.style || source_style == EBorderStyle::kHidden) return true; - EBorderStyle edge_border_style = NGTableBorders::BorderStyle( - edge.style, NGTableBorders::ToBoxSide(edge.edge_side)); + EBorderStyle edge_border_style = + NGTableBorders::BorderStyle(edge.style.get(), edge.edge_side); if (edge_border_style == EBorderStyle::kHidden) return false; - LayoutUnit edge_width = NGTableBorders::BorderWidth( - edge.style, NGTableBorders::ToBoxSide(edge.edge_side)); + LayoutUnit edge_width = + NGTableBorders::BorderWidth(edge.style.get(), edge.edge_side); if (source_width < edge_width) return false; if (source_width > edge_width) @@ -37,19 +37,18 @@ return source_style > edge_border_style; } -NGTableBorders::EdgeSide ToEdgeSide(BoxSide box_side) { - return static_cast<NGTableBorders::EdgeSide>(box_side); -} - } // namespace NGTableBorders::NGTableBorders(const ComputedStyle& table_style, - const NGBoxStrut& table_border_padding) + const NGBoxStrut& table_border, + const NGBoxStrut& table_padding) : writing_direction_(table_style.GetWritingDirection()), is_collapsed_(table_style.BorderCollapse() == EBorderCollapse::kCollapse) { - if (!is_collapsed_) - cached_table_border_padding_ = table_border_padding; + if (!is_collapsed_) { + cached_table_border_ = table_border; + cached_table_padding_ = table_padding; + } } #if DCHECK_IS_ON() @@ -117,12 +116,16 @@ for (wtf_size_t i = 0; i < rowspan; ++i) { wtf_size_t start_edge_index = first_inline_start_edge + i * edges_per_row_; border_strut.inline_start = - std::max(border_strut.inline_start, BorderWidth(start_edge_index)); + std::max(border_strut.inline_start, HasEdgeAtIndex(start_edge_index) + ? BorderWidth(start_edge_index) + : LayoutUnit()); if (start_edge_index >= edges_.size()) break; wtf_size_t end_edge_index = first_inline_end_edge + i * edges_per_row_; border_strut.inline_end = - std::max(border_strut.inline_end, BorderWidth(end_edge_index)); + std::max(border_strut.inline_end, HasEdgeAtIndex(end_edge_index) + ? BorderWidth(end_edge_index) + : LayoutUnit()); } // Compute block border widths. wtf_size_t start_edge_column_index = column * 2 + 1; @@ -132,10 +135,14 @@ break; wtf_size_t start_edge_index = row * edges_per_row_ + current_column_index; border_strut.block_start = - std::max(border_strut.block_start, BorderWidth(start_edge_index)); + std::max(border_strut.block_start, HasEdgeAtIndex(start_edge_index) + ? BorderWidth(start_edge_index) + : LayoutUnit()); wtf_size_t end_edge_index = start_edge_index + rowspan * edges_per_row_; border_strut.block_end = - std::max(border_strut.block_end, BorderWidth(end_edge_index)); + std::max(border_strut.block_end, HasEdgeAtIndex(end_edge_index) + ? BorderWidth(end_edge_index) + : LayoutUnit()); } DCHECK(is_collapsed_); // If borders are not divisible by 2, two half borders will not add up @@ -156,7 +163,7 @@ // block[start|end] borders are computed by traversing all the edges. // inline[start|end] borders are computed by looking at first/last edge. if (edges_per_row_ == 0) { - cached_table_border_padding_ = NGBoxStrut(); + cached_table_border_ = NGBoxStrut(); return; } DCHECK_GE((table_column_count + 1) * 2, edges_per_row_); @@ -167,11 +174,13 @@ collapsed_visual_inline_end_ = borders.inline_end; wtf_size_t inline_start_edge = 0; wtf_size_t inline_end_edge = 2 * table_column_count; - borders.inline_start = BorderWidth(inline_start_edge) / 2; - borders.inline_end = inline_end_edge < edges_.size() + borders.inline_start = HasEdgeAtIndex(inline_start_edge) + ? BorderWidth(inline_start_edge) / 2 + : LayoutUnit(); + borders.inline_end = HasEdgeAtIndex(inline_end_edge) ? BorderWidth(inline_end_edge) / 2 : LayoutUnit(); - cached_table_border_padding_ = borders; + cached_table_border_ = borders; } NGBoxStrut NGTableBorders::CellBorder(wtf_size_t row, @@ -258,14 +267,14 @@ } } MergeRowAxisBorder(cell_start_row, cell_start_column, clamped_colspan, - source_style, LogicalBoxSide::kBlockStart); + source_style, LogicalEdgeSide::kBlockStart); MergeRowAxisBorder(cell_start_row + clamped_rowspan, cell_start_column, - clamped_colspan, source_style, LogicalBoxSide::kBlockEnd); + clamped_colspan, source_style, LogicalEdgeSide::kBlockEnd); MergeColumnAxisBorder(cell_start_row, cell_start_column, clamped_rowspan, - source_style, LogicalBoxSide::kInlineStart); + source_style, LogicalEdgeSide::kInlineStart); MergeColumnAxisBorder(cell_start_row, cell_start_column + clamped_colspan, clamped_rowspan, source_style, - LogicalBoxSide::kInlineEnd); + LogicalEdgeSide::kInlineEnd); if (mark_inner_borders) { MarkInnerBordersAsDoNotFill(cell_start_row, cell_start_column, clamped_rowspan, clamped_colspan); @@ -276,8 +285,8 @@ wtf_size_t start_column, wtf_size_t colspan, const ComputedStyle* source_style, - LogicalBoxSide logical_side) { - BoxSide physical_side = LogicalToPhysical(logical_side); + LogicalEdgeSide logical_side) { + EdgeSide physical_side = LogicalToPhysical(logical_side); EBorderStyle source_border_style = BorderStyle(source_style, physical_side); if (source_border_style == EBorderStyle::kNone) return; @@ -290,7 +299,7 @@ if (IsSourceMoreSpecificThanEdge(source_border_style, source_border_width, edges_[current_edge])) { edges_[current_edge].style = source_style; - edges_[current_edge].edge_side = ToEdgeSide(physical_side); + edges_[current_edge].edge_side = physical_side; } } } @@ -299,8 +308,8 @@ wtf_size_t start_column, wtf_size_t rowspan, const ComputedStyle* source_style, - LogicalBoxSide logical_side) { - BoxSide physical_side = LogicalToPhysical(logical_side); + LogicalEdgeSide logical_side) { + EdgeSide physical_side = LogicalToPhysical(logical_side); EBorderStyle source_border_style = BorderStyle(source_style, physical_side); if (source_border_style == EBorderStyle::kNone) return; @@ -313,7 +322,7 @@ if (IsSourceMoreSpecificThanEdge(source_border_style, source_border_width, edges_[current_edge])) { edges_[current_edge].style = source_style; - edges_[current_edge].edge_side = ToEdgeSide(physical_side); + edges_[current_edge].edge_side = physical_side; } } }
diff --git a/third_party/blink/renderer/core/layout/ng/table/ng_table_borders.h b/third_party/blink/renderer/core/layout/ng/table/ng_table_borders.h index 4fdde32..c4585be 100644 --- a/third_party/blink/renderer/core/layout/ng/table/ng_table_borders.h +++ b/third_party/blink/renderer/core/layout/ng/table/ng_table_borders.h
@@ -5,6 +5,7 @@ #ifndef THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_NG_TABLE_NG_TABLE_BORDERS_H_ #define THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_NG_TABLE_NG_TABLE_BORDERS_H_ +#include "base/memory/scoped_refptr.h" #include "third_party/blink/renderer/core/style/computed_style.h" #include "third_party/blink/renderer/core/style/computed_style_constants.h" #include "third_party/blink/renderer/platform/geometry/layout_unit.h" @@ -53,13 +54,12 @@ // | | | | // |9 |11 |13 |15 -class NGTableBorders { - STACK_ALLOCATED(); - +class NGTableBorders : public RefCounted<NGTableBorders> { public: // |table_border_padding| as computed from css values. NGTableBorders(const ComputedStyle& table_style, - const NGBoxStrut& table_border_padding); + const NGBoxStrut& table_border, + const NGBoxStrut& table_padding); #if DCHECK_IS_ON() String DumpEdges(); @@ -67,7 +67,7 @@ #endif // Side of the style the collapsed border belongs to. - enum class LogicalBoxSide { + enum class LogicalEdgeSide { kInlineStart, kInlineEnd, kBlockStart, @@ -83,55 +83,77 @@ // Edge is defined by a style, and box side. Box side specifies which // style border defines the edge. struct Edge { - const ComputedStyle* style; + scoped_refptr<const ComputedStyle> style; EdgeSide edge_side; }; - static inline BoxSide ToBoxSide(EdgeSide edge_side) { - DCHECK_NE(edge_side, EdgeSide::kDoNotFill); - return static_cast<BoxSide>(edge_side); - } - - static LayoutUnit BorderWidth(const ComputedStyle* style, BoxSide box_side) { - switch (box_side) { - case BoxSide::kLeft: + static LayoutUnit BorderWidth(const ComputedStyle* style, + EdgeSide edge_side) { + if (!style) + return LayoutUnit(); + switch (edge_side) { + case EdgeSide::kLeft: return LayoutUnit(style->BorderLeftWidth()); - case BoxSide::kRight: + case EdgeSide::kRight: return LayoutUnit(style->BorderRightWidth()); - case BoxSide::kTop: + case EdgeSide::kTop: return LayoutUnit(style->BorderTopWidth()); - case BoxSide::kBottom: + case EdgeSide::kBottom: return LayoutUnit(style->BorderBottomWidth()); + case EdgeSide::kDoNotFill: + return LayoutUnit(); } } static EBorderStyle BorderStyle(const ComputedStyle* style, - BoxSide box_side) { - switch (box_side) { - case BoxSide::kLeft: + EdgeSide edge_side) { + if (!style) + return EBorderStyle::kNone; + switch (edge_side) { + case EdgeSide::kLeft: return style->BorderLeftStyle(); - case BoxSide::kRight: + case EdgeSide::kRight: return style->BorderRightStyle(); - case BoxSide::kTop: + case EdgeSide::kTop: return style->BorderTopStyle(); - case BoxSide::kBottom: + case EdgeSide::kBottom: return style->BorderBottomStyle(); + case EdgeSide::kDoNotFill: + return EBorderStyle::kNone; } } - static Color BorderColor(const ComputedStyle* style, BoxSide box_side) { - switch (box_side) { - case BoxSide::kLeft: + static Color BorderColor(const ComputedStyle* style, EdgeSide edge_side) { + switch (edge_side) { + case EdgeSide::kLeft: return style->VisitedDependentColor(GetCSSPropertyBorderLeftColor()); - case BoxSide::kRight: + case EdgeSide::kRight: return style->VisitedDependentColor(GetCSSPropertyBorderRightColor()); - case BoxSide::kTop: + case EdgeSide::kTop: return style->VisitedDependentColor(GetCSSPropertyBorderTopColor()); - case BoxSide::kBottom: + case EdgeSide::kBottom: + return style->VisitedDependentColor(GetCSSPropertyBorderBottomColor()); + case EdgeSide::kDoNotFill: + NOTREACHED(); return style->VisitedDependentColor(GetCSSPropertyBorderBottomColor()); } } + LayoutUnit BorderWidth(wtf_size_t edge_index) const { + return BorderWidth(edges_[edge_index].style.get(), + edges_[edge_index].edge_side); + } + + EBorderStyle BorderStyle(wtf_size_t edge_index) const { + return BorderStyle(edges_[edge_index].style.get(), + edges_[edge_index].edge_side); + } + + Color BorderColor(wtf_size_t edge_index) const { + return BorderColor(edges_[edge_index].style.get(), + edges_[edge_index].edge_side); + } + using Edges = Vector<Edge>; struct Section { @@ -145,18 +167,14 @@ wtf_size_t EdgesPerRow() const { return edges_per_row_; } - LayoutUnit BorderWidth(wtf_size_t edge_index) const { - if (edge_index < edges_.size() && edges_[edge_index].style && - edges_[edge_index].edge_side != EdgeSide::kDoNotFill) { - return BorderWidth(edges_[edge_index].style, - ToBoxSide(edges_[edge_index].edge_side)); - } - return LayoutUnit(); + NGBoxStrut TableBorderPadding() const { + DCHECK(cached_table_border_); + return *cached_table_border_ + cached_table_padding_; } - NGBoxStrut TableBorderPadding() const { - DCHECK(cached_table_border_padding_); - return *cached_table_border_padding_; + NGBoxStrut TableBorder() const { + DCHECK(cached_table_border_); + return *cached_table_border_; } WritingDirectionMode TableWritingDirection() const { @@ -211,6 +229,21 @@ Edges::const_iterator end() const { return edges_.end(); } + wtf_size_t EdgeCount() const { return edges_.size(); } + + bool HasEdgeAtIndex(wtf_size_t edge_index) const { + return edge_index < edges_.size() && edges_[edge_index].style; + } + + // Is there and edge at edges[edge_index + index_offset]? + bool HasEdgeAtIndex(wtf_size_t edge_index, int index_offset) const { + return (index_offset >= 0 || + (index_offset < 0 && + edge_index >= static_cast<wtf_size_t>(abs(index_offset)))) && + edge_index + index_offset < edges_.size() && + edges_[edge_index + index_offset].style; + } + private: wtf_size_t ClampColspan(wtf_size_t column, wtf_size_t colspan) const { DCHECK_GE(last_column_index_, column); @@ -242,79 +275,79 @@ wtf_size_t start_column, wtf_size_t colspan, const ComputedStyle* source_style, - LogicalBoxSide side); + LogicalEdgeSide side); void MergeColumnAxisBorder(wtf_size_t start_row, wtf_size_t start_column, wtf_size_t rowspan, const ComputedStyle* source_style, - LogicalBoxSide side); + LogicalEdgeSide side); void MarkInnerBordersAsDoNotFill(wtf_size_t start_row, wtf_size_t start_column, wtf_size_t rowspan, wtf_size_t colspan); - BoxSide LogicalToPhysical(LogicalBoxSide logical_side) const { + EdgeSide LogicalToPhysical(LogicalEdgeSide logical_side) const { // https://www.w3.org/TR/css-writing-modes-4/#logical-to-physical switch (logical_side) { - case LogicalBoxSide::kInlineStart: + case LogicalEdgeSide::kInlineStart: switch (writing_direction_.GetWritingMode()) { case WritingMode::kHorizontalTb: return writing_direction_.Direction() == TextDirection::kLtr - ? BoxSide::kLeft - : BoxSide::kRight; + ? EdgeSide::kLeft + : EdgeSide::kRight; case WritingMode::kVerticalLr: case WritingMode::kVerticalRl: case WritingMode::kSidewaysRl: return writing_direction_.Direction() == TextDirection::kLtr - ? BoxSide::kTop - : BoxSide::kBottom; + ? EdgeSide::kTop + : EdgeSide::kBottom; case WritingMode::kSidewaysLr: return writing_direction_.Direction() == TextDirection::kLtr - ? BoxSide::kBottom - : BoxSide::kTop; + ? EdgeSide::kBottom + : EdgeSide::kTop; } - case LogicalBoxSide::kInlineEnd: + case LogicalEdgeSide::kInlineEnd: switch (writing_direction_.GetWritingMode()) { case WritingMode::kHorizontalTb: return writing_direction_.Direction() == TextDirection::kLtr - ? BoxSide::kRight - : BoxSide::kLeft; + ? EdgeSide::kRight + : EdgeSide::kLeft; case WritingMode::kVerticalLr: case WritingMode::kVerticalRl: case WritingMode::kSidewaysRl: return writing_direction_.Direction() == TextDirection::kLtr - ? BoxSide::kBottom - : BoxSide::kTop; + ? EdgeSide::kBottom + : EdgeSide::kTop; case WritingMode::kSidewaysLr: return writing_direction_.Direction() == TextDirection::kLtr - ? BoxSide::kTop - : BoxSide::kBottom; + ? EdgeSide::kTop + : EdgeSide::kBottom; } - case LogicalBoxSide::kBlockStart: + case LogicalEdgeSide::kBlockStart: switch (writing_direction_.GetWritingMode()) { case WritingMode::kHorizontalTb: - return BoxSide::kTop; + return EdgeSide::kTop; case WritingMode::kVerticalLr: - return BoxSide::kLeft; + return EdgeSide::kLeft; case WritingMode::kVerticalRl: case WritingMode::kSidewaysRl: - return BoxSide::kRight; + return EdgeSide::kRight; case WritingMode::kSidewaysLr: - return BoxSide::kLeft; + return EdgeSide::kLeft; } - case LogicalBoxSide::kBlockEnd: + case LogicalEdgeSide::kBlockEnd: switch (writing_direction_.GetWritingMode()) { case WritingMode::kHorizontalTb: - return BoxSide::kBottom; + return EdgeSide::kBottom; case WritingMode::kVerticalLr: - return BoxSide::kRight; + return EdgeSide::kRight; case WritingMode::kVerticalRl: case WritingMode::kSidewaysRl: - return BoxSide::kLeft; + return EdgeSide::kLeft; case WritingMode::kSidewaysLr: - return BoxSide::kRight; + return EdgeSide::kRight; } } } @@ -324,7 +357,8 @@ wtf_size_t edges_per_row_ = 0; // Table border/padding are expensive to compute for collapsed tables. // We compute them once, and cache them. - base::Optional<NGBoxStrut> cached_table_border_padding_; + base::Optional<NGBoxStrut> cached_table_border_; + NGBoxStrut cached_table_padding_; // Collapsed tables use first border to compute inline start/end. // Visual overflow use enclosing rectangle of all borders // to compute inline start/end.
diff --git a/third_party/blink/renderer/core/layout/ng/table/ng_table_constraint_space_data.h b/third_party/blink/renderer/core/layout/ng/table/ng_table_constraint_space_data.h new file mode 100644 index 0000000..06ed4233 --- /dev/null +++ b/third_party/blink/renderer/core/layout/ng/table/ng_table_constraint_space_data.h
@@ -0,0 +1,169 @@ +// 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_CORE_LAYOUT_NG_TABLE_NG_TABLE_CONSTRAINT_SPACE_DATA_H_ +#define THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_NG_TABLE_NG_TABLE_CONSTRAINT_SPACE_DATA_H_ + +#include "third_party/blink/renderer/core/layout/geometry/logical_size.h" +#include "third_party/blink/renderer/platform/geometry/layout_unit.h" +#include "third_party/blink/renderer/platform/text/writing_mode.h" +#include "third_party/blink/renderer/platform/wtf/ref_counted.h" +#include "third_party/blink/renderer/platform/wtf/vector.h" + +namespace blink { + +// Same NGTableConstraintSpaceData object gets passed on ConstraintSpace to +// all rows and sections in a single table. It contains all the geometry +// information needed to layout sections/rows/cells. This is different from most +// other algorithms, where constraint space data is not shared. +class NGTableConstraintSpaceData + : public RefCounted<NGTableConstraintSpaceData> { + public: + // Table grid columns are used to compute cell geometry. + struct ColumnLocation { + ColumnLocation(LayoutUnit offset, LayoutUnit inline_size) + : offset(offset), inline_size(inline_size) {} + LayoutUnit offset; + LayoutUnit inline_size; + bool operator==(const ColumnLocation& other) const { + return offset == other.offset && inline_size == other.inline_size; + } + }; + + // Section hold row index information used to map between table and + // section row indexes. + struct Section { + Section(wtf_size_t start_row_index, wtf_size_t rowspan) + : start_row_index(start_row_index), rowspan(rowspan) {} + bool operator==(const Section& other) const { + return start_row_index == other.start_row_index && + rowspan == other.rowspan; + } + wtf_size_t start_row_index; // first section row in table grid. + wtf_size_t rowspan; + }; + + // Data needed by row layout algorithm. + struct Row { + Row(LayoutUnit baseline, + LayoutUnit block_size, + wtf_size_t start_cell_index, + wtf_size_t cell_count, + bool has_baseline_aligned_percentage_block_size_descendants, + bool is_collapsed) + : baseline(baseline), + block_size(block_size), + start_cell_index(start_cell_index), + cell_count(cell_count), + has_baseline_aligned_percentage_block_size_descendants( + has_baseline_aligned_percentage_block_size_descendants), + is_collapsed(is_collapsed) {} + bool operator==(const Row& other) const { + return baseline == other.baseline && block_size == other.block_size && + start_cell_index == other.start_cell_index && + cell_count == other.cell_count && + has_baseline_aligned_percentage_block_size_descendants == + other.has_baseline_aligned_percentage_block_size_descendants && + is_collapsed == other.is_collapsed; + } + bool operator!=(const Row& other) const { return !(*this == other); } + LayoutUnit baseline; + LayoutUnit block_size; + wtf_size_t start_cell_index; + wtf_size_t cell_count; + bool has_baseline_aligned_percentage_block_size_descendants; + bool is_collapsed; + }; + + // Data needed to layout a single cell. + struct Cell { + Cell(NGBoxStrut border_box_borders, + LayoutUnit block_size, + wtf_size_t start_column, + bool is_constrained) + : border_box_borders(border_box_borders), + block_size(block_size), + start_column(start_column), + is_constrained(is_constrained) {} + bool operator==(const Cell& other) const { + return border_box_borders == other.border_box_borders && + block_size == other.block_size && + is_constrained == other.is_constrained; + } + bool operator!=(const Cell& other) const { return !(*this == other); } + // Size of borders drawn on the inside of the border box. + NGBoxStrut border_box_borders; + // Size of the cell. Need this for cells that span multiple rows. + LayoutUnit block_size; + wtf_size_t start_column; + bool is_constrained; + }; + + bool EqualTableSpecificData(const NGTableConstraintSpaceData* other) const { + return table_inline_size == other->table_inline_size && + table_writing_direction == other->table_writing_direction && + table_border_spacing == other->table_border_spacing && + treat_table_block_size_as_constrained == + other->treat_table_block_size_as_constrained && + hide_table_cell_if_empty == other->hide_table_cell_if_empty && + column_locations == other->column_locations; + } + + bool MaySkipRowLayout(const NGTableConstraintSpaceData* other, + wtf_size_t row_index) const { + if (other->rows.size() <= row_index) + return false; + if (rows[row_index] != other->rows[row_index]) + return false; + DCHECK_LT(row_index, rows.size()); + wtf_size_t end_index = + rows[row_index].start_cell_index + rows[row_index].cell_count; + for (wtf_size_t cell_index = rows[row_index].start_cell_index; + cell_index < end_index; ++cell_index) { + if (cells[cell_index] != other->cells[cell_index]) + return false; + } + return true; + } + + bool MaySkipSectionLayout(const NGTableConstraintSpaceData* other, + wtf_size_t section_index) const { + if (other->sections.size() <= section_index) + return false; + DCHECK_LT(section_index, sections.size()); + wtf_size_t end_index = sections[section_index].start_row_index + + sections[section_index].rowspan; + for (wtf_size_t row_index = sections[section_index].start_row_index; + row_index < end_index; ++row_index) { + if (!MaySkipRowLayout(other, row_index)) + return false; + } + return true; + } + + Vector<ColumnLocation> column_locations; + Vector<Section> sections; + Vector<Row> rows; + Vector<Cell> cells; + LayoutUnit table_inline_size; + WritingDirectionMode table_writing_direction = + WritingDirectionMode(WritingMode::kHorizontalTb, TextDirection::kLtr); + LogicalSize table_border_spacing; + bool treat_table_block_size_as_constrained; + bool hide_table_cell_if_empty; // currently on regular constraint space. + bool has_collapsed_borders; +}; + +} // namespace blink + +WTF_ALLOW_MOVE_INIT_AND_COMPARE_WITH_MEM_FUNCTIONS( + blink::NGTableConstraintSpaceData::ColumnLocation) +WTF_ALLOW_MOVE_INIT_AND_COMPARE_WITH_MEM_FUNCTIONS( + blink::NGTableConstraintSpaceData::Section) +WTF_ALLOW_MOVE_INIT_AND_COMPARE_WITH_MEM_FUNCTIONS( + blink::NGTableConstraintSpaceData::Row) +WTF_ALLOW_MOVE_INIT_AND_COMPARE_WITH_MEM_FUNCTIONS( + blink::NGTableConstraintSpaceData::Cell) + +#endif // THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_NG_TABLE_NG_TABLE_CONSTRAINT_SPACE_DATA_H_
diff --git a/third_party/blink/renderer/core/layout/ng/table/ng_table_fragment_data.h b/third_party/blink/renderer/core/layout/ng/table/ng_table_fragment_data.h new file mode 100644 index 0000000..c2aa3d8a --- /dev/null +++ b/third_party/blink/renderer/core/layout/ng/table/ng_table_fragment_data.h
@@ -0,0 +1,42 @@ +// 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_CORE_LAYOUT_NG_TABLE_NG_TABLE_FRAGMENT_DATA_H_ +#define THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_NG_TABLE_NG_TABLE_FRAGMENT_DATA_H_ + +#include "third_party/blink/renderer/core/layout/ng/ng_layout_input_node.h" +#include "third_party/blink/renderer/core/style/computed_style.h" +#include "third_party/blink/renderer/platform/geometry/layout_unit.h" +#include "third_party/blink/renderer/platform/wtf/vector.h" + +namespace blink { + +class NGTableFragmentData { + public: + // COLGROUP/COL geometry information. Used for painting column backgrounds. + // Only present if column has a background. + struct ColumnGeometry { + wtf_size_t start_column; + wtf_size_t span; + LayoutUnit inline_offset; + LayoutUnit inline_size; + NGLayoutInputNode node; + }; + + using ColumnGeometries = Vector<ColumnGeometry>; + + // Column/row location is used for collapsed border painting. + // Only present if borders are collapsed. + struct CollapsedBordersGeometry { + Vector<LayoutUnit> columns; // Column offsets from table grid border. + Vector<LayoutUnit> rows; // Row offsets from table grid border. + }; +}; + +} // namespace blink + +WTF_ALLOW_MOVE_INIT_AND_COMPARE_WITH_MEM_FUNCTIONS( + blink::NGTableFragmentData::ColumnGeometry) + +#endif // THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_NG_TABLE_NG_TABLE_FRAGMENT_DATA_H_
diff --git a/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm_types.cc b/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm_types.cc index ceeff00..32b456b 100644 --- a/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm_types.cc +++ b/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm_types.cc
@@ -49,6 +49,7 @@ } if (length.IsPercent()) *percentage_inline_size = length.Percent(); + if (*percentage_inline_size && max_length.IsPercent()) { *percentage_inline_size = std::min(**percentage_inline_size, max_length.Percent()); @@ -105,14 +106,14 @@ &css_inline_size, &css_min_inline_size, &css_max_inline_size, &css_percentage_inline_size); - MinMaxSizesInput input(kIndefiniteSize, MinMaxSizesType::kContent); + MinMaxSizesInput input(kIndefiniteSize, MinMaxSizesType::kIntrinsic); MinMaxSizesResult min_max_size; if (is_collapsed) { NGConstraintSpaceBuilder builder(table_writing_mode, node.Style().GetWritingMode(), /* is_new_fc */ false); builder.SetTableCellBorders(cell_border); - builder.SetIsTableCell(true); + builder.SetIsTableCell(true, /* is_legacy_table_cell */ false); NGConstraintSpace space = builder.ToConstraintSpace(); // It'd be nice to avoid computing minmax if not needed, but the criteria // is not clear. @@ -148,8 +149,11 @@ } else { content_max = min_max_size.sizes.max_size; } - if (css_max_inline_size) + if (css_max_inline_size) { content_max = std::min(content_max, *css_max_inline_size); + resolved_min_inline_size = + std::min(resolved_min_inline_size, *css_max_inline_size); + } LayoutUnit resolved_max_inline_size = std::max(resolved_min_inline_size, content_max); @@ -169,7 +173,7 @@ const Length& section_css_block_size = section.Style().LogicalHeight(); // TODO(crbug.com/1105272): Decide what to do with |Length::IsCalculated()|. bool is_constrained = - section_css_block_size.IsPercent() || section_css_block_size.IsFixed(); + section_css_block_size.IsFixed() || section_css_block_size.IsPercent(); base::Optional<float> percent; if (section_css_block_size.IsPercent()) percent = section_css_block_size.Percent();
diff --git a/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm_utils.cc b/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm_utils.cc index b85cfb5b..fa10519 100644 --- a/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm_utils.cc +++ b/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm_utils.cc
@@ -67,7 +67,7 @@ builder.SetPercentageResolutionSize(percentage_resolution_size); builder.SetReplacedPercentageResolutionSize(percentage_resolution_size); builder.SetIsFixedInlineSize(true); - builder.SetIsTableCell(true); + builder.SetIsTableCell(true, /* is_legacy_table_cell */ false); builder.SetIsRestrictedBlockSizeTableCell(is_restricted_block_size_table); builder.SetNeedsBaseline(true); builder.SetCacheSlot(NGCacheSlot::kMeasure);
diff --git a/third_party/blink/renderer/core/paint/compositing/composited_layer_mapping.cc b/third_party/blink/renderer/core/paint/compositing/composited_layer_mapping.cc index b3f7fe1..5cecf06 100644 --- a/third_party/blink/renderer/core/paint/compositing/composited_layer_mapping.cc +++ b/third_party/blink/renderer/core/paint/compositing/composited_layer_mapping.cc
@@ -1309,11 +1309,12 @@ CreateGraphicsLayer(CompositingReason::kLayerForScrollingContents); scrolling_contents_layer_->SetHitTestable(true); + DCHECK(scrollable_area); auto element_id = scrollable_area->GetScrollElementId(); scrolling_contents_layer_->SetElementId(element_id); layer_changed = true; - if (scrolling_coordinator && scrollable_area) { + if (scrolling_coordinator) { scrolling_coordinator->ScrollableAreaScrollLayerDidChange( scrollable_area); const auto& object = GetLayoutObject();
diff --git a/third_party/blink/renderer/core/paint/ng/ng_fieldset_painter.cc b/third_party/blink/renderer/core/paint/ng/ng_fieldset_painter.cc index 50246136..c1c55ee 100644 --- a/third_party/blink/renderer/core/paint/ng/ng_fieldset_painter.cc +++ b/third_party/blink/renderer/core/paint/ng/ng_fieldset_painter.cc
@@ -5,6 +5,7 @@ #include "third_party/blink/renderer/core/paint/ng/ng_fieldset_painter.h" #include "third_party/blink/renderer/core/layout/layout_box.h" +#include "third_party/blink/renderer/core/layout/ng/ng_fieldset_layout_algorithm.h" #include "third_party/blink/renderer/core/layout/ng/ng_length_utils.h" #include "third_party/blink/renderer/core/layout/ng/ng_physical_box_fragment.h" #include "third_party/blink/renderer/core/paint/background_image_geometry.h" @@ -59,21 +60,34 @@ legend_border_box.size = (*legend)->Size(); // Recalculate the legend offset without the relative position offset. + // Note that legend->Offset() is the offset after applying + // position:relative, but the fieldset border painting needs to avoid + // the legend position with static position. + // + // See https://html.spec.whatwg.org/C/#the-fieldset-and-legend-elements + // > * If the element has a rendered legend, then the border is expected to + // > not be painted behind the rectangle defined as follows, using the + // > writing mode of the fieldset: ... + // > ... at its static position (ignoring transforms), ... const WritingDirectionMode writing_direction = style.GetWritingDirection(); - const LogicalSize logical_fieldset_size = - fieldset_size.ConvertToLogical(writing_direction.GetWritingMode()); + const auto writing_mode = writing_direction.GetWritingMode(); const NGBoxStrut border_padding = (fragment.Borders() + fragment.Padding()) .ConvertToLogical(writing_direction.GetWritingMode(), writing_direction.Direction()); + const LayoutUnit fieldset_content_inline_size = + fieldset_size.ConvertToLogical(writing_mode).inline_size - + border_padding.InlineSum(); const NGBoxStrut legend_margins = ComputeMarginsFor( - (*legend)->Style(), - (logical_fieldset_size.inline_size - border_padding.InlineSum()) - .ClampNegativeToZero(), + (*legend)->Style(), fieldset_content_inline_size.ClampNegativeToZero(), writing_direction.GetWritingMode(), writing_direction.Direction()); const LogicalOffset offset = { - border_padding.inline_start + legend_margins.inline_start, + NGFieldsetLayoutAlgorithm::ComputeLegendInlineOffset( + (*legend)->Style(), + legend_border_box.size.ConvertToLogical(writing_mode).inline_size, + legend_margins, border_padding.inline_start, + fieldset_content_inline_size), legend_margins.block_start}; legend_border_box.offset = offset.ConvertToPhysical( writing_direction, fieldset_size, legend_border_box.size);
diff --git a/third_party/blink/renderer/core/script/classic_pending_script.cc b/third_party/blink/renderer/core/script/classic_pending_script.cc index ff3055c..7e29da6 100644 --- a/third_party/blink/renderer/core/script/classic_pending_script.cc +++ b/third_party/blink/renderer/core/script/classic_pending_script.cc
@@ -121,9 +121,8 @@ ClassicPendingScript::~ClassicPendingScript() {} NOINLINE void ClassicPendingScript::CheckState() const { - // TODO(hiroshige): Turn these CHECK()s into DCHECK() before going to beta. - CHECK(GetElement()); - CHECK_EQ(is_external_, !!GetResource()); + DCHECK(GetElement()); + DCHECK_EQ(is_external_, !!GetResource()); } namespace {
diff --git a/third_party/blink/renderer/core/svg/animation/smil_time.h b/third_party/blink/renderer/core/svg/animation/smil_time.h index fe5497d5..8cfe0d7 100644 --- a/third_party/blink/renderer/core/svg/animation/smil_time.h +++ b/third_party/blink/renderer/core/svg/animation/smil_time.h
@@ -114,6 +114,11 @@ DCHECK(other.IsFinite()); return time_.IntDiv(other.time_); } + double FltDiv(SMILTime other) const { + DCHECK(IsFinite()); + DCHECK(other.IsFinite()); + return time_.FltDiv(other.time_); + } SMILTime operator%(SMILTime other) const { DCHECK(IsFinite()); DCHECK(other.IsFinite());
diff --git a/third_party/blink/renderer/modules/credentialmanager/credential_manager_type_converters.cc b/third_party/blink/renderer/modules/credentialmanager/credential_manager_type_converters.cc index 8f348df45..2af684c 100644 --- a/third_party/blink/renderer/modules/credentialmanager/credential_manager_type_converters.cc +++ b/third_party/blink/renderer/modules/credentialmanager/credential_manager_type_converters.cc
@@ -295,20 +295,20 @@ // static AuthenticatorSelectionCriteriaPtr TypeConverter<AuthenticatorSelectionCriteriaPtr, - blink::AuthenticatorSelectionCriteria*>:: - Convert(const blink::AuthenticatorSelectionCriteria* criteria) { + blink::AuthenticatorSelectionCriteria>:: + Convert(const blink::AuthenticatorSelectionCriteria& criteria) { auto mojo_criteria = blink::mojom::blink::AuthenticatorSelectionCriteria::New(); base::Optional<String> attachment; - if (criteria->hasAuthenticatorAttachment()) - attachment = criteria->authenticatorAttachment(); + if (criteria.hasAuthenticatorAttachment()) + attachment = criteria.authenticatorAttachment(); mojo_criteria->authenticator_attachment = ConvertTo<AuthenticatorAttachment>(attachment); - mojo_criteria->require_resident_key = criteria->requireResidentKey(); + mojo_criteria->require_resident_key = criteria.requireResidentKey(); mojo_criteria->user_verification = UserVerificationRequirement::PREFERRED; - if (criteria->hasUserVerification()) { + if (criteria.hasUserVerification()) { mojo_criteria->user_verification = ConvertTo<UserVerificationRequirement>( - blink::IDLEnumAsString(criteria->userVerification())); + blink::IDLEnumAsString(criteria.userVerification())); } return mojo_criteria; } @@ -316,55 +316,60 @@ // static PublicKeyCredentialUserEntityPtr TypeConverter<PublicKeyCredentialUserEntityPtr, - blink::PublicKeyCredentialUserEntity*>:: - Convert(const blink::PublicKeyCredentialUserEntity* user) { + blink::PublicKeyCredentialUserEntity>:: + Convert(const blink::PublicKeyCredentialUserEntity& user) { auto entity = PublicKeyCredentialUserEntity::New(); - entity->id = ConvertTo<Vector<uint8_t>>(user->id()); - entity->name = user->name(); - if (user->hasIcon()) { - if (user->icon().IsEmpty()) + // PublicKeyCredentialEntity + entity->name = user.name(); + if (user.hasIcon()) { + if (user.icon().IsEmpty()) entity->icon = blink::KURL(); else - entity->icon = blink::KURL(user->icon()); + entity->icon = blink::KURL(user.icon()); } - entity->display_name = user->displayName(); + // PublicKeyCredentialUserEntity + entity->id = ConvertTo<Vector<uint8_t>>(user.id()); + entity->display_name = user.displayName(); return entity; } // static PublicKeyCredentialRpEntityPtr TypeConverter<PublicKeyCredentialRpEntityPtr, - blink::PublicKeyCredentialRpEntity*>:: - Convert(const blink::PublicKeyCredentialRpEntity* rp) { + blink::PublicKeyCredentialRpEntity>:: + Convert(const blink::PublicKeyCredentialRpEntity& rp) { auto entity = PublicKeyCredentialRpEntity::New(); - if (rp->hasId()) { - entity->id = rp->id(); - } - if (!rp->name()) { + // PublicKeyCredentialEntity + if (!rp.name()) { return nullptr; } - entity->name = rp->name(); - if (rp->hasIcon()) { - if (rp->icon().IsEmpty()) + entity->name = rp.name(); + if (rp.hasIcon()) { + if (rp.icon().IsEmpty()) entity->icon = blink::KURL(); else - entity->icon = blink::KURL(rp->icon()); + entity->icon = blink::KURL(rp.icon()); } + // PublicKeyCredentialRpEntity + if (rp.hasId()) { + entity->id = rp.id(); + } + return entity; } // static PublicKeyCredentialDescriptorPtr TypeConverter<PublicKeyCredentialDescriptorPtr, - blink::PublicKeyCredentialDescriptor*>:: - Convert(const blink::PublicKeyCredentialDescriptor* descriptor) { + blink::PublicKeyCredentialDescriptor>:: + Convert(const blink::PublicKeyCredentialDescriptor& descriptor) { auto mojo_descriptor = PublicKeyCredentialDescriptor::New(); mojo_descriptor->type = ConvertTo<PublicKeyCredentialType>( - blink::IDLEnumAsString(descriptor->type())); - mojo_descriptor->id = ConvertTo<Vector<uint8_t>>(descriptor->id()); - if (descriptor->hasTransports() && !descriptor->transports().IsEmpty()) { - for (const auto& transport : descriptor->transports()) { + blink::IDLEnumAsString(descriptor.type())); + mojo_descriptor->id = ConvertTo<Vector<uint8_t>>(descriptor.id()); + if (descriptor.hasTransports() && !descriptor.transports().IsEmpty()) { + for (const auto& transport : descriptor.transports()) { auto maybe_transport( ConvertTo<base::Optional<AuthenticatorTransport>>(transport)); if (maybe_transport) { @@ -383,48 +388,43 @@ // static PublicKeyCredentialParametersPtr TypeConverter<PublicKeyCredentialParametersPtr, - blink::PublicKeyCredentialParameters*>:: - Convert(const blink::PublicKeyCredentialParameters* parameter) { + blink::PublicKeyCredentialParameters>:: + Convert(const blink::PublicKeyCredentialParameters& parameter) { auto mojo_parameter = PublicKeyCredentialParameters::New(); mojo_parameter->type = ConvertTo<PublicKeyCredentialType>( - blink::IDLEnumAsString(parameter->type())); + blink::IDLEnumAsString(parameter.type())); // A COSEAlgorithmIdentifier's value is a number identifying a cryptographic // algorithm. Values are registered in the IANA COSE Algorithms registry. // https://www.iana.org/assignments/cose/cose.xhtml#algorithms - mojo_parameter->algorithm_identifier = parameter->alg(); + mojo_parameter->algorithm_identifier = parameter.alg(); return mojo_parameter; } // static PublicKeyCredentialCreationOptionsPtr TypeConverter<PublicKeyCredentialCreationOptionsPtr, - blink::PublicKeyCredentialCreationOptions*>:: - Convert(const blink::PublicKeyCredentialCreationOptions* options) { + blink::PublicKeyCredentialCreationOptions>:: + Convert(const blink::PublicKeyCredentialCreationOptions& options) { auto mojo_options = blink::mojom::blink::PublicKeyCredentialCreationOptions::New(); mojo_options->relying_party = - PublicKeyCredentialRpEntity::From(options->rp()); - mojo_options->user = PublicKeyCredentialUserEntity::From(options->user()); - if (!mojo_options->relying_party | !mojo_options->user) { + PublicKeyCredentialRpEntity::From(*options.rp()); + mojo_options->user = PublicKeyCredentialUserEntity::From(*options.user()); + if (!mojo_options->relying_party || !mojo_options->user) { return nullptr; } - mojo_options->challenge = ConvertTo<Vector<uint8_t>>(options->challenge()); - - if (options->hasTimeout()) { - mojo_options->timeout = - base::TimeDelta::FromMilliseconds(options->timeout()); - } + mojo_options->challenge = ConvertTo<Vector<uint8_t>>(options.challenge()); // Steps 7 and 8 of https://w3c.github.io/webauthn/#sctn-createCredential Vector<PublicKeyCredentialParametersPtr> parameters; - if (options->pubKeyCredParams().size() == 0) { + if (options.pubKeyCredParams().size() == 0) { parameters.push_back(CreatePublicKeyCredentialParameter(kCoseEs256)); parameters.push_back(CreatePublicKeyCredentialParameter(kCoseRs256)); } else { - for (auto& parameter : options->pubKeyCredParams()) { + for (auto& parameter : options.pubKeyCredParams()) { PublicKeyCredentialParametersPtr normalized_parameter = - PublicKeyCredentialParameters::From(parameter.Get()); + PublicKeyCredentialParameters::From(*parameter); if (normalized_parameter) { parameters.push_back(std::move(normalized_parameter)); } @@ -433,29 +433,31 @@ return nullptr; } } - mojo_options->public_key_parameters = std::move(parameters); - if (options->hasAuthenticatorSelection()) { - mojo_options->authenticator_selection = - AuthenticatorSelectionCriteria::From(options->authenticatorSelection()); + if (options.hasTimeout()) { + mojo_options->timeout = + base::TimeDelta::FromMilliseconds(options.timeout()); } - if (options->hasExcludeCredentials()) { - // Adds the excludeCredentials members - for (auto& descriptor : options->excludeCredentials()) { - PublicKeyCredentialDescriptorPtr mojo_descriptor = - PublicKeyCredentialDescriptor::From(descriptor.Get()); - if (mojo_descriptor) { - mojo_options->exclude_credentials.push_back(std::move(mojo_descriptor)); - } + // Adds the excludeCredentials members + for (auto& descriptor : options.excludeCredentials()) { + PublicKeyCredentialDescriptorPtr mojo_descriptor = + PublicKeyCredentialDescriptor::From(*descriptor); + if (mojo_descriptor) { + mojo_options->exclude_credentials.push_back(std::move(mojo_descriptor)); } } + if (options.hasAuthenticatorSelection()) { + mojo_options->authenticator_selection = + AuthenticatorSelectionCriteria::From(*options.authenticatorSelection()); + } + mojo_options->attestation = blink::mojom::AttestationConveyancePreference::NONE; - if (options->hasAttestation()) { - const auto& attestation = options->attestation(); + if (options.hasAttestation()) { + const auto& attestation = options.attestation(); if (attestation == "none") { // Default value. } else if (attestation == "indirect") { @@ -474,11 +476,14 @@ mojo_options->protection_policy = blink::mojom::ProtectionPolicy::UNSPECIFIED; mojo_options->enforce_protection_policy = false; - if (options->hasExtensions()) { - auto* extensions = options->extensions(); + if (options.hasExtensions()) { + auto* extensions = options.extensions(); + if (extensions->hasAppidExclude()) { + mojo_options->appid_exclude = extensions->appidExclude(); + } if (extensions->hasCableRegistration()) { CableRegistrationPtr mojo_cable = - CableRegistration::From(extensions->cableRegistration()); + CableRegistration::From(*extensions->cableRegistration()); if (mojo_cable) { mojo_options->cable_registration_data = std::move(mojo_cable); } @@ -486,9 +491,6 @@ if (extensions->hasHmacCreateSecret()) { mojo_options->hmac_create_secret = extensions->hmacCreateSecret(); } - if (extensions->hasAppidExclude()) { - mojo_options->appid_exclude = extensions->appidExclude(); - } #if defined(OS_ANDROID) if (extensions->hasUvm()) { mojo_options->user_verification_methods = extensions->uvm(); @@ -519,14 +521,14 @@ // static CableAuthenticationPtr -TypeConverter<CableAuthenticationPtr, blink::CableAuthenticationData*>::Convert( - const blink::CableAuthenticationData* data) { +TypeConverter<CableAuthenticationPtr, blink::CableAuthenticationData>::Convert( + const blink::CableAuthenticationData& data) { auto entity = CableAuthentication::New(); - entity->version = data->version(); - entity->client_eid = ConvertFixedSizeArray(data->clientEid(), 16); + entity->version = data.version(); + entity->client_eid = ConvertFixedSizeArray(data.clientEid(), 16); entity->authenticator_eid = - ConvertFixedSizeArray(data->authenticatorEid(), 16); - entity->session_pre_key = ConvertFixedSizeArray(data->sessionPreKey(), 32); + ConvertFixedSizeArray(data.authenticatorEid(), 16); + entity->session_pre_key = ConvertFixedSizeArray(data.sessionPreKey(), 32); if (entity->client_eid.IsEmpty() || entity->authenticator_eid.IsEmpty() || entity->session_pre_key.IsEmpty()) { return nullptr; @@ -536,12 +538,12 @@ // static CableRegistrationPtr -TypeConverter<CableRegistrationPtr, blink::CableRegistrationData*>::Convert( - const blink::CableRegistrationData* data) { +TypeConverter<CableRegistrationPtr, blink::CableRegistrationData>::Convert( + const blink::CableRegistrationData& data) { auto entity = CableRegistration::New(); - entity->versions = data->versions(); + entity->versions = data.versions(); entity->relying_party_public_key = - ConvertFixedSizeArray(data->rpPublicKey(), 65); + ConvertFixedSizeArray(data.rpPublicKey(), 65); if (entity->relying_party_public_key.IsEmpty()) { return nullptr; } @@ -551,40 +553,38 @@ // static PublicKeyCredentialRequestOptionsPtr TypeConverter<PublicKeyCredentialRequestOptionsPtr, - blink::PublicKeyCredentialRequestOptions*>:: - Convert(const blink::PublicKeyCredentialRequestOptions* options) { + blink::PublicKeyCredentialRequestOptions>:: + Convert(const blink::PublicKeyCredentialRequestOptions& options) { auto mojo_options = blink::mojom::blink::PublicKeyCredentialRequestOptions::New(); - mojo_options->challenge = ConvertTo<Vector<uint8_t>>(options->challenge()); + mojo_options->challenge = ConvertTo<Vector<uint8_t>>(options.challenge()); - if (options->hasTimeout()) { + if (options.hasTimeout()) { mojo_options->timeout = - base::TimeDelta::FromMilliseconds(options->timeout()); + base::TimeDelta::FromMilliseconds(options.timeout()); } - if (options->hasRpId()) { - mojo_options->relying_party_id = options->rpId(); + if (options.hasRpId()) { + mojo_options->relying_party_id = options.rpId(); } - if (options->hasAllowCredentials()) { - // Adds the allowList members - for (auto descriptor : options->allowCredentials()) { - PublicKeyCredentialDescriptorPtr mojo_descriptor = - PublicKeyCredentialDescriptor::From(descriptor.Get()); - if (mojo_descriptor) { - mojo_options->allow_credentials.push_back(std::move(mojo_descriptor)); - } + // Adds the allowList members + for (auto descriptor : options.allowCredentials()) { + PublicKeyCredentialDescriptorPtr mojo_descriptor = + PublicKeyCredentialDescriptor::From(*descriptor); + if (mojo_descriptor) { + mojo_options->allow_credentials.push_back(std::move(mojo_descriptor)); } } mojo_options->user_verification = UserVerificationRequirement::PREFERRED; - if (options->hasUserVerification()) { + if (options.hasUserVerification()) { mojo_options->user_verification = ConvertTo<UserVerificationRequirement>( - blink::IDLEnumAsString(options->userVerification())); + blink::IDLEnumAsString(options.userVerification())); } - if (options->hasExtensions()) { - auto* extensions = options->extensions(); + if (options.hasExtensions()) { + auto* extensions = options.extensions(); if (extensions->hasAppid()) { mojo_options->appid = extensions->appid(); } @@ -594,8 +594,7 @@ if (data->version() != 1) { continue; } - CableAuthenticationPtr mojo_cable = - CableAuthentication::From(data.Get()); + CableAuthenticationPtr mojo_cable = CableAuthentication::From(*data); if (mojo_cable) { mojo_data.push_back(std::move(mojo_cable)); }
diff --git a/third_party/blink/renderer/modules/credentialmanager/credential_manager_type_converters.h b/third_party/blink/renderer/modules/credentialmanager/credential_manager_type_converters.h index fe4afc6..3307e6b3 100644 --- a/third_party/blink/renderer/modules/credentialmanager/credential_manager_type_converters.h +++ b/third_party/blink/renderer/modules/credentialmanager/credential_manager_type_converters.h
@@ -99,65 +99,65 @@ template <> struct TypeConverter<blink::mojom::blink::AuthenticatorSelectionCriteriaPtr, - blink::AuthenticatorSelectionCriteria*> { + blink::AuthenticatorSelectionCriteria> { static blink::mojom::blink::AuthenticatorSelectionCriteriaPtr Convert( - const blink::AuthenticatorSelectionCriteria*); + const blink::AuthenticatorSelectionCriteria&); }; template <> struct TypeConverter<blink::mojom::blink::PublicKeyCredentialUserEntityPtr, - blink::PublicKeyCredentialUserEntity*> { + blink::PublicKeyCredentialUserEntity> { static blink::mojom::blink::PublicKeyCredentialUserEntityPtr Convert( - const blink::PublicKeyCredentialUserEntity*); + const blink::PublicKeyCredentialUserEntity&); }; template <> struct TypeConverter<blink::mojom::blink::PublicKeyCredentialRpEntityPtr, - blink::PublicKeyCredentialRpEntity*> { + blink::PublicKeyCredentialRpEntity> { static blink::mojom::blink::PublicKeyCredentialRpEntityPtr Convert( - const blink::PublicKeyCredentialRpEntity*); + const blink::PublicKeyCredentialRpEntity&); }; template <> struct TypeConverter<blink::mojom::blink::PublicKeyCredentialDescriptorPtr, - blink::PublicKeyCredentialDescriptor*> { + blink::PublicKeyCredentialDescriptor> { static blink::mojom::blink::PublicKeyCredentialDescriptorPtr Convert( - const blink::PublicKeyCredentialDescriptor*); + const blink::PublicKeyCredentialDescriptor&); }; template <> struct TypeConverter<blink::mojom::blink::PublicKeyCredentialParametersPtr, - blink::PublicKeyCredentialParameters*> { + blink::PublicKeyCredentialParameters> { static blink::mojom::blink::PublicKeyCredentialParametersPtr Convert( - const blink::PublicKeyCredentialParameters*); + const blink::PublicKeyCredentialParameters&); }; template <> struct TypeConverter<blink::mojom::blink::PublicKeyCredentialCreationOptionsPtr, - blink::PublicKeyCredentialCreationOptions*> { + blink::PublicKeyCredentialCreationOptions> { static blink::mojom::blink::PublicKeyCredentialCreationOptionsPtr Convert( - const blink::PublicKeyCredentialCreationOptions*); + const blink::PublicKeyCredentialCreationOptions&); }; template <> struct TypeConverter<blink::mojom::blink::CableAuthenticationPtr, - blink::CableAuthenticationData*> { + blink::CableAuthenticationData> { static blink::mojom::blink::CableAuthenticationPtr Convert( - const blink::CableAuthenticationData*); + const blink::CableAuthenticationData&); }; template <> struct TypeConverter<blink::mojom::blink::CableRegistrationPtr, - blink::CableRegistrationData*> { + blink::CableRegistrationData> { static blink::mojom::blink::CableRegistrationPtr Convert( - const blink::CableRegistrationData*); + const blink::CableRegistrationData&); }; template <> struct TypeConverter<blink::mojom::blink::PublicKeyCredentialRequestOptionsPtr, - blink::PublicKeyCredentialRequestOptions*> { + blink::PublicKeyCredentialRequestOptions> { static blink::mojom::blink::PublicKeyCredentialRequestOptionsPtr Convert( - const blink::PublicKeyCredentialRequestOptions*); + const blink::PublicKeyCredentialRequestOptions&); }; } // namespace mojo
diff --git a/third_party/blink/renderer/modules/credentialmanager/credentials_container.cc b/third_party/blink/renderer/modules/credentialmanager/credentials_container.cc index e2c0b14..645f0b2 100644 --- a/third_party/blink/renderer/modules/credentialmanager/credentials_container.cc +++ b/third_party/blink/renderer/modules/credentialmanager/credentials_container.cc
@@ -604,7 +604,7 @@ } auto mojo_options = - MojoPublicKeyCredentialRequestOptions::From(options->publicKey()); + MojoPublicKeyCredentialRequestOptions::From(*options->publicKey()); if (mojo_options) { if (!mojo_options->relying_party_id) { mojo_options->relying_party_id = resolver->GetFrame() @@ -839,7 +839,7 @@ } auto mojo_options = - MojoPublicKeyCredentialCreationOptions::From(options->publicKey()); + MojoPublicKeyCredentialCreationOptions::From(*options->publicKey()); if (!mojo_options) { resolver->Reject(MakeGarbageCollected<DOMException>( DOMExceptionCode::kNotSupportedError,
diff --git a/third_party/blink/renderer/modules/payments/payment_request.cc b/third_party/blink/renderer/modules/payments/payment_request.cc index 3e20ff6..a0e2ac8 100644 --- a/third_party/blink/renderer/modules/payments/payment_request.cc +++ b/third_party/blink/renderer/modules/payments/payment_request.cc
@@ -103,53 +103,53 @@ namespace mojo { template <> -struct TypeConverter<PaymentCurrencyAmountPtr, blink::PaymentCurrencyAmount*> { +struct TypeConverter<PaymentCurrencyAmountPtr, blink::PaymentCurrencyAmount> { static PaymentCurrencyAmountPtr Convert( - const blink::PaymentCurrencyAmount* input) { + const blink::PaymentCurrencyAmount& input) { PaymentCurrencyAmountPtr output = PaymentCurrencyAmount::New(); - output->currency = input->currency().UpperASCII(); - output->value = input->value(); + output->currency = input.currency().UpperASCII(); + output->value = input.value(); return output; } }; template <> -struct TypeConverter<PaymentItemPtr, blink::PaymentItem*> { - static PaymentItemPtr Convert(const blink::PaymentItem* input) { +struct TypeConverter<PaymentItemPtr, blink::PaymentItem> { + static PaymentItemPtr Convert(const blink::PaymentItem& input) { PaymentItemPtr output = payments::mojom::blink::PaymentItem::New(); - output->label = input->label(); - output->amount = PaymentCurrencyAmount::From(input->amount()); - output->pending = input->pending(); + output->label = input.label(); + output->amount = PaymentCurrencyAmount::From(*input.amount()); + output->pending = input.pending(); return output; } }; template <> -struct TypeConverter<PaymentShippingOptionPtr, blink::PaymentShippingOption*> { +struct TypeConverter<PaymentShippingOptionPtr, blink::PaymentShippingOption> { static PaymentShippingOptionPtr Convert( - const blink::PaymentShippingOption* input) { + const blink::PaymentShippingOption& input) { PaymentShippingOptionPtr output = payments::mojom::blink::PaymentShippingOption::New(); - output->id = input->id(); - output->label = input->label(); - output->amount = PaymentCurrencyAmount::From(input->amount()); - output->selected = input->hasSelected() && input->selected(); + output->id = input.id(); + output->label = input.label(); + output->amount = PaymentCurrencyAmount::From(*input.amount()); + output->selected = input.hasSelected() && input.selected(); return output; } }; template <> -struct TypeConverter<PaymentOptionsPtr, const blink::PaymentOptions*> { - static PaymentOptionsPtr Convert(const blink::PaymentOptions* input) { +struct TypeConverter<PaymentOptionsPtr, blink::PaymentOptions> { + static PaymentOptionsPtr Convert(const blink::PaymentOptions& input) { PaymentOptionsPtr output = payments::mojom::blink::PaymentOptions::New(); - output->request_payer_name = input->requestPayerName(); - output->request_payer_email = input->requestPayerEmail(); - output->request_payer_phone = input->requestPayerPhone(); - output->request_shipping = input->requestShipping(); + output->request_payer_name = input.requestPayerName(); + output->request_payer_email = input.requestPayerEmail(); + output->request_payer_phone = input.requestPayerPhone(); + output->request_shipping = input.requestShipping(); - if (input->shippingType() == "delivery") + if (input.shippingType() == "delivery") output->shipping_type = PaymentShippingType::DELIVERY; - else if (input->shippingType() == "pickup") + else if (input.shippingType() == "pickup") output->shipping_type = PaymentShippingType::PICKUP; else output->shipping_type = PaymentShippingType::SHIPPING; @@ -160,55 +160,55 @@ template <> struct TypeConverter<PaymentValidationErrorsPtr, - blink::PaymentValidationErrors*> { + blink::PaymentValidationErrors> { static PaymentValidationErrorsPtr Convert( - const blink::PaymentValidationErrors* input) { + const blink::PaymentValidationErrors& input) { PaymentValidationErrorsPtr output = payments::mojom::blink::PaymentValidationErrors::New(); - output->error = input->hasError() ? input->error() : g_empty_string; - output->payer = input->hasPayer() - ? PayerErrors::From(input->payer()) - : PayerErrors::From(blink::PayerErrors::Create()); - output->shipping_address = - input->hasShippingAddress() - ? AddressErrors::From(input->shippingAddress()) - : AddressErrors::From(blink::AddressErrors::Create()); + output->error = input.hasError() ? input.error() : g_empty_string; + auto* payer_errors = + input.hasPayer() ? input.payer() : blink::PayerErrors::Create(); + output->payer = PayerErrors::From(*payer_errors); + auto* address_errors = input.hasShippingAddress() + ? input.shippingAddress() + : blink::AddressErrors::Create(); + output->shipping_address = AddressErrors::From(*address_errors); return output; } }; template <> -struct TypeConverter<PayerErrorsPtr, blink::PayerErrors*> { - static PayerErrorsPtr Convert(const blink::PayerErrors* input) { +struct TypeConverter<PayerErrorsPtr, blink::PayerErrors> { + static PayerErrorsPtr Convert(const blink::PayerErrors& input) { PayerErrorsPtr output = payments::mojom::blink::PayerErrors::New(); - output->email = input->hasEmail() ? input->email() : g_empty_string; - output->name = input->hasName() ? input->name() : g_empty_string; - output->phone = input->hasPhone() ? input->phone() : g_empty_string; + output->email = input.hasEmail() ? input.email() : g_empty_string; + output->name = input.hasName() ? input.name() : g_empty_string; + output->phone = input.hasPhone() ? input.phone() : g_empty_string; return output; } }; template <> -struct TypeConverter<AddressErrorsPtr, blink::AddressErrors*> { - static AddressErrorsPtr Convert(const blink::AddressErrors* input) { +struct TypeConverter<AddressErrorsPtr, blink::AddressErrors> { + static AddressErrorsPtr Convert(const blink::AddressErrors& input) { AddressErrorsPtr output = payments::mojom::blink::AddressErrors::New(); output->address_line = - input->hasAddressLine() ? input->addressLine() : g_empty_string; - output->city = input->hasCity() ? input->city() : g_empty_string; - output->country = input->hasCountry() ? input->country() : g_empty_string; - output->dependent_locality = input->hasDependentLocality() - ? input->dependentLocality() + input.hasAddressLine() ? input.addressLine() : g_empty_string; + output->city = input.hasCity() ? input.city() : g_empty_string; + output->country = input.hasCountry() ? input.country() : g_empty_string; + output->dependent_locality = input.hasDependentLocality() + ? input.dependentLocality() : g_empty_string; output->organization = - input->hasOrganization() ? input->organization() : g_empty_string; - output->phone = input->hasPhone() ? input->phone() : g_empty_string; + input.hasOrganization() ? input.organization() : g_empty_string; + output->phone = input.hasPhone() ? input.phone() : g_empty_string; output->postal_code = - input->hasPostalCode() ? input->postalCode() : g_empty_string; + input.hasPostalCode() ? input.postalCode() : g_empty_string; output->recipient = - input->hasRecipient() ? input->recipient() : g_empty_string; - output->region = input->hasRegion() ? input->region() : g_empty_string; + input.hasRecipient() ? input.recipient() : g_empty_string; + output->region = input.hasRegion() ? input.region() : g_empty_string; output->sorting_code = - input->hasSortingCode() ? input->sortingCode() : g_empty_string; + input.hasSortingCode() ? input.sortingCode() : g_empty_string; return output; } }; @@ -303,7 +303,7 @@ exception_state); if (exception_state.HadException()) return; - output.push_back(payments::mojom::blink::PaymentItem::From(item)); + output.push_back(payments::mojom::blink::PaymentItem::From(*item)); } } @@ -357,7 +357,7 @@ unique_ids.insert(option->id()); output.push_back( - payments::mojom::blink::PaymentShippingOption::From(option)); + payments::mojom::blink::PaymentShippingOption::From(*option)); } } @@ -376,8 +376,7 @@ return; } - output = payments::mojom::blink::PaymentItem::From( - const_cast<PaymentItem*>(input)); + output = payments::mojom::blink::PaymentItem::From(*input); } // Parses Android Pay data to avoid parsing JSON in the browser. @@ -619,7 +618,7 @@ } output->shipping_address_errors = payments::mojom::blink::AddressErrors::From( - input->shippingAddressErrors()); + *input->shippingAddressErrors()); } if (input->hasPaymentMethodErrors()) { @@ -970,8 +969,7 @@ // The payment provider should respond in PaymentRequest::OnPaymentResponse(). payment_provider_->Retry( - payments::mojom::blink::PaymentValidationErrors::From( - const_cast<PaymentValidationErrors*>(errors))); + payments::mojom::blink::PaymentValidationErrors::From(*errors)); retry_resolver_ = MakeGarbageCollected<ScriptPromiseResolver>(script_state); @@ -1247,13 +1245,13 @@ payment_provider_->Init( std::move(client), std::move(validated_method_data), std::move(validated_details), - payments::mojom::blink::PaymentOptions::From(options_.Get()), + payments::mojom::blink::PaymentOptions::From(*options_), skip_to_gpay_ready); #else payment_provider_->Init( std::move(client), std::move(validated_method_data), std::move(validated_details), - payments::mojom::blink::PaymentOptions::From(options_.Get())); + payments::mojom::blink::PaymentOptions::From(*options_)); #endif }
diff --git a/third_party/blink/renderer/platform/BUILD.gn b/third_party/blink/renderer/platform/BUILD.gn index ccdfd97..557c4ab 100644 --- a/third_party/blink/renderer/platform/BUILD.gn +++ b/third_party/blink/renderer/platform/BUILD.gn
@@ -1469,12 +1469,6 @@ "widget/compositing/layer_tree_view.cc", "widget/compositing/layer_tree_view.h", "widget/compositing/layer_tree_view_delegate.h", - "widget/compositing/queue_report_time_swap_promise.cc", - "widget/compositing/queue_report_time_swap_promise.h", - "widget/compositing/widget_compositor.cc", - "widget/compositing/widget_compositor.h", - "widget/compositing/widget_swap_queue.cc", - "widget/compositing/widget_swap_queue.h", "widget/frame_widget.cc", "widget/frame_widget.h", "widget/input/compositor_thread_event_queue.cc", @@ -2036,7 +2030,6 @@ "widget/compositing/layer_tree_settings_unittest.cc", "widget/compositing/layer_tree_view_unittest.cc", "widget/compositing/test/stub_layer_tree_view_delegate.h", - "widget/compositing/widget_compositor_unittest.cc", "widget/input/elastic_overscroll_controller_bezier_unittest.cc", "widget/input/elastic_overscroll_controller_exponential_unittest.cc", "widget/input/input_event_prediction_unittest.cc",
diff --git a/third_party/blink/renderer/platform/widget/compositing/queue_report_time_swap_promise.cc b/third_party/blink/renderer/platform/widget/compositing/queue_report_time_swap_promise.cc deleted file mode 100644 index 8a6c899e..0000000 --- a/third_party/blink/renderer/platform/widget/compositing/queue_report_time_swap_promise.cc +++ /dev/null
@@ -1,69 +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. - -#include "third_party/blink/renderer/platform/widget/compositing/queue_report_time_swap_promise.h" - -#if defined(OS_ANDROID) -#include "third_party/blink/public/platform/platform.h" -#endif - -namespace blink { - -QueueReportTimeSwapPromise::QueueReportTimeSwapPromise( - int source_frame_number, - DrainCallback drain_callback, - base::OnceClosure swap_callback, - scoped_refptr<base::SingleThreadTaskRunner> compositor_task_runner) - : source_frame_number_(source_frame_number), - drain_callback_(std::move(drain_callback)), - swap_callback_(std::move(swap_callback)), -#if defined(OS_ANDROID) - call_swap_on_activate_( - Platform::Current()->IsSynchronousCompositingEnabled()), -#endif - compositor_task_runner_(std::move(compositor_task_runner)) { -} - -QueueReportTimeSwapPromise::~QueueReportTimeSwapPromise() { - if (compositor_task_runner_ && (drain_callback_ || swap_callback_)) { - DCHECK(!compositor_task_runner_->BelongsToCurrentThread()); - compositor_task_runner_->PostTask( - FROM_HERE, - base::BindOnce([](DrainCallback, base::OnceClosure) {}, - std::move(drain_callback_), std::move(swap_callback_))); - } -} - -void QueueReportTimeSwapPromise::WillSwap( - viz::CompositorFrameMetadata* metadata) { - DCHECK_GT(metadata->frame_token, 0u); -} - -void QueueReportTimeSwapPromise::DidSwap() { - if (swap_callback_) - std::move(swap_callback_).Run(); -} - -cc::SwapPromise::DidNotSwapAction QueueReportTimeSwapPromise::DidNotSwap( - DidNotSwapReason reason) { - if (reason == cc::SwapPromise::SWAP_FAILS || - reason == cc::SwapPromise::COMMIT_NO_UPDATE) { - if (drain_callback_) - std::move(drain_callback_).Run(source_frame_number_); - if (swap_callback_) - std::move(swap_callback_).Run(); - } - return DidNotSwapAction::BREAK_PROMISE; -} - -void QueueReportTimeSwapPromise::DidActivate() { - if (drain_callback_) - std::move(drain_callback_).Run(source_frame_number_); -#if defined(OS_ANDROID) - if (call_swap_on_activate_ && swap_callback_) - std::move(swap_callback_).Run(); -#endif -} - -} // namespace blink
diff --git a/third_party/blink/renderer/platform/widget/compositing/queue_report_time_swap_promise.h b/third_party/blink/renderer/platform/widget/compositing/queue_report_time_swap_promise.h deleted file mode 100644 index a1de1aa..0000000 --- a/third_party/blink/renderer/platform/widget/compositing/queue_report_time_swap_promise.h +++ /dev/null
@@ -1,52 +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. - -#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_WIDGET_COMPOSITING_QUEUE_REPORT_TIME_SWAP_PROMISE_H_ -#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_WIDGET_COMPOSITING_QUEUE_REPORT_TIME_SWAP_PROMISE_H_ - -#include "base/memory/scoped_refptr.h" -#include "base/single_thread_task_runner.h" -#include "build/build_config.h" -#include "cc/trees/swap_promise.h" - -namespace blink { - -// This class invokes DrainCallback to drain queued callbacks for frame numbers -// lower or equal to |source_frame_number| when the commit results in a -// successful activation of the pending layer tree in swap promise. -// -// This class doesn't have the reporting callback of the swap time. -class QueueReportTimeSwapPromise : public cc::SwapPromise { - public: - using DrainCallback = base::OnceCallback<void(int)>; - QueueReportTimeSwapPromise( - int source_frame_number, - DrainCallback drain_callback, - base::OnceClosure swap_callback, - scoped_refptr<base::SingleThreadTaskRunner> compositor_task_runner); - ~QueueReportTimeSwapPromise() override; - QueueReportTimeSwapPromise(const QueueReportTimeSwapPromise&) = delete; - QueueReportTimeSwapPromise& operator=(const QueueReportTimeSwapPromise&) = - delete; - - void WillSwap(viz::CompositorFrameMetadata* metadata) override; - void DidSwap() override; - cc::SwapPromise::DidNotSwapAction DidNotSwap( - DidNotSwapReason reason) override; - void DidActivate() override; - int64_t TraceId() const override { return 0; } - - private: - int source_frame_number_; - DrainCallback drain_callback_; - base::OnceClosure swap_callback_; -#if defined(OS_ANDROID) - const bool call_swap_on_activate_; -#endif - scoped_refptr<base::SingleThreadTaskRunner> compositor_task_runner_; -}; - -} // namespace blink - -#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_WIDGET_COMPOSITING_QUEUE_REPORT_TIME_SWAP_PROMISE_H_
diff --git a/third_party/blink/renderer/platform/widget/compositing/widget_compositor.cc b/third_party/blink/renderer/platform/widget/compositing/widget_compositor.cc deleted file mode 100644 index 88d33c7..0000000 --- a/third_party/blink/renderer/platform/widget/compositing/widget_compositor.cc +++ /dev/null
@@ -1,126 +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. - -#include "third_party/blink/renderer/platform/widget/compositing/widget_compositor.h" - -#include "cc/trees/layer_tree_host.h" -#include "third_party/blink/renderer/platform/widget/compositing/queue_report_time_swap_promise.h" -#include "third_party/blink/renderer/platform/widget/widget_base.h" - -namespace blink { - -WidgetCompositor::WidgetCompositor( - base::WeakPtr<WidgetBase> widget_base, - scoped_refptr<base::SingleThreadTaskRunner> main_task_runner, - scoped_refptr<base::SingleThreadTaskRunner> compositor_task_runner, - mojo::PendingReceiver<mojom::blink::WidgetCompositor> receiver) - : widget_base_(widget_base), - main_task_runner_(std::move(main_task_runner)), - compositor_task_runner_(std::move(compositor_task_runner)), - swap_queue_(std::make_unique<WidgetSwapQueue>()) { - if (!compositor_task_runner_) { - BindOnThread(std::move(receiver)); - } else { - compositor_task_runner_->PostTask( - FROM_HERE, base::BindOnce(&WidgetCompositor::BindOnThread, - base::Unretained(this), std::move(receiver))); - } -} - -void WidgetCompositor::Shutdown() { - if (!compositor_task_runner_) { - ResetOnThread(); - } else { - compositor_task_runner_->PostTask( - FROM_HERE, base::BindOnce(&WidgetCompositor::ResetOnThread, - scoped_refptr<WidgetCompositor>(this))); - } -} - -void WidgetCompositor::BindOnThread( - mojo::PendingReceiver<mojom::blink::WidgetCompositor> receiver) { - DCHECK(CalledOnValidCompositorThread()); - receiver_.Bind(std::move(receiver), compositor_task_runner_); -} - -void WidgetCompositor::ResetOnThread() { - DCHECK(CalledOnValidCompositorThread()); - receiver_.reset(); -} - -void WidgetCompositor::VisualStateRequest(VisualStateRequestCallback callback) { - DCHECK(CalledOnValidCompositorThread()); - - auto drain_callback = - WTF::Bind(&WidgetCompositor::DrainQueue, base::RetainedRef(this)); - auto swap_callback = WTF::Bind(&WidgetCompositor::VisualStateResponse, - base::RetainedRef(this)); - if (!compositor_task_runner_) { - CreateQueueSwapPromise(std::move(drain_callback), std::move(swap_callback), - std::move(callback)); - } else { - main_task_runner_->PostTask( - FROM_HERE, - base::BindOnce(&WidgetCompositor::CreateQueueSwapPromise, - base::RetainedRef(this), std::move(drain_callback), - std::move(swap_callback), std::move(callback))); - } -} - -cc::LayerTreeHost* WidgetCompositor::LayerTreeHost() const { - return widget_base_->LayerTreeHost(); -} - -void WidgetCompositor::CreateQueueSwapPromise( - base::OnceCallback<void(int)> drain_callback, - base::OnceClosure swap_callback, - VisualStateRequestCallback callback) { - DCHECK(main_task_runner_->BelongsToCurrentThread()); - - if (!widget_base_) - return; - - bool first_message_for_frame = false; - int source_frame_number = LayerTreeHost()->SourceFrameNumber(); - swap_queue_->Queue(source_frame_number, std::move(callback), - &first_message_for_frame); - if (first_message_for_frame) { - LayerTreeHost()->QueueSwapPromise( - std::make_unique<QueueReportTimeSwapPromise>( - source_frame_number, std::move(drain_callback), - std::move(swap_callback), compositor_task_runner_)); - // Request a main frame if one is not already in progress. This might either - // A) request a commit ahead of time or B) request a commit which is not - // needed because there are not pending updates. If B) then the frame will - // be aborted early and the swap promises will be broken (see - // EarlyOut_NoUpdates). - LayerTreeHost()->SetNeedsAnimateIfNotInsideMainFrame(); - } else if (compositor_task_runner_) { - // Delete callbacks on the compositor thread. - compositor_task_runner_->PostTask( - FROM_HERE, - base::BindOnce([](base::OnceCallback<void(int)>, base::OnceClosure) {}, - std::move(drain_callback), std::move(swap_callback))); - } -} - -void WidgetCompositor::VisualStateResponse() { - DCHECK(CalledOnValidCompositorThread()); - Vector<VisualStateRequestCallback> callbacks; - swap_queue_->GetCallbacks(&callbacks); - for (auto& callback : callbacks) - std::move(callback).Run(); -} - -void WidgetCompositor::DrainQueue(int source_frame_number) { - DCHECK(CalledOnValidCompositorThread()); - swap_queue_->Drain(source_frame_number); -} - -bool WidgetCompositor::CalledOnValidCompositorThread() { - return !compositor_task_runner_ || - compositor_task_runner_->BelongsToCurrentThread(); -} - -} // namespace blink
diff --git a/third_party/blink/renderer/platform/widget/compositing/widget_compositor.h b/third_party/blink/renderer/platform/widget/compositing/widget_compositor.h deleted file mode 100644 index dd8832f..0000000 --- a/third_party/blink/renderer/platform/widget/compositing/widget_compositor.h +++ /dev/null
@@ -1,63 +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. - -#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_WIDGET_COMPOSITING_WIDGET_COMPOSITOR_H_ -#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_WIDGET_COMPOSITING_WIDGET_COMPOSITOR_H_ - -#include "mojo/public/cpp/bindings/pending_receiver.h" -#include "mojo/public/cpp/bindings/receiver.h" -#include "third_party/blink/public/mojom/page/widget.mojom-blink.h" -#include "third_party/blink/renderer/platform/platform_export.h" -#include "third_party/blink/renderer/platform/widget/compositing/widget_swap_queue.h" - -namespace cc { -class LayerTreeHost; -} - -namespace blink { - -class WidgetBase; - -class PLATFORM_EXPORT WidgetCompositor - : public base::RefCountedThreadSafe<WidgetCompositor>, - public mojom::blink::WidgetCompositor { - public: - WidgetCompositor( - base::WeakPtr<WidgetBase> widget_base, - scoped_refptr<base::SingleThreadTaskRunner> main_task_runner, - scoped_refptr<base::SingleThreadTaskRunner> compositor_task_runner, - mojo::PendingReceiver<mojom::blink::WidgetCompositor> receiver); - ~WidgetCompositor() override = default; - WidgetCompositor(const WidgetCompositor&) = delete; - WidgetCompositor& operator=(const WidgetCompositor&) = delete; - - void Shutdown(); - - // mojom::WidgetCompositor: - void VisualStateRequest(VisualStateRequestCallback callback) override; - - virtual cc::LayerTreeHost* LayerTreeHost() const; - - private: - void BindOnThread( - mojo::PendingReceiver<mojom::blink::WidgetCompositor> receiver); - void ResetOnThread(); - void CreateQueueSwapPromise(base::OnceCallback<void(int)> drain_callback, - base::OnceClosure swap_callback, - VisualStateRequestCallback callback); - void VisualStateResponse(); - void DrainQueue(int source_frame_number); - bool CalledOnValidCompositorThread(); - - // Note that |widget_base_| is safe to be accessed on the main thread. - base::WeakPtr<WidgetBase> widget_base_; - scoped_refptr<base::SingleThreadTaskRunner> main_task_runner_; - scoped_refptr<base::SingleThreadTaskRunner> compositor_task_runner_; - mojo::Receiver<mojom::blink::WidgetCompositor> receiver_{this}; - std::unique_ptr<WidgetSwapQueue> swap_queue_; -}; - -} // namespace blink - -#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_WIDGET_COMPOSITING_WIDGET_COMPOSITOR_H_
diff --git a/third_party/blink/renderer/platform/widget/compositing/widget_compositor_unittest.cc b/third_party/blink/renderer/platform/widget/compositing/widget_compositor_unittest.cc deleted file mode 100644 index 0658962..0000000 --- a/third_party/blink/renderer/platform/widget/compositing/widget_compositor_unittest.cc +++ /dev/null
@@ -1,103 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "third_party/blink/renderer/platform/widget/compositing/widget_compositor.h" - -#include "base/test/task_environment.h" -#include "cc/test/layer_tree_test.h" -#include "cc/trees/layer_tree_host.h" -#include "mojo/public/cpp/bindings/remote.h" -#include "third_party/blink/renderer/platform/widget/widget_base.h" -#include "third_party/blink/renderer/platform/widget/widget_base_client.h" - -namespace blink { - -class StubWidgetBaseClient : public WidgetBaseClient { - public: - void BeginMainFrame(base::TimeTicks) override {} - void RecordTimeToFirstActivePaint(base::TimeDelta) override {} - void UpdateLifecycle(WebLifecycleUpdate, DocumentUpdateReason) override {} - void RequestNewLayerTreeFrameSink(LayerTreeFrameSinkCallback) override {} - WebInputEventResult DispatchBufferedTouchEvents() override { - return WebInputEventResult::kNotHandled; - } - WebInputEventResult HandleInputEvent(const WebCoalescedInputEvent&) override { - return WebInputEventResult::kNotHandled; - } - bool SupportsBufferedTouchEvents() override { return false; } - bool WillHandleGestureEvent(const WebGestureEvent&) override { return false; } - bool WillHandleMouseEvent(const WebMouseEvent&) override { return false; } - void ObserveGestureEventAndResult(const WebGestureEvent&, - const gfx::Vector2dF&, - const cc::OverscrollBehavior&, - bool) override {} - void FocusChanged(bool) override {} - void UpdateVisualProperties( - const VisualProperties& visual_properties) override {} - void UpdateScreenRects(const gfx::Rect& widget_screen_rect, - const gfx::Rect& window_screen_rect) override {} -}; - -class FakeWidgetCompositor : public WidgetCompositor { - public: - FakeWidgetCompositor( - cc::LayerTreeHost* layer_tree_host, - base::WeakPtr<WidgetBase> widget_base, - scoped_refptr<base::SingleThreadTaskRunner> main_task_runner, - scoped_refptr<base::SingleThreadTaskRunner> compositor_task_runner, - mojo::PendingReceiver<mojom::blink::WidgetCompositor> receiver) - : WidgetCompositor(widget_base, - std::move(main_task_runner), - std::move(compositor_task_runner), - std::move(receiver)), - layer_tree_host_(layer_tree_host) {} - - cc::LayerTreeHost* LayerTreeHost() const override { return layer_tree_host_; } - - cc::LayerTreeHost* layer_tree_host_; -}; - -class WidgetCompositorTest : public cc::LayerTreeTest { - public: - using CompositorMode = cc::CompositorMode; - - void BeginTest() override { - widget_base_ = std::make_unique<WidgetBase>( - &client_, - blink::CrossVariantMojoAssociatedRemote< - blink::mojom::WidgetHostInterfaceBase>(), - blink::CrossVariantMojoAssociatedReceiver< - blink::mojom::WidgetInterfaceBase>()); - - widget_compositor_ = base::MakeRefCounted<FakeWidgetCompositor>( - layer_tree_host(), widget_base_->GetWeakPtr(), - layer_tree_host()->GetTaskRunnerProvider()->MainThreadTaskRunner(), - layer_tree_host()->GetTaskRunnerProvider()->ImplThreadTaskRunner(), - remote_.BindNewPipeAndPassReceiver()); - - remote_->VisualStateRequest(base::BindOnce( - &WidgetCompositorTest::VisualStateResponse, base::Unretained(this))); - PostSetNeedsCommitToMainThread(); - } - - void VisualStateResponse() { - is_callback_run_ = true; - widget_compositor_->Shutdown(); - widget_compositor_ = nullptr; - EndTest(); - } - - void AfterTest() override { EXPECT_TRUE(is_callback_run_); } - - mojo::Remote<mojom::blink::WidgetCompositor> remote_; - StubWidgetBaseClient client_; - std::unique_ptr<WidgetBase> widget_base_; - scoped_refptr<FakeWidgetCompositor> widget_compositor_; - bool is_callback_run_ = false; - base::test::SingleThreadTaskEnvironment task_environment_; -}; - -SINGLE_AND_MULTI_THREAD_TEST_F(WidgetCompositorTest); - -} // namespace blink
diff --git a/third_party/blink/renderer/platform/widget/compositing/widget_swap_queue.cc b/third_party/blink/renderer/platform/widget/compositing/widget_swap_queue.cc deleted file mode 100644 index bcb37940..0000000 --- a/third_party/blink/renderer/platform/widget/compositing/widget_swap_queue.cc +++ /dev/null
@@ -1,37 +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. - -#include "third_party/blink/renderer/platform/widget/compositing/widget_swap_queue.h" - -namespace blink { - -void WidgetSwapQueue::Queue(int source_frame_number, - VisualStateRequestCallback callback, - bool* is_first) { - base::AutoLock lock(lock_); - if (is_first) - *is_first = (queue_.count(source_frame_number) == 0); - - queue_[source_frame_number].push_back(std::move(callback)); -} - -void WidgetSwapQueue::Drain(int source_frame_number) { - base::AutoLock lock(lock_); - auto end = queue_.upper_bound(source_frame_number); - for (auto i = queue_.begin(); i != end; i++) { - DCHECK(i->first <= source_frame_number); - std::move(i->second.begin(), i->second.end(), - std::back_inserter(next_callbacks_)); - } - queue_.erase(queue_.begin(), end); -} - -void WidgetSwapQueue::GetCallbacks( - Vector<VisualStateRequestCallback>* callbacks) { - std::move(next_callbacks_.begin(), next_callbacks_.end(), - std::back_inserter(*callbacks)); - next_callbacks_.clear(); -} - -} // namespace blink
diff --git a/third_party/blink/renderer/platform/widget/compositing/widget_swap_queue.h b/third_party/blink/renderer/platform/widget/compositing/widget_swap_queue.h deleted file mode 100644 index 8f73484a..0000000 --- a/third_party/blink/renderer/platform/widget/compositing/widget_swap_queue.h +++ /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. - -#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_WIDGET_COMPOSITING_WIDGET_SWAP_QUEUE_H_ -#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_WIDGET_COMPOSITING_WIDGET_SWAP_QUEUE_H_ - -#include <map> -#include "base/synchronization/lock.h" -#include "third_party/blink/public/mojom/page/widget.mojom-blink.h" -#include "third_party/blink/renderer/platform/wtf/vector.h" - -namespace blink { - -// Queue used to keep track of which VisualStateRequestCallback should be -// invoked after a particular compositor frame swap. The callbacks are -// guaranteed to be processed after the frame is processed, but there is no -// guarantee that nothing else happens between processing the frame and -// processing the callback. -class WidgetSwapQueue { - public: - using VisualStateRequestCallback = - mojom::blink::WidgetCompositor::VisualStateRequestCallback; - WidgetSwapQueue() = default; - ~WidgetSwapQueue() = default; - WidgetSwapQueue(const WidgetSwapQueue&) = delete; - WidgetSwapQueue& operator=(const WidgetSwapQueue&) = delete; - - // Returns true if there are no callback in the queue. - bool Empty() const { return queue_.empty(); } - - // Queues the callback to be returned on a matching Drain call. - // - // |source_frame_number| - frame number to queue |callback| for. - // |callback| - callback to queue. The method takes ownership of |callback|. - // |is_first| - output parameter. Set to true if this was the first message - // enqueued for the given source_frame_number. - void Queue(int source_frame_number, - VisualStateRequestCallback callback, - bool* is_first); - - // The method will append cllbacks queued for frame numbers lower or equal to - // |source_frame_number| - // - // |source_frame_number| - swapped frame number. - void Drain(int source_frame_number); - - // The method will clear |next_callbacks_| after copying to |callbacks|. - // - // |callbacks| - vector to store callbacks. - void GetCallbacks(Vector<VisualStateRequestCallback>* callbacks); - - private: - base::Lock lock_; - std::map<int, Vector<VisualStateRequestCallback>> queue_; - Vector<VisualStateRequestCallback> next_callbacks_; -}; - -} // namespace blink - -#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_WIDGET_COMPOSITING_WIDGET_SWAP_QUEUE_H_
diff --git a/third_party/blink/renderer/platform/widget/widget_base.cc b/third_party/blink/renderer/platform/widget/widget_base.cc index 7ac1a8f..6ebed026 100644 --- a/third_party/blink/renderer/platform/widget/widget_base.cc +++ b/third_party/blink/renderer/platform/widget/widget_base.cc
@@ -24,7 +24,6 @@ #include "third_party/blink/renderer/platform/scheduler/public/thread.h" #include "third_party/blink/renderer/platform/widget/compositing/layer_tree_settings.h" #include "third_party/blink/renderer/platform/widget/compositing/layer_tree_view.h" -#include "third_party/blink/renderer/platform/widget/compositing/widget_compositor.h" #include "third_party/blink/renderer/platform/widget/frame_widget.h" #include "third_party/blink/renderer/platform/widget/input/ime_event_guard.h" #include "third_party/blink/renderer/platform/widget/input/main_thread_event_queue.h" @@ -190,11 +189,6 @@ FROM_HERE, base::BindOnce([](scoped_refptr<WidgetInputHandlerManager> manager) {}, std::move(widget_input_handler_manager_))); - - if (widget_compositor_) { - widget_compositor_->Shutdown(); - widget_compositor_ = nullptr; - } } cc::LayerTreeHost* WidgetBase::LayerTreeHost() const { @@ -669,18 +663,6 @@ client_->FocusChanged(enable); } -void WidgetBase::BindWidgetCompositor( - mojo::PendingReceiver<mojom::blink::WidgetCompositor> receiver) { - if (widget_compositor_) - widget_compositor_->Shutdown(); - - widget_compositor_ = base::MakeRefCounted<WidgetCompositor>( - weak_ptr_factory_.GetWeakPtr(), - LayerTreeHost()->GetTaskRunnerProvider()->MainThreadTaskRunner(), - LayerTreeHost()->GetTaskRunnerProvider()->ImplThreadTaskRunner(), - std::move(receiver)); -} - void WidgetBase::UpdateCompositionInfo(bool immediate_request) { if (!monitor_composition_info_ && !immediate_request) return; // Do not calculate composition info if not requested.
diff --git a/third_party/blink/renderer/platform/widget/widget_base.h b/third_party/blink/renderer/platform/widget/widget_base.h index f2820ec..2d9e2e8 100644 --- a/third_party/blink/renderer/platform/widget/widget_base.h +++ b/third_party/blink/renderer/platform/widget/widget_base.h
@@ -39,7 +39,6 @@ class LayerTreeView; class WidgetBaseClient; class WidgetInputHandlerManager; -class WidgetCompositor; namespace scheduler { class WebRenderWidgetSchedulingState; @@ -216,13 +215,6 @@ CrossVariantMojoRemote< mojom::blink::PointerLockContextInterfaceBase>)> callback); - void BindWidgetCompositor( - mojo::PendingReceiver<mojom::blink::WidgetCompositor> receiver); - - base::WeakPtr<WidgetBase> GetWeakPtr() { - return weak_ptr_factory_.GetWeakPtr(); - } - private: bool CanComposeInline(); void UpdateTextInputStateInternal(bool show_virtual_keyboard, @@ -250,7 +242,6 @@ base::TimeTicks was_shown_time_ = base::TimeTicks::Now(); bool has_focus_ = false; WidgetBaseInputHandler input_handler_{this}; - scoped_refptr<WidgetCompositor> widget_compositor_; // Stores the current selection bounds. gfx::Rect selection_focus_rect_;
diff --git a/third_party/blink/web_tests/TestExpectations b/third_party/blink/web_tests/TestExpectations index 3169184..1b3ffd5 100644 --- a/third_party/blink/web_tests/TestExpectations +++ b/third_party/blink/web_tests/TestExpectations
@@ -1044,6 +1044,7 @@ crbug.com/591099 virtual/layout_ng_block_frag/external/wpt/css/css-multicol/multicol-basic-004.html [ Failure ] crbug.com/591099 virtual/layout_ng_block_frag/external/wpt/css/css-multicol/multicol-basic-007.xht [ Failure ] crbug.com/591099 virtual/layout_ng_block_frag/external/wpt/css/css-multicol/multicol-basic-008.xht [ Failure ] +crbug.com/982194 virtual/layout_ng_block_frag/external/wpt/css/css-multicol/multicol-block-no-clip-001.xht [ Crash ] crbug.com/591099 virtual/layout_ng_block_frag/external/wpt/css/css-multicol/multicol-clip-002.xht [ Failure ] crbug.com/591099 virtual/layout_ng_block_frag/external/wpt/css/css-multicol/multicol-columns-003.xht [ Failure ] crbug.com/591099 virtual/layout_ng_block_frag/external/wpt/css/css-multicol/multicol-columns-004.xht [ Failure ] @@ -1315,10 +1316,13 @@ crbug.com/875235 virtual/layout_ng_fieldset/external/wpt/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/legend-margin-inline.html [ Failure ] ### virtual/layout_ng_fieldset/fast/forms/fieldset/ -crbug.com/875235 virtual/layout_ng_fieldset/fast/forms/fieldset/fieldset-align.html [ Failure ] crbug.com/875235 virtual/layout_ng_fieldset/fast/forms/fieldset/legend-small-after-margin-before-border-horizontal-mode.html [ Failure ] crbug.com/875235 virtual/layout_ng_fieldset/fast/forms/fieldset/legend-after-margin-with-before-border-horizontal-mode.html [ Failure ] +## Fieldseet in NG - Passing reference tests +crbug.com/875235 virtual/layout_ng_fieldset/external/wpt/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/fieldset-vertical.html [ Pass ] + + # ====== LayoutNG-only failures until here ====== # ====== MathMLCore-only tests from here ====== @@ -6436,13 +6440,6 @@ # Sheriff 2020-02-07 crbug.com/1050039 [ Mac ] fast/events/onbeforeunload-focused-iframe.html [ Pass Failure ] -crbug.com/1050039 [ Mac ] virtual/omt-worker-fetch/fast/workers/worker-onerror-01.html [ Pass Failure ] -crbug.com/1050039 [ Mac ] virtual/omt-worker-fetch/fast/workers/worker-onerror-02.html [ Pass Failure ] -crbug.com/1050039 [ Mac ] virtual/omt-worker-fetch/fast/workers/worker-onerror-03.html [ Pass Failure ] -crbug.com/1050039 [ Mac ] virtual/omt-worker-fetch/fast/workers/worker-onerror-04.html [ Pass Failure ] -crbug.com/1050039 [ Mac ] virtual/omt-worker-fetch/fast/workers/worker-onerror-06.html [ Pass Failure ] -crbug.com/1050039 [ Mac ] virtual/omt-worker-fetch/fast/workers/worker-onerror-08.html [ Pass Failure ] -crbug.com/1050039 [ Mac ] virtual/omt-worker-fetch/fast/workers/worker-onerror-09.html [ Pass Failure ] # DevTools roll crbug.com/1006759 http/tests/devtools/elements/styles-1/edit-value-url-with-color.js [ Skip ] @@ -6847,3 +6844,7 @@ crbug.com/1111287 http/tests/devtools/sources/debugger-breakpoints/breakpoint-with-sourcemap-dart.js [ Pass Failure ] crbug.com/1111410 http/tests/serviceworker/service-worker-gc.html [ Pass Failure ] + +# Sheriff 2020-08-01 +crbug.com/1112111 fast/forms/month/month-picker-appearance-step.html [ Pass Failure ] +
diff --git a/third_party/blink/web_tests/external/wpt/workers/interfaces/WorkerGlobalScope/onerror/not-handled.html b/third_party/blink/web_tests/external/wpt/workers/interfaces/WorkerGlobalScope/onerror/not-handled.html index 96fdb4a..11d03d72 100644 --- a/third_party/blink/web_tests/external/wpt/workers/interfaces/WorkerGlobalScope/onerror/not-handled.html +++ b/third_party/blink/web_tests/external/wpt/workers/interfaces/WorkerGlobalScope/onerror/not-handled.html
@@ -4,16 +4,25 @@ <script src="/resources/testharnessreport.js"></script> <div id="log"></div> <script> -async_test(function() { - var worker = new Worker('not-handled.js'); - worker.onerror = this.step_func(function(e) { +function createHandler(t) { + return t.step_func(function(e) { assert_true(e instanceof ErrorEvent, 'e instanceof ErrorEvent'); assert_equals(typeof e.message, 'string', 'typeof e.message'); assert_equals(e.filename, document.URL.replace('.html', '.js'), 'e.filename'); assert_equals(typeof e.lineno, 'number', 'typeof e.lineno'); assert_equals(typeof e.colno, 'number', 'typeof e.column'); e.preventDefault(); // "handled" - this.done(); + t.done(); }); -}); -</script> \ No newline at end of file +} + +async_test(function(t) { + var worker = new Worker('not-handled.js'); + worker.onerror = createHandler(t); +}, 'Not handled evaluation error => Worker.onerror handler'); + +async_test(function(t) { + var worker = new Worker('not-handled.js'); + worker.addEventListener('error', createHandler(t)); +}, 'Not handled evaluation error => Worker error listener'); +</script>
diff --git a/third_party/blink/web_tests/fast/harness/results.html b/third_party/blink/web_tests/fast/harness/results.html index 396db0e..d329739 100644 --- a/third_party/blink/web_tests/fast/harness/results.html +++ b/third_party/blink/web_tests/fast/harness/results.html
@@ -255,8 +255,14 @@ <h3>Querying Results</h3> - <p>Select the results you are interested in using "Query" buttons. - Narrow down the results further with "Filter" checkboxes.</p> + <p>Select the results you are interested in using "Query" buttons.</p> + <p>Narrow down the results further with "Filter" search box and checkboxes. + The following types of search strings are supported:</p> + <ul> + <li>Test name: full or partial,</li> + <li>Bug number: full or partial,</li> + <li>Range of test time duration: 'time:min-' or 'time:min-max' or 'time:-max' in seconds.</li> + </ul> <h3>Displaying results.</h3> @@ -377,7 +383,9 @@ </div> <div id="filters"> <span class="fix-width">Filters</span> - <input id="text-filter" onchange="Query.filterChanged()" type="text" placeholder="test name/bug#, hit enter"> + <input id="text-filter" onsearch="Query.filterChanged()" type="search" + placeholder="test name | bug | time ⏎" + title="Format of time: 'time:min-' or 'time:min-max' or 'time:-max' in seconds"> <label id="CRASH"><input type="checkbox">Crash <span></span></label> <label id="TIMEOUT"><input type="checkbox">Timeout <span></span></label> <label id="TEXT"><input type="checkbox">Text failure <span></span></label> @@ -781,7 +789,10 @@ getResultToolbars: (test) => { let toolbars = []; let pathParser = new PathParser(test.expectPath); - toolbars.push(new PlainHtmlToolbar().createDom(test.actual)); + let actual = test.actual; + if (test.time) + actual = actual.replace(/ |$/, `(${test.time}s)` + "$&"); + toolbars.push(new PlainHtmlToolbar().createDom(actual)); for (let result of test.actualMap.keys()) { switch(result) { case "PASS": @@ -798,8 +809,7 @@ pathParser.resultLink("-crash-log.txt"), "Crash log", "crash log")); break; case "TIMEOUT": - toolbars.push(new PlainHtmlToolbar().createDom("Test timed out. " - + ("time" in test ? `(${test.time}s)` : ""))); + toolbars.push(new PlainHtmlToolbar().createDom("Test timed out. ")); if (test.text_mismatch) toolbars.push(new TextResultsToolbar().createDom(test)); break; @@ -906,7 +916,14 @@ if (el.querySelector('input').checked) filterMap.set(el.id.replace("_", "+"), true); } - let searchText = document.querySelector("#text-filter").value; + let searchText = document.querySelector("#text-filter").value.trim(); + let timeRange = searchText.match(/^time:([ .0-9]*)-([ .0-9]*)$/); + let timeMin = 0, timeMax = Number.MAX_VALUE; + if (timeRange) { + timeMin = parseFloat(timeRange[1]); + if (timeRange[2] != "") + timeMax = parseFloat(timeRange[2]); + } let textFilter = (!searchText || searchText.length < 1) ? _ => true : test => { @@ -918,6 +935,11 @@ return true; } } + if (timeRange) { + let time = test.time || 0; + if (time >= timeMin && time <= timeMax) + return true; + } return false; }; return test => { @@ -1109,6 +1131,9 @@ artifacts.hasOwnProperty('expected_audio'); let is_leak = artifacts.hasOwnProperty('leak_log'); + if (artifacts.hasOwnProperty('reference_file_mismatch')) + test.reference = artifacts.reference_file_mismatch; + if (is_image && is_text) return 'IMAGE+TEXT'; if (is_image) @@ -1800,23 +1825,25 @@ this.toolbar.showDefault = _ => this.viewOptionsChangeHandler({target: this.toolbar.querySelector(".view-options")}); this.toolbar.classList.add("image-toolbar"); this.toolbar.classList.add("tx-toolbar"); - let html = ` - image: - <a class="actual" href="${pathParser.resultLink("-actual.png")}" title="Actual result">actual</a> - <a class="expected" href="${pathParser.resultLink("-expected.png")}" title="Expected result">expected</a> - <a class="diff" href="${pathParser.resultLink("-diff.png")}" title="Difference">diff</a> - `; + + let html = "image: "; + html += `<a class="toggle" href="${pathParser.resultLink("-actual.png")}" title="Actual result">actual</a> `; + html += `<a class="toggle" href="${pathParser.resultLink("-expected.png")}" title="Expected result">expected</a>`; + if (test.reference) + html += `(<a style="padding: 0" target="reference" href="../${test.reference}">ref</a>) `; + html += `<a class="toggle" href="${pathParser.resultLink("-diff.png")}" title="Difference">diff</a>`; html += ` <label><select class="view-options"> <option value="single">Single view</option> <option value="animated">Animated view</option> <option value="multiple">Side by side view</option> - </label> + </select></label> `; + this.toolbar.innerHTML = html; this.toolbar.addEventListener("click", ev => { - if (ev.target.tagName == "A") { + if (ev.target.tagName == "A" && ev.target.className == "toggle") { this.selectAnchor(ev.target); ev.preventDefault(); if (this.animationIntervalId) { @@ -1862,7 +1889,7 @@ break; case "multiple": { this.setAnimation(false); - for (let anchor of Array.from(this.toolbar.querySelectorAll("a"))) + for (let anchor of Array.from(this.toolbar.querySelectorAll("a.toggle"))) this.selectAnchor(anchor, true); } break; @@ -1906,7 +1933,7 @@ if (this.animationPaused) return; // Find next anchor. - let allAnchors = Array.from(this.toolbar.querySelectorAll("a")); + let allAnchors = Array.from(this.toolbar.querySelectorAll("a.toggle")); let nextAnchor = allAnchors[0]; for (let i = 0; i < allAnchors.length; i++) { if (allAnchors[i].hasAttribute("data-selected")) { @@ -2537,6 +2564,16 @@ } } // class AudioResultsViewer +(function setTextFilterWidth() { + let textFilter = document.querySelector("#text-filter"); + let div = document.createElement('div'); + div.style.position = 'absolute'; + div.style.top = '-200px'; + div.style.fontSize = getComputedStyle(textFilter).fontSize; + div.textContent = textFilter.getAttribute('placeholder'); + document.body.appendChild(div); + textFilter.style.width = div.offsetWidth + 30 + 'px'; +})(); </script> <script> // jsonp callback
diff --git a/third_party/blink/web_tests/fast/workers/worker-onerror-02-expected.txt b/third_party/blink/web_tests/fast/workers/worker-onerror-02-expected.txt deleted file mode 100644 index 2f37013..0000000 --- a/third_party/blink/web_tests/fast/workers/worker-onerror-02-expected.txt +++ /dev/null
@@ -1,15 +0,0 @@ -This tests that unhandled exceptions trigger 'worker.onerror'. - -On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE". - - -Page-level worker.onerror handler triggered: -PASS errorEvent.message is "Uncaught ReferenceError: foo is not defined" -PASS stripURL(errorEvent.filename) is "[blob: URL]" -PASS errorEvent.lineno is 2 -PASS errorEvent.colno is 9 -PASS errorEvent.error is null -PASS successfullyParsed is true - -TEST COMPLETE -
diff --git a/third_party/blink/web_tests/fast/workers/worker-onerror-02.html b/third_party/blink/web_tests/fast/workers/worker-onerror-02.html deleted file mode 100644 index 94cd726a..0000000 --- a/third_party/blink/web_tests/fast/workers/worker-onerror-02.html +++ /dev/null
@@ -1,26 +0,0 @@ -<!DOCTYPE html> -<html> -<head> - <script> - window.isOnErrorTest = true; - </script> - <script src="../../resources/js-test.js"></script> - <script src="resources/onerror-test.js"></script> -</head> -<body> - <!-- This script's body will be used to build a Blob URL to use as a Worker. --> - <script id="workerCode" type="text/plain"> - foo.bar = 0; - </script> - <script> - description("This tests that unhandled exceptions trigger 'worker.onerror'."); - - checkErrorEventInHandler({ - message: "Uncaught ReferenceError: foo is not defined", - filename: "[blob: URL]", - lineno: 2, - colno: 9, - }); - </script> -</body> -</html>
diff --git a/third_party/blink/web_tests/fast/workers/worker-onerror-03-expected.txt b/third_party/blink/web_tests/fast/workers/worker-onerror-03-expected.txt deleted file mode 100644 index 0250041..0000000 --- a/third_party/blink/web_tests/fast/workers/worker-onerror-03-expected.txt +++ /dev/null
@@ -1,15 +0,0 @@ -This tests that unhandled exceptions trigger the 'error' event on worker. - -On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE". - - -Page-level worker 'error' event listener triggered: -PASS errorEvent.message is "Uncaught ReferenceError: foo is not defined" -PASS stripURL(errorEvent.filename) is "[blob: URL]" -PASS errorEvent.lineno is 2 -PASS errorEvent.colno is 9 -PASS errorEvent.error is null -PASS successfullyParsed is true - -TEST COMPLETE -
diff --git a/third_party/blink/web_tests/fast/workers/worker-onerror-03.html b/third_party/blink/web_tests/fast/workers/worker-onerror-03.html deleted file mode 100644 index 9ed0437..0000000 --- a/third_party/blink/web_tests/fast/workers/worker-onerror-03.html +++ /dev/null
@@ -1,26 +0,0 @@ -<!DOCTYPE html> -<html> -<head> - <script> - window.isOnErrorTest = true; - </script> - <script src="../../resources/js-test.js"></script> - <script src="resources/onerror-test.js"></script> -</head> -<body> - <!-- This script's body will be used to build a Blob URL to use as a Worker. --> - <script id="workerCode" type="text/plain"> - foo.bar = 0; - </script> - <script> - description("This tests that unhandled exceptions trigger the 'error' event on worker."); - - checkErrorEventInListener({ - message: "Uncaught ReferenceError: foo is not defined", - filename: "[blob: URL]", - lineno: 2, - colno: 9, - }); - </script> -</body> -</html>
diff --git a/third_party/blink/web_tests/fast/workers/worker-onerror-05-expected.txt b/third_party/blink/web_tests/fast/workers/worker-onerror-05-expected.txt deleted file mode 100644 index fe92a82c..0000000 --- a/third_party/blink/web_tests/fast/workers/worker-onerror-05-expected.txt +++ /dev/null
@@ -1,21 +0,0 @@ -This tests that exceptions trigger a worker's onerror in addition to the page-level 'worker.onerror'. - -On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE". - - -Worker-level onerror handler triggered: -PASS errorEvent.message is "Uncaught ReferenceError: foo is not defined" -PASS stripURL(errorEvent.filename) is "[blob: URL]" -PASS errorEvent.lineno is 12 -PASS errorEvent.colno is 9 - -Page-level worker.onerror handler triggered: -PASS errorEvent.message is "Uncaught ReferenceError: foo is not defined" -PASS stripURL(errorEvent.filename) is "[blob: URL]" -PASS errorEvent.lineno is 12 -PASS errorEvent.colno is 9 -PASS errorEvent.error is null -PASS successfullyParsed is true - -TEST COMPLETE -
diff --git a/third_party/blink/web_tests/fast/workers/worker-onerror-05.html b/third_party/blink/web_tests/fast/workers/worker-onerror-05.html deleted file mode 100644 index 8c6c5cc..0000000 --- a/third_party/blink/web_tests/fast/workers/worker-onerror-05.html +++ /dev/null
@@ -1,43 +0,0 @@ -<!DOCTYPE html> -<html> -<head> - <script> - window.isOnErrorTest = true; - </script> - <script src="../../resources/js-test.js"></script> - <script src="resources/onerror-test.js"></script> -</head> -<body> - <!-- This script's body will be used to build a Blob URL to use as a Worker. --> - <script id="workerCode" type="text/plain"> - onerror = function(message, filename, lineno, colno, error) { - postMessage({ - message: message, - filename: filename, - lineno: lineno, - colno: colno - }); - return false; - } - - foo.bar = 0; - </script> - <script> - description("This tests that exceptions trigger a worker's onerror in addition to the page-level 'worker.onerror'."); - - checkPostMessage({ - message: "Uncaught ReferenceError: foo is not defined", - filename: "[blob: URL]", - lineno: 12, - colno: 9, - }); - - checkErrorEventInHandler({ - message: "Uncaught ReferenceError: foo is not defined", - filename: "[blob: URL]", - lineno: 12, - colno: 9, - }); - </script> -</body> -</html>
diff --git a/third_party/blink/web_tests/fast/workers/worker-onerror-06-expected.txt b/third_party/blink/web_tests/fast/workers/worker-onerror-06-expected.txt deleted file mode 100644 index 2ba3bbe..0000000 --- a/third_party/blink/web_tests/fast/workers/worker-onerror-06-expected.txt +++ /dev/null
@@ -1,15 +0,0 @@ -This tests that returning true from a page-level handler suppresses the exception. - -On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE". - - -Page-level worker.onerror handler triggered: -PASS errorEvent.message is "Uncaught ReferenceError: foo is not defined" -PASS stripURL(errorEvent.filename) is "[blob: URL]" -PASS errorEvent.lineno is 13 -PASS errorEvent.colno is 9 -PASS errorEvent.error is null -PASS successfullyParsed is true - -TEST COMPLETE -
diff --git a/third_party/blink/web_tests/fast/workers/worker-onerror-06.html b/third_party/blink/web_tests/fast/workers/worker-onerror-06.html deleted file mode 100644 index 4e058d9c..0000000 --- a/third_party/blink/web_tests/fast/workers/worker-onerror-06.html +++ /dev/null
@@ -1,38 +0,0 @@ -<!DOCTYPE html> -<html> -<head> - <script> - window.isOnErrorTest = true; - </script> - <script src="../../resources/js-test.js"></script> - <script src="resources/onerror-test.js"></script> -</head> -<body> - <!-- This script's body will be used to build a Blob URL to use as a Worker. --> - <script id="workerCode" type="text/plain"> - onerror = function(message, filename, lineno, colno, error) { - postMessage({ - message: message, - filename: filename, - lineno: lineno, - colno: colno, - error: error - }); - return false; - } - - foo.bar = 0; - </script> - <script> - description("This tests that returning true from a page-level handler suppresses the exception."); - - checkErrorEventInHandler({ - message: "Uncaught ReferenceError: foo is not defined", - filename: "[blob: URL]", - lineno: 13, - colno: 9, - }, true); - </script> -</body> -</html> -
diff --git a/third_party/blink/web_tests/fast/workers/worker-onerror-07-expected.txt b/third_party/blink/web_tests/fast/workers/worker-onerror-07-expected.txt deleted file mode 100644 index 17f1f2e..0000000 --- a/third_party/blink/web_tests/fast/workers/worker-onerror-07-expected.txt +++ /dev/null
@@ -1,14 +0,0 @@ -Handling an error in the worker should suppress the page-level onerror handler. - -On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE". - - -Worker-level onerror handler triggered: -PASS errorEvent.message is "Uncaught ReferenceError: foo is not defined" -PASS stripURL(errorEvent.filename) is "[blob: URL]" -PASS errorEvent.lineno is 13 -PASS errorEvent.colno is 9 -PASS successfullyParsed is true - -TEST COMPLETE -
diff --git a/third_party/blink/web_tests/fast/workers/worker-onerror-07.html b/third_party/blink/web_tests/fast/workers/worker-onerror-07.html deleted file mode 100644 index 95440ca..0000000 --- a/third_party/blink/web_tests/fast/workers/worker-onerror-07.html +++ /dev/null
@@ -1,39 +0,0 @@ -<!DOCTYPE html> -<html> -<head> - <script> - window.isOnErrorTest = true; - </script> - <script src="../../resources/js-test.js"></script> - <script src="resources/onerror-test.js"></script> -</head> -<body> - <!-- This script's body will be used to build a Blob URL to use as a Worker. --> - <script id="workerCode" type="text/plain"> - onerror = function(message, filename, lineno, colno, error) { - postMessage({ - message: message, - filename: filename, - lineno: lineno, - colno: colno - }); - setTimeout(function () { postMessage({ done: true }); }, 0); - return true; - } - - foo.bar = 0; - </script> - <script> - description("Handling an error in the worker should suppress the page-level onerror handler."); - - checkPostMessage({ - message: "Uncaught ReferenceError: foo is not defined", - filename: "[blob: URL]", - lineno: 13, - colno: 9, - }); - - errorEventHandlerShouldNotFire(); - </script> -</body> -</html>
diff --git a/tools/clang/scripts/package.py b/tools/clang/scripts/package.py index 4e4574a6..7b75a85 100755 --- a/tools/clang/scripts/package.py +++ b/tools/clang/scripts/package.py
@@ -228,9 +228,10 @@ 'lib/clang/$V/lib/darwin/libclang_rt.asan_iossim_dynamic.dylib', 'lib/clang/$V/lib/darwin/libclang_rt.asan_osx_dynamic.dylib', - # OS X and iOS builtin libraries (iossim is lipo'd into ios) for the - # _IsOSVersionAtLeast runtime function. + # OS X and iOS builtin libraries for the _IsOSVersionAtLeast runtime + # function. 'lib/clang/$V/lib/darwin/libclang_rt.ios.a', + 'lib/clang/$V/lib/darwin/libclang_rt.iossim.a', 'lib/clang/$V/lib/darwin/libclang_rt.osx.a', # Profile runtime (used by profiler and code coverage).
diff --git a/tools/clang/scripts/update.py b/tools/clang/scripts/update.py index a6d914b0..23cab4d 100755 --- a/tools/clang/scripts/update.py +++ b/tools/clang/scripts/update.py
@@ -37,9 +37,9 @@ # Do NOT CHANGE this if you don't know what you're doing -- see # https://chromium.googlesource.com/chromium/src/+/master/docs/updating_clang.md # Reverting problematic clang rolls is safe, though. -CLANG_REVISION = '7e8d5a90f2c101388d3b0bbce8555e871c670232' -CLANG_SVN_REVISION = 'n361601' -CLANG_SUB_REVISION = 1 +CLANG_REVISION = '1bd7046e4ce0102adef6096a12a289d7f94b8c73' +CLANG_SVN_REVISION = 'n362116' +CLANG_SUB_REVISION = 2 PACKAGE_VERSION = '%s-%s-%s' % (CLANG_SVN_REVISION, CLANG_REVISION[:8], CLANG_SUB_REVISION)
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml index 30a5dcf..08568fa 100644 --- a/tools/metrics/histograms/enums.xml +++ b/tools/metrics/histograms/enums.xml
@@ -21340,6 +21340,7 @@ <int value="757" label="DeviceChannelDowngradeBehavior"/> <int value="758" label="BackForwardCacheEnabled"/> <int value="759" label="NoteTakingAppsLockScreenAllowlist"/> + <int value="760" label="CCTToSDialogEnabled"/> </enum> <enum name="EnterprisePolicyDeviceIdValidity"> @@ -40249,6 +40250,7 @@ <int value="-1670137340" label="OptimizeLoadingIPCForSmallResources:disabled"/> <int value="-1669486359" label="ImportantSitesInCBD:enabled"/> + <int value="-1668306615" label="CrostiniUseDlc:enabled"/> <int value="-1665720309" label="ArcNativeBridge64BitSupportExperiment:disabled"/> <int value="-1664795930" label="StorageAccessAPI:disabled"/> @@ -43577,6 +43579,7 @@ <int value="1919917329" label="ImplicitRootScroller:disabled"/> <int value="1920894670" label="OmniboxPreserveDefaultMatchAgainstAsyncUpdate:enabled"/> + <int value="1923052799" label="CrostiniUseDlc:disabled"/> <int value="1924192543" label="ProactiveTabFreezeAndDiscard:enabled"/> <int value="1925627218" label="FullscreenToolbarReveal:disabled"/> <int value="1926524951" label="SystemWebApps:disabled"/> @@ -72133,6 +72136,12 @@ <int value="30998784" label="Generic Error"/> </enum> +<enum name="VirtualKeyboardHandwritingCommitType"> + <int value="0" label="Commit text only"/> + <int value="1" label="Commit text with newline"/> + <int value="2" label="Commit text with space"/> +</enum> + <enum name="VisibleTab"> <int value="0" label="Custom Tab"/> <int value="1" label="Chrome Tab"/>
diff --git a/tools/metrics/histograms/histograms.xml b/tools/metrics/histograms/histograms.xml index 365115d..bf6803b 100644 --- a/tools/metrics/histograms/histograms.xml +++ b/tools/metrics/histograms/histograms.xml
@@ -70953,6 +70953,37 @@ <summary>Text input events related to gesture typing.</summary> </histogram> +<histogram name="InputMethod.VirtualKeyboard.Handwriting.CommitType" + enum="VirtualKeyboardHandwritingCommitType" expires_after="2021-07-01"> + <owner>curtismcmullan@chromium.org</owner> + <owner>essential-inputs-team@google.com</owner> + <summary> + What kind of commit method was used when text was committed to an input from + the handwriting input method in the virtual keyboard. + </summary> +</histogram> + +<histogram + name="InputMethod.VirtualKeyboard.Handwriting.KeyboardContainerOnCommit" + enum="VirtualKeyboardContainerType" expires_after="2021-07-01"> + <owner>curtismcmullan@chromium.org</owner> + <owner>essential-inputs-team@google.com</owner> + <summary> + What type of container was the virtual keyboard in when text was committed + from the handwriting input method. + </summary> +</histogram> + +<histogram name="InputMethod.VirtualKeyboard.Handwriting.KeyboardWidthOnCommit" + units="px" expires_after="2021-07-01"> + <owner>curtismcmullan@chromium.org</owner> + <owner>essential-inputs-team@google.com</owner> + <summary> + How wide was the keyboard container when text was committed from the + handwriting input method. + </summary> +</histogram> + <histogram name="InputMethod.VirtualKeyboard.InitLatency" units="ms" expires_after="M95"> <owner>shend@chromium.org</owner> @@ -204986,8 +205017,14 @@ <histogram_suffixes name="HardwareVerifierSupportCategory" separator="."> <suffix name="audio_codec" label=""/> <suffix name="battery" label=""/> + <suffix name="camera" label=""/> + <suffix name="display_panel" label=""/> + <suffix name="dram" label=""/> <suffix name="network" label=""/> <suffix name="storage" label=""/> + <suffix name="stylus" label=""/> + <suffix name="touchpad" label=""/> + <suffix name="touchscreen" label=""/> <suffix name="vpd_cached" label=""/> <affected-histogram name="ChromeOS.HardwareVerifier.Report"/> </histogram_suffixes>
diff --git a/tools/style_variable_generator/base_generator.py b/tools/style_variable_generator/base_generator.py index 7b97f4f..0f6f6948 100644 --- a/tools/style_variable_generator/base_generator.py +++ b/tools/style_variable_generator/base_generator.py
@@ -126,26 +126,24 @@ def AddJSONFileToModel(self, path): try: with open(path, 'r') as f: - return self.AddJSONToModel(f.read(), self.GetName(), path) + return self.AddJSONToModel(f.read(), path) except ValueError as err: raise ValueError('\n%s:\n %s' % (path, err)) - def AddJSONToModel(self, json_string, generator_name=None, in_file=None): + def AddJSONToModel(self, json_string, in_file=None): '''Adds a |json_string| with variable definitions to the model. See *test.json5 files for a defacto format reference. - |generator_name| is used to get the generator-specific context from the - 'options' object. |in_file| is used to populate a file-to-context map. ''' # TODO(calamity): Add allow_duplicate_keys=False once pyjson5 is # rolled. data = json5.loads(json_string, object_pairs_hook=collections.OrderedDict) - if generator_name is None: - generator_name = self.GetName() - generator_context = data.get('options', {}).get(generator_name, None) + # Use the generator's name to get the generator-specific context from + # the input. + generator_context = data.get('options', {}).get(self.GetName(), None) self.in_file_to_context[in_file] = generator_context for name, value in data['colors'].items():
diff --git a/ui/accessibility/platform/ax_platform_node.cc b/ui/accessibility/platform/ax_platform_node.cc index 6bf1a7a..4f8d630 100644 --- a/ui/accessibility/platform/ax_platform_node.cc +++ b/ui/accessibility/platform/ax_platform_node.cc
@@ -62,6 +62,14 @@ return GetDelegate() ? GetDelegate()->GetUniqueId().Get() : -1; } +void AXPlatformNode::SetIsPrimaryWebContentsForWindow(bool is_primary) { + is_primary_web_contents_for_window_ = is_primary; +} + +bool AXPlatformNode::IsPrimaryWebContentsForWindow() const { + return is_primary_web_contents_for_window_; +} + std::string AXPlatformNode::ToString() { return GetDelegate() ? GetDelegate()->ToString() : "No delegate"; }
diff --git a/ui/accessibility/platform/ax_platform_node.h b/ui/accessibility/platform/ax_platform_node.h index 24b33ca8..e8ad7ed 100644 --- a/ui/accessibility/platform/ax_platform_node.h +++ b/ui/accessibility/platform/ax_platform_node.h
@@ -92,6 +92,10 @@ // Return true if this object is equal to or a descendant of |ancestor|. virtual bool IsDescendantOf(AXPlatformNode* ancestor) const = 0; + // Set |this| as the primary web contents for the window. + void SetIsPrimaryWebContentsForWindow(bool is_primary); + bool IsPrimaryWebContentsForWindow() const; + // Return the unique ID. int32_t GetUniqueId() const; @@ -125,6 +129,8 @@ // underlying content. static gfx::NativeViewAccessible popup_focus_override_; + bool is_primary_web_contents_for_window_ = false; + DISALLOW_COPY_AND_ASSIGN(AXPlatformNode); };
diff --git a/ui/accessibility/platform/ax_platform_node_auralinux.cc b/ui/accessibility/platform/ax_platform_node_auralinux.cc index 2fe51ea5..af7f130 100644 --- a/ui/accessibility/platform/ax_platform_node_auralinux.cc +++ b/ui/accessibility/platform/ax_platform_node_auralinux.cc
@@ -2497,7 +2497,10 @@ frame->SetDocumentParent(parent_atk_object); } -AtkObject* AXPlatformNodeAuraLinux::FindFirstWebContentDocument() { +AtkObject* AXPlatformNodeAuraLinux::FindPrimaryWebContentDocument() { + // It could get multiple web contents since additional web content is added, + // when the DevTools window is opened. + std::vector<AtkObject*> web_content_candidates; for (auto child_iterator_ptr = GetDelegate()->ChildrenBegin(); *child_iterator_ptr != *GetDelegate()->ChildrenEnd(); ++(*child_iterator_ptr)) { @@ -2509,12 +2512,36 @@ continue; if (child_node->GetAtkRole() != ATK_ROLE_DOCUMENT_WEB) continue; - return child; + web_content_candidates.push_back(child); } + if (web_content_candidates.empty()) + return nullptr; + + // If it finds just one web content, return it. + if (web_content_candidates.size() == 1) + return web_content_candidates[0]; + + for (auto* object : web_content_candidates) { + auto* child_node = AXPlatformNodeAuraLinux::FromAtkObject(object); + // If it is a primary web contents, return it. + if (child_node->IsPrimaryWebContentsForWindow()) + return object; + } return nullptr; } +bool AXPlatformNodeAuraLinux::IsWebDocumentForRelations() { + AtkObject* atk_object = GetOrCreateAtkObject(); + if (!atk_object) + return false; + AXPlatformNodeAuraLinux* parent = FromAtkObject(GetParent()); + if (!parent || !GetDelegate()->IsWebContent() || + GetAtkRole() != ATK_ROLE_DOCUMENT_WEB) + return false; + return parent->FindPrimaryWebContentDocument() == atk_object; +} + AtkObject* AXPlatformNodeAuraLinux::CreateAtkObject() { if (GetData().role != ax::mojom::Role::kApplication && !GetDelegate()->IsToplevelBrowserWindow() && @@ -3183,7 +3210,7 @@ AtkRelationSet* relation_set = atk_relation_set_new(); - if (GetDelegate()->IsWebContent() && GetAtkRole() == ATK_ROLE_DOCUMENT_WEB) { + if (IsWebDocumentForRelations()) { AtkObject* parent_frame = FindAtkObjectParentFrame(atk_object); if (parent_frame) { atk_relation_set_add_relation_by_type( @@ -3192,7 +3219,7 @@ } if (auto* document_parent = FromAtkObject(document_parent_)) { - AtkObject* document = document_parent->FindFirstWebContentDocument(); + AtkObject* document = document_parent->FindPrimaryWebContentDocument(); if (document) { atk_relation_set_add_relation_by_type(relation_set, ATK_RELATION_EMBEDS, document);
diff --git a/ui/accessibility/platform/ax_platform_node_auralinux.h b/ui/accessibility/platform/ax_platform_node_auralinux.h index f4cfa87..6c63a193 100644 --- a/ui/accessibility/platform/ax_platform_node_auralinux.h +++ b/ui/accessibility/platform/ax_platform_node_auralinux.h
@@ -340,8 +340,11 @@ // the toplevel frame which contains the node. void SetDocumentParentOnFrameIfNecessary(); - // Find the first child which is a document containing web content. - AtkObject* FindFirstWebContentDocument(); + // Find the child which is a document containing the primary web content. + AtkObject* FindPrimaryWebContentDocument(); + + // Returns true if it is a web content for the relations. + bool IsWebDocumentForRelations(); // If a selection that intersects this node get the full selection // including start and end node ids.
diff --git a/ui/file_manager/file_manager/foreground/js/file_tasks.js b/ui/file_manager/file_manager/foreground/js/file_tasks.js index 05295be..49a47b0 100644 --- a/ui/file_manager/file_manager/foreground/js/file_tasks.js +++ b/ui/file_manager/file_manager/foreground/js/file_tasks.js
@@ -934,12 +934,15 @@ } /** - * Setup a task picker combobutton based on the given tasks. + * Setup a task picker combobutton based on the given tasks. The combobutton + * is not shown if there are no tasks, or if any entry is a directory. + * * @param {!cr.ui.ComboButton} combobutton * @param {!Array<!chrome.fileManagerPrivate.FileTask>} tasks */ updateOpenComboButton_(combobutton, tasks) { - combobutton.hidden = tasks.length == 0; + combobutton.hidden = + tasks.length == 0 || this.entries_.some(e => e.isDirectory); if (tasks.length == 0) { return; }
diff --git a/ui/file_manager/integration_tests/file_manager/tasks.js b/ui/file_manager/integration_tests/file_manager/tasks.js index 489a2da..fabbeab 100644 --- a/ui/file_manager/integration_tests/file_manager/tasks.js +++ b/ui/file_manager/integration_tests/file_manager/tasks.js
@@ -287,3 +287,22 @@ const appId = await setupTaskTest(RootPath.DOWNLOADS, tasks); await executeDefaultTask(appId, 'dummytaskid-2|open-with'); }; + +testcase.noActionBarOpenForDirectories = async () => { + const tasks = [new FakeTask(true, 'dummytaskid|open-with', 'DummyTask1')]; + + // Override tasks for the test. + const appId = await setupTaskTest(RootPath.DOWNLOADS, tasks); + + // Select file and ensure action bar open is shown. + await remoteCall.callRemoteTestUtil('selectFile', appId, ['hello.txt']); + await remoteCall.waitForElement(appId, '#tasks:not([hidden])'); + + // Select dir and ensure action bar open is hidden, but context menu is shown. + await remoteCall.callRemoteTestUtil('selectFile', appId, ['photos']); + await remoteCall.waitForElement(appId, '#tasks[hidden]'); + chrome.test.assertTrue(!!await remoteCall.callRemoteTestUtil( + 'fakeMouseRightClick', appId, ['#file-list .table-row[selected]'])); + await remoteCall.waitForElement( + appId, '#default-task-menu-item:not([hidden])'); +};
diff --git a/ui/ozone/platform/drm/gpu/gbm_surface_factory.cc b/ui/ozone/platform/drm/gpu/gbm_surface_factory.cc index 5953baf..05484ff 100644 --- a/ui/ozone/platform/drm/gpu/gbm_surface_factory.cc +++ b/ui/ozone/platform/drm/gpu/gbm_surface_factory.cc
@@ -117,6 +117,7 @@ } std::vector<EGLDeviceEXT> devices(DRM_MAX_MINOR, EGL_NO_DEVICE_EXT); + EGLDeviceEXT virgl_device = EGL_NO_DEVICE_EXT; EGLDeviceEXT amdgpu_device = EGL_NO_DEVICE_EXT; EGLDeviceEXT i915_device = EGL_NO_DEVICE_EXT; EGLint num_devices = 0; @@ -128,12 +129,20 @@ eglQueryDeviceStringEXT(device, EGL_DRM_DEVICE_FILE_EXT); if (!filename) // Not a DRM device. continue; + if (IsDriverName(filename, "virtio_gpu")) + virgl_device = device; if (IsDriverName(filename, "amdgpu")) amdgpu_device = device; if (IsDriverName(filename, "i915")) i915_device = device; } + if (virgl_device != EGL_NO_DEVICE_EXT) { + native_display_ = gl::EGLDisplayPlatform( + reinterpret_cast<EGLNativeDisplayType>(virgl_device), + EGL_PLATFORM_DEVICE_EXT); + } + if (amdgpu_device != EGL_NO_DEVICE_EXT) { native_display_ = gl::EGLDisplayPlatform( reinterpret_cast<EGLNativeDisplayType>(amdgpu_device),
diff --git a/ui/ozone/platform/wayland/gpu/wayland_surface_factory_unittest.cc b/ui/ozone/platform/wayland/gpu/wayland_surface_factory_unittest.cc index 350b75c..9334271 100644 --- a/ui/ozone/platform/wayland/gpu/wayland_surface_factory_unittest.cc +++ b/ui/ozone/platform/wayland/gpu/wayland_surface_factory_unittest.cc
@@ -234,7 +234,8 @@ } // Now, schedule 3 buffers for swap. - auto* mock_surface = server_.GetObject<wl::MockSurface>(widget_); + auto* mock_surface = server_.GetObject<wl::MockSurface>( + window_->root_surface()->GetSurfaceId()); CallbacksHelper cbs_helper; // Submit all the available buffers.
diff --git a/ui/ozone/platform/wayland/host/wayland_pointer_unittest.cc b/ui/ozone/platform/wayland/host/wayland_pointer_unittest.cc index cf12f91f..41dfb73 100644 --- a/ui/ozone/platform/wayland/host/wayland_pointer_unittest.cc +++ b/ui/ozone/platform/wayland/host/wayland_pointer_unittest.cc
@@ -85,8 +85,8 @@ Sync(); - wl::MockSurface* other_surface = - server_.GetObject<wl::MockSurface>(other_widget); + wl::MockSurface* other_surface = server_.GetObject<wl::MockSurface>( + other_window->root_surface()->GetSurfaceId()); ASSERT_TRUE(other_surface); wl_pointer_send_enter(pointer_->resource(), 1, surface_->resource(), 0, 0);
diff --git a/ui/ozone/platform/wayland/host/wayland_screen_unittest.cc b/ui/ozone/platform/wayland/host/wayland_screen_unittest.cc index 899fff6..e7ff85e 100644 --- a/ui/ozone/platform/wayland/host/wayland_screen_unittest.cc +++ b/ui/ozone/platform/wayland/host/wayland_screen_unittest.cc
@@ -419,7 +419,8 @@ ValidateTheDisplayForWidget(widget, primary_display.id()); // Now, send enter event for the surface, which was created before. - wl::MockSurface* surface = server_.GetObject<wl::MockSurface>(widget); + wl::MockSurface* surface = server_.GetObject<wl::MockSurface>( + window_->root_surface()->GetSurfaceId()); ASSERT_TRUE(surface); wl_surface_send_enter(surface->resource(), output_->resource()); @@ -463,7 +464,8 @@ PlatformWindowType::kWindow, gfx::kNullAcceleratedWidget, &delegate); - auto* surface = server_.GetObject<wl::MockSurface>(window_->GetWidget()); + auto* surface = server_.GetObject<wl::MockSurface>( + window_->root_surface()->GetSurfaceId()); ASSERT_TRUE(surface); // Announce pointer capability so that WaylandPointer is created on the client @@ -488,8 +490,8 @@ // WaylandScreen must return the last pointer location. EXPECT_EQ(gfx::Point(10, 20), platform_screen_->GetCursorScreenPoint()); - auto* second_surface = - server_.GetObject<wl::MockSurface>(second_window->GetWidget()); + auto* second_surface = server_.GetObject<wl::MockSurface>( + second_window->root_surface()->GetSurfaceId()); ASSERT_TRUE(second_surface); // Now, leave the first surface and enter second one. wl_pointer_send_leave(pointer->resource(), ++serial, surface->resource()); @@ -530,8 +532,8 @@ Sync(); - auto* menu_surface = - server_.GetObject<wl::MockSurface>(menu_window->GetWidget()); + auto* menu_surface = server_.GetObject<wl::MockSurface>( + menu_window->root_surface()->GetSurfaceId()); ASSERT_TRUE(menu_surface); wl_pointer_send_enter(pointer->resource(), ++serial, menu_surface->resource(), @@ -577,8 +579,8 @@ Sync(); - auto* nested_menu_surface = - server_.GetObject<wl::MockSurface>(nested_menu_window->GetWidget()); + auto* nested_menu_surface = server_.GetObject<wl::MockSurface>( + nested_menu_window->root_surface()->GetSurfaceId()); ASSERT_TRUE(nested_menu_surface); wl_pointer_send_enter(pointer->resource(), ++serial,
diff --git a/ui/ozone/platform/wayland/host/wayland_window.cc b/ui/ozone/platform/wayland/host/wayland_window.cc index 43c6a233..705e0a5 100644 --- a/ui/ozone/platform/wayland/host/wayland_window.cc +++ b/ui/ozone/platform/wayland/host/wayland_window.cc
@@ -27,7 +27,10 @@ WaylandWindow::WaylandWindow(PlatformWindowDelegate* delegate, WaylandConnection* connection) - : delegate_(delegate), connection_(connection) {} + : delegate_(delegate), + connection_(connection), + accelerated_widget_( + connection->wayland_window_manager()->AllocateAcceleratedWidget()) {} WaylandWindow::~WaylandWindow() { shutting_down_ = true; @@ -84,10 +87,7 @@ } gfx::AcceleratedWidget WaylandWindow::GetWidget() const { - // TODO(https://crbug.com/1110354): We should not use wl_surface id as widget. - // Wayland Server can reuse ids for wl_surface but AcceleratedWidgets are not - // expected to be reused very soon. - return root_surface_->GetSurfaceId(); + return accelerated_widget_; } void WaylandWindow::SetPointerFocus(bool focus) {
diff --git a/ui/ozone/platform/wayland/host/wayland_window.h b/ui/ozone/platform/wayland/host/wayland_window.h index f243db5b..ae3fe86 100644 --- a/ui/ozone/platform/wayland/host/wayland_window.h +++ b/ui/ozone/platform/wayland/host/wayland_window.h
@@ -257,6 +257,9 @@ // Set when the window enters in shutdown process. bool shutting_down_ = false; + // AcceleratedWidget for this window. This will be unique even over time. + gfx::AcceleratedWidget accelerated_widget_; + DISALLOW_COPY_AND_ASSIGN(WaylandWindow); };
diff --git a/ui/ozone/platform/wayland/host/wayland_window_manager.cc b/ui/ozone/platform/wayland/host/wayland_window_manager.cc index b30d1c7c..0c9b86e 100644 --- a/ui/ozone/platform/wayland/host/wayland_window_manager.cc +++ b/ui/ozone/platform/wayland/host/wayland_window_manager.cc
@@ -128,6 +128,10 @@ observer.OnSubsurfaceRemoved(window, subsurface); } +gfx::AcceleratedWidget WaylandWindowManager::AllocateAcceleratedWidget() { + return ++last_accelerated_widget_; +} + std::vector<WaylandWindow*> WaylandWindowManager::GetAllWindows() const { std::vector<WaylandWindow*> result; for (auto entry : window_map_)
diff --git a/ui/ozone/platform/wayland/host/wayland_window_manager.h b/ui/ozone/platform/wayland/host/wayland_window_manager.h index fd058b32..ca32c248 100644 --- a/ui/ozone/platform/wayland/host/wayland_window_manager.h +++ b/ui/ozone/platform/wayland/host/wayland_window_manager.h
@@ -69,6 +69,9 @@ void RemoveSubsurface(gfx::AcceleratedWidget widget, WaylandSubsurface* subsurface); + // Creates a new unique gfx::AcceleratedWidget. + gfx::AcceleratedWidget AllocateAcceleratedWidget(); + private: base::ObserverList<WaylandWindowObserver> observers_; @@ -76,6 +79,10 @@ WaylandWindow* located_events_grabber_ = nullptr; + // Stores strictly monotonically increasing counter for allocating unique + // AccelerateWidgets. + gfx::AcceleratedWidget last_accelerated_widget_ = gfx::kNullAcceleratedWidget; + DISALLOW_COPY_AND_ASSIGN(WaylandWindowManager); };
diff --git a/ui/ozone/platform/wayland/host/wayland_window_unittest.cc b/ui/ozone/platform/wayland/host/wayland_window_unittest.cc index 40f304e..91ed358d 100644 --- a/ui/ozone/platform/wayland/host/wayland_window_unittest.cc +++ b/ui/ozone/platform/wayland/host/wayland_window_unittest.cc
@@ -100,9 +100,9 @@ } protected: - void SendConfigureEventPopup(gfx::AcceleratedWidget menu_widget, + void SendConfigureEventPopup(WaylandWindow* menu_window, const gfx::Rect bounds) { - auto* popup = GetPopupByWidget(menu_widget); + auto* popup = GetPopupByWindow(menu_window); ASSERT_TRUE(popup); if (GetParam() == kXdgShellV6) { zxdg_popup_v6_send_configure(popup->resource(), bounds.x(), bounds.y(), @@ -166,9 +166,9 @@ Mock::VerifyAndClearExpectations(&delegate_); } - void VerifyXdgPopupPosition(gfx::AcceleratedWidget menu_widget, + void VerifyXdgPopupPosition(WaylandWindow* menu_window, const PopupPosition& position) { - auto* popup = GetPopupByWidget(menu_widget); + auto* popup = GetPopupByWindow(menu_window); ASSERT_TRUE(popup); EXPECT_EQ(popup->anchor_rect(), position.anchor_rect); @@ -209,8 +209,9 @@ EXPECT_FALSE(window->CanDispatchEvent(&test_key_event)); } - wl::TestXdgPopup* GetPopupByWidget(gfx::AcceleratedWidget widget) { - wl::MockSurface* mock_surface = server_.GetObject<wl::MockSurface>(widget); + wl::TestXdgPopup* GetPopupByWindow(WaylandWindow* window) { + wl::MockSurface* mock_surface = server_.GetObject<wl::MockSurface>( + window->root_surface()->GetSurfaceId()); if (mock_surface) { auto* mock_xdg_surface = mock_surface->xdg_surface(); if (mock_xdg_surface) @@ -1200,8 +1201,8 @@ uint32_t serial = 0; // Test that CanDispatchEvent is set correctly. - wl::MockSurface* toplevel_surface = - server_.GetObject<wl::MockSurface>(widget_); + wl::MockSurface* toplevel_surface = server_.GetObject<wl::MockSurface>( + window_->root_surface()->GetSurfaceId()); wl_pointer_send_enter(server_.seat()->pointer()->resource(), ++serial, toplevel_surface->resource(), 0, 0); @@ -1356,11 +1357,10 @@ Sync(); - gfx::AcceleratedWidget menu_window_widget = menu_window->GetWidget(); - VerifyXdgPopupPosition(menu_window_widget, menu_window_positioner); + VerifyXdgPopupPosition(menu_window.get(), menu_window_positioner); EXPECT_CALL(menu_window_delegate, OnBoundsChanged(_)).Times(0); - SendConfigureEventPopup(menu_window_widget, menu_window_bounds); + SendConfigureEventPopup(menu_window.get(), menu_window_bounds); Sync(); @@ -1372,15 +1372,13 @@ nested_menu_window_positioner.size); std::unique_ptr<WaylandWindow> nested_menu_window = CreateWaylandWindowWithParams( - PlatformWindowType::kMenu, menu_window_widget, + PlatformWindowType::kMenu, menu_window->GetWidget(), nested_menu_window_bounds, &nested_menu_window_delegate); EXPECT_TRUE(nested_menu_window); Sync(); - gfx::AcceleratedWidget nested_menu_window_widget = - nested_menu_window->GetWidget(); - VerifyXdgPopupPosition(nested_menu_window_widget, + VerifyXdgPopupPosition(nested_menu_window.get(), nested_menu_window_positioner); EXPECT_CALL(nested_menu_window_delegate, OnBoundsChanged(_)).Times(0); @@ -1389,7 +1387,7 @@ nested_menu_window_positioner.anchor_rect.y()); gfx::Rect calculated_nested_bounds = nested_menu_window_bounds; calculated_nested_bounds.set_origin(origin); - SendConfigureEventPopup(nested_menu_window_widget, calculated_nested_bounds); + SendConfigureEventPopup(nested_menu_window.get(), calculated_nested_bounds); Sync(); @@ -1407,7 +1405,7 @@ nested_menu_window_delegate, OnBoundsChanged(gfx::Rect({139, 156}, nested_menu_window_bounds.size()))); calculated_nested_bounds.set_origin({-301, 80}); - SendConfigureEventPopup(nested_menu_window_widget, calculated_nested_bounds); + SendConfigureEventPopup(nested_menu_window.get(), calculated_nested_bounds); Sync(); @@ -1422,7 +1420,7 @@ gfx::Rect({0, 363}, window_->GetBounds().size()))); EXPECT_CALL(menu_window_delegate, OnBoundsChanged(gfx::Rect({440, 0}, menu_window_bounds.size()))); - SendConfigureEventPopup(menu_window_widget, + SendConfigureEventPopup(menu_window.get(), gfx::Rect({440, -363}, menu_window_bounds.size())); Sync(); @@ -1433,23 +1431,22 @@ nested_menu_window.reset(); nested_menu_window_bounds.set_origin({723, 258}); nested_menu_window = CreateWaylandWindowWithParams( - PlatformWindowType::kMenu, menu_window_widget, nested_menu_window_bounds, - &nested_menu_window_delegate); + PlatformWindowType::kMenu, menu_window->GetWidget(), + nested_menu_window_bounds, &nested_menu_window_delegate); EXPECT_TRUE(nested_menu_window); Sync(); - nested_menu_window_widget = nested_menu_window->GetWidget(); // We must get the anchor on gfx::Point(4, 258). nested_menu_window_positioner.anchor_rect.set_origin({4, 258}); - VerifyXdgPopupPosition(nested_menu_window_widget, + VerifyXdgPopupPosition(nested_menu_window.get(), nested_menu_window_positioner); Sync(); EXPECT_CALL(nested_menu_window_delegate, OnBoundsChanged(_)).Times(0); calculated_nested_bounds.set_origin({283, 258}); - SendConfigureEventPopup(nested_menu_window_widget, calculated_nested_bounds); + SendConfigureEventPopup(nested_menu_window.get(), calculated_nested_bounds); Sync(); @@ -1464,7 +1461,7 @@ nested_menu_window_delegate, OnBoundsChanged(gfx::Rect({149, 258}, nested_menu_window_bounds.size()))); calculated_nested_bounds.set_origin({-291, 258}); - SendConfigureEventPopup(nested_menu_window_widget, calculated_nested_bounds); + SendConfigureEventPopup(nested_menu_window.get(), calculated_nested_bounds); Sync(); @@ -1477,7 +1474,7 @@ OnBoundsChanged(gfx::Rect({0, 0}, window_->GetBounds().size()))); EXPECT_CALL(menu_window_delegate, OnBoundsChanged(gfx::Rect({440, 76}, menu_window_bounds.size()))); - SendConfigureEventPopup(menu_window_widget, + SendConfigureEventPopup(menu_window.get(), gfx::Rect({440, 76}, menu_window_bounds.size())); Sync(); @@ -1625,27 +1622,26 @@ VerifyAndClearExpectations(); gfx::Rect nestedPopup_bounds(gfx::Rect(10, 30, 40, 20)); - std::unique_ptr<WaylandWindow> nestedPopup_window = + std::unique_ptr<WaylandWindow> nested_popup_window = CreateWaylandWindowWithParams(PlatformWindowType::kPopup, menu_window->GetWidget(), nestedPopup_bounds, &delegate_); - EXPECT_TRUE(nestedPopup_window); + EXPECT_TRUE(nested_popup_window); VerifyAndClearExpectations(); - nestedPopup_window->SetPointerFocus(true); + nested_popup_window->SetPointerFocus(true); Sync(); - auto* mock_surface_nested_popup = - GetPopupByWidget(nestedPopup_window->GetWidget()); + auto* mock_surface_nested_popup = GetPopupByWindow(nested_popup_window.get()); ASSERT_TRUE(mock_surface_nested_popup); auto anchor_width = (mock_surface_nested_popup->anchor_rect()).width(); EXPECT_EQ(4, anchor_width); - nestedPopup_window->SetPointerFocus(false); + nested_popup_window->SetPointerFocus(false); } // Case 2: When the menu bounds are positive and there is a negative or @@ -1662,27 +1658,26 @@ VerifyAndClearExpectations(); gfx::Rect nestedPopup_bounds(gfx::Rect(10, 30, 10, 20)); - std::unique_ptr<WaylandWindow> nestedPopup_window = + std::unique_ptr<WaylandWindow> nested_popup_window = CreateWaylandWindowWithParams(PlatformWindowType::kPopup, menu_window->GetWidget(), nestedPopup_bounds, &delegate_); - EXPECT_TRUE(nestedPopup_window); + EXPECT_TRUE(nested_popup_window); VerifyAndClearExpectations(); - nestedPopup_window->SetPointerFocus(true); + nested_popup_window->SetPointerFocus(true); Sync(); - auto* mock_surface_nested_popup = - GetPopupByWidget(nestedPopup_window->GetWidget()); + auto* mock_surface_nested_popup = GetPopupByWindow(nested_popup_window.get()); ASSERT_TRUE(mock_surface_nested_popup); auto anchor_width = (mock_surface_nested_popup->anchor_rect()).width(); EXPECT_EQ(1, anchor_width); - nestedPopup_window->SetPointerFocus(false); + nested_popup_window->SetPointerFocus(false); } // Case 3: When the menu bounds are negative and there is a positive, @@ -1699,27 +1694,26 @@ VerifyAndClearExpectations(); gfx::Rect nestedPopup_bounds(gfx::Rect(5, 30, 21, 20)); - std::unique_ptr<WaylandWindow> nestedPopup_window = + std::unique_ptr<WaylandWindow> nested_popup_window = CreateWaylandWindowWithParams(PlatformWindowType::kPopup, menu_window->GetWidget(), nestedPopup_bounds, &delegate_); - EXPECT_TRUE(nestedPopup_window); + EXPECT_TRUE(nested_popup_window); VerifyAndClearExpectations(); - nestedPopup_window->SetPointerFocus(true); + nested_popup_window->SetPointerFocus(true); Sync(); - auto* mock_surface_nested_popup = - GetPopupByWidget(nestedPopup_window->GetWidget()); + auto* mock_surface_nested_popup = GetPopupByWindow(nested_popup_window.get()); ASSERT_TRUE(mock_surface_nested_popup); auto anchor_width = (mock_surface_nested_popup->anchor_rect()).width(); EXPECT_EQ(8, anchor_width); - nestedPopup_window->SetPointerFocus(false); + nested_popup_window->SetPointerFocus(false); } // Case 4: When the menu bounds are negative and there is a negative, @@ -1736,20 +1730,19 @@ VerifyAndClearExpectations(); gfx::Rect nestedPopup_bounds(gfx::Rect(5, 30, 21, 20)); - std::unique_ptr<WaylandWindow> nestedPopup_window = + std::unique_ptr<WaylandWindow> nested_popup_window = CreateWaylandWindowWithParams(PlatformWindowType::kPopup, menu_window->GetWidget(), nestedPopup_bounds, &delegate_); - EXPECT_TRUE(nestedPopup_window); + EXPECT_TRUE(nested_popup_window); VerifyAndClearExpectations(); - nestedPopup_window->SetPointerFocus(true); + nested_popup_window->SetPointerFocus(true); Sync(); - auto* mock_surface_nested_popup = - GetPopupByWidget(nestedPopup_window->GetWidget()); + auto* mock_surface_nested_popup = GetPopupByWindow(nested_popup_window.get()); ASSERT_TRUE(mock_surface_nested_popup); @@ -1757,7 +1750,7 @@ EXPECT_EQ(1, anchor_width); - nestedPopup_window->SetPointerFocus(false); + nested_popup_window->SetPointerFocus(false); } TEST_P(WaylandWindowTest, AuxiliaryWindowNestedParent) { @@ -1989,7 +1982,7 @@ Sync(); - auto* test_popup = GetPopupByWidget(popup->GetWidget()); + auto* test_popup = GetPopupByWindow(popup.get()); ASSERT_TRUE(test_popup); EXPECT_NE(test_popup->grab_serial(), button_release_serial); EXPECT_EQ(test_popup->grab_serial(), button_press_serial); @@ -2033,7 +2026,7 @@ Sync(); - auto* test_popup = GetPopupByWidget(popup->GetWidget()); + auto* test_popup = GetPopupByWindow(popup.get()); ASSERT_TRUE(test_popup); EXPECT_NE(test_popup->grab_serial(), touch_up_serial); EXPECT_EQ(test_popup->grab_serial(), touch_down_serial); @@ -2087,7 +2080,7 @@ Sync(); - auto* test_popup = GetPopupByWidget(popup->GetWidget()); + auto* test_popup = GetPopupByWindow(popup.get()); ASSERT_TRUE(test_popup); EXPECT_EQ(test_popup->grab_serial(), 0u); }
diff --git a/ui/ozone/platform/wayland/test/wayland_test.cc b/ui/ozone/platform/wayland/test/wayland_test.cc index 67c8f2bb..4ce3ad03 100644 --- a/ui/ozone/platform/wayland/test/wayland_test.cc +++ b/ui/ozone/platform/wayland/test/wayland_test.cc
@@ -64,11 +64,12 @@ // Pause the server after it has responded to all incoming events. server_.Pause(); - surface_ = server_.GetObject<wl::MockSurface>(widget_); + auto id = window_->root_surface()->GetSurfaceId(); + surface_ = server_.GetObject<wl::MockSurface>(id); ASSERT_TRUE(surface_); // The surface must be activated before buffers are attached. - ActivateSurface(server_.GetObject<wl::MockSurface>(widget_)->xdg_surface()); + ActivateSurface(server_.GetObject<wl::MockSurface>(id)->xdg_surface()); Sync();
diff --git a/ui/ozone/platform/wayland/wayland_buffer_manager_unittest.cc b/ui/ozone/platform/wayland/wayland_buffer_manager_unittest.cc index 487f7d0..8721395d 100644 --- a/ui/ozone/platform/wayland/wayland_buffer_manager_unittest.cc +++ b/ui/ozone/platform/wayland/wayland_buffer_manager_unittest.cc
@@ -456,7 +456,8 @@ ProcessCreatedBufferResourcesWithExpectation(2u /* expected size */, false /* fail */); - auto* mock_surface = server_.GetObject<wl::MockSurface>(widget); + auto* mock_surface = server_.GetObject<wl::MockSurface>( + window_->root_surface()->GetSurfaceId()); constexpr uint32_t kNumberOfCommits = 3; EXPECT_CALL(*mock_surface, Attach(_, _, _)).Times(kNumberOfCommits); @@ -565,7 +566,8 @@ ProcessCreatedBufferResourcesWithExpectation(3u /* expected size */, false /* fail */); - auto* mock_surface = server_.GetObject<wl::MockSurface>(widget); + auto* mock_surface = server_.GetObject<wl::MockSurface>( + window_->root_surface()->GetSurfaceId()); auto* mock_wp_presentation = server_.EnsureWpPresentation(); ASSERT_TRUE(mock_wp_presentation); @@ -685,7 +687,8 @@ ProcessCreatedBufferResourcesWithExpectation(3u /* expected size */, false /* fail */); - auto* mock_surface = server_.GetObject<wl::MockSurface>(widget); + auto* mock_surface = server_.GetObject<wl::MockSurface>( + window_->root_surface()->GetSurfaceId()); constexpr uint32_t kNumberOfCommits = 3; EXPECT_CALL(*mock_surface, Attach(_, _, _)).Times(kNumberOfCommits); @@ -804,7 +807,8 @@ constexpr uint32_t kDmabufBufferId2 = 2; const gfx::AcceleratedWidget widget = window_->GetWidget(); - auto* mock_surface = server_.GetObject<wl::MockSurface>(widget); + auto* mock_surface = server_.GetObject<wl::MockSurface>( + window_->root_surface()->GetSurfaceId()); MockSurfaceGpu mock_surface_gpu(buffer_manager_gpu_.get(), widget_); auto* linux_dmabuf = server_.zwp_linux_dmabuf_v1(); @@ -902,7 +906,8 @@ Sync(); - auto* mock_surface = server_.GetObject<wl::MockSurface>(widget); + auto* mock_surface = server_.GetObject<wl::MockSurface>( + temp_window->root_surface()->GetSurfaceId()); MockSurfaceGpu mock_surface_gpu(buffer_manager_gpu_.get(), widget); auto* linux_dmabuf = server_.zwp_linux_dmabuf_v1(); @@ -970,7 +975,8 @@ ProcessCreatedBufferResourcesWithExpectation(1u /* expected size */, false /* fail */); - auto* mock_surface = server_.GetObject<wl::MockSurface>(widget); + auto* mock_surface = server_.GetObject<wl::MockSurface>( + window_->root_surface()->GetSurfaceId()); constexpr uint32_t kNumberOfCommits = 3; EXPECT_CALL(*mock_surface, Attach(_, _, _)).Times(kNumberOfCommits); @@ -1121,7 +1127,8 @@ auto widget = temp_window->GetWidget(); auto bounds = temp_window->GetBounds(); - auto* mock_surface = server_.GetObject<wl::MockSurface>(widget); + auto* mock_surface = server_.GetObject<wl::MockSurface>( + temp_window->root_surface()->GetSurfaceId()); ASSERT_TRUE(mock_surface); ActivateSurface(mock_surface->xdg_surface()); @@ -1190,7 +1197,8 @@ const gfx::AcceleratedWidget widget = window_->GetWidget(); const gfx::Rect bounds = window_->GetBounds(); - auto* mock_surface = server_.GetObject<wl::MockSurface>(widget); + auto* mock_surface = server_.GetObject<wl::MockSurface>( + window_->root_surface()->GetSurfaceId()); MockSurfaceGpu mock_surface_gpu(buffer_manager_gpu_.get(), widget); @@ -1253,7 +1261,8 @@ const gfx::AcceleratedWidget widget = window_->GetWidget(); const gfx::Rect bounds = window_->GetBounds(); - auto* mock_surface = server_.GetObject<wl::MockSurface>(widget); + auto* mock_surface = server_.GetObject<wl::MockSurface>( + window_->root_surface()->GetSurfaceId()); MockSurfaceGpu mock_surface_gpu(buffer_manager_gpu_.get(), widget); @@ -1315,7 +1324,8 @@ const gfx::AcceleratedWidget widget = window_->GetWidget(); const gfx::Rect bounds = window_->GetBounds(); - auto* mock_surface = server_.GetObject<wl::MockSurface>(widget); + auto* mock_surface = server_.GetObject<wl::MockSurface>( + window_->root_surface()->GetSurfaceId()); MockSurfaceGpu mock_surface_gpu(buffer_manager_gpu_.get(), widget); @@ -1413,7 +1423,8 @@ .Times(1); ASSERT_TRUE(!connection_->presentation()); EXPECT_CALL(mock_surface_gpu, OnPresentation(kBufferId1, _)).Times(1); - auto* mock_surface = server_.GetObject<wl::MockSurface>(widget); + auto* mock_surface = server_.GetObject<wl::MockSurface>( + window_->root_surface()->GetSurfaceId()); EXPECT_CALL(*mock_surface, Attach(_, _, _)).Times(1); EXPECT_CALL(*mock_surface, Frame(_)).Times(1); EXPECT_CALL(*mock_surface,
diff --git a/ui/views/controls/webview/webview.cc b/ui/views/controls/webview/webview.cc index b3c8633f..639ba0b 100644 --- a/ui/views/controls/webview/webview.cc +++ b/ui/views/controls/webview/webview.cc
@@ -272,8 +272,17 @@ if (web_contents() && !web_contents()->IsCrashed()) { content::RenderWidgetHostView* host_view = web_contents()->GetRenderWidgetHostView(); - if (host_view) - return host_view->GetNativeViewAccessible(); + if (host_view) { + gfx::NativeViewAccessible accessible = + host_view->GetNativeViewAccessible(); + // |accessible| needs to know whether this is the primary WebContents. + if (auto* ax_platform_node = + ui::AXPlatformNode::FromNativeViewAccessible(accessible)) { + ax_platform_node->SetIsPrimaryWebContentsForWindow( + is_primary_web_contents_for_window_); + } + return accessible; + } } return View::GetNativeViewAccessible(); }
diff --git a/ui/views/controls/webview/webview.h b/ui/views/controls/webview/webview.h index cfcf301..f18eea14 100644 --- a/ui/views/controls/webview/webview.h +++ b/ui/views/controls/webview/webview.h
@@ -89,6 +89,11 @@ // if the web contents is changed. void SetCrashedOverlayView(View* crashed_overlay_view); + // Sets whether this is the primary web contents for the window. + void set_is_primary_web_contents_for_window(bool is_primary) { + is_primary_web_contents_for_window_ = is_primary; + } + // When used to host UI, we need to explicitly allow accelerators to be // processed. Default is false. void set_allow_accelerators(bool allow_accelerators) { @@ -199,6 +204,7 @@ content::BrowserContext* browser_context_; bool allow_accelerators_ = false; View* crashed_overlay_view_ = nullptr; + bool is_primary_web_contents_for_window_ = false; // Minimum and maximum sizes to determine WebView bounds for auto-resizing. // Empty if auto resize is not enabled.